├── .gitignore
├── Makefile
├── LICENSE
├── README
├── cardBag_test.go
├── resultSet_test.go
├── test-poker-odds.sh
├── resultSet.go
├── cardSliceProcessor.go
├── cardBag.go
├── subsetChooser.go
├── subsetChooser_test.go
├── hand_test.go
├── poker-odds.go
├── card.go
├── hand.go
└── COPYING
/.gitignore:
--------------------------------------------------------------------------------
1 | poker-odds
2 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | poker-odds: *.go
2 | go build -o poker-odds *.go
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | These programs are available under the terms of the GNU Public License version
2 | 2. See COPYING for details.
3 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | poker-odds is a program for calculating the odds in a Texas Hold 'Em poker
2 | game. It tells you how likely you are to make each category of hand. For
3 | example, it can tell you how likely it is that, if you start with two aces, you
4 | will get four of a kind.
5 |
6 | Currently, poker-odds only takes into account one player. You cannot augment it
7 | with your knowledge, actual or suspected, about what other players hold (and
8 | what therefore cannot be revealed by the dealer).
9 |
10 | I wrote poker-odds partly to learn the Google Go (Golang) programming language.
11 | poker-odds can be configured to use as many or as few goprocs as you like. More
12 | goprocs means more parallelism, of course.
13 |
14 | I hope you have fun with this and with Go!
15 |
16 | Colin McCabe
17 |
--------------------------------------------------------------------------------
/cardBag_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Colin Patrick McCabe
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, version 2.
7 | *
8 | * This program is distributed in the hope that it will be useful,
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | * GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License
14 | * along with this program. If not, see .
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "testing"
21 | )
22 |
23 | func TestCardBag1(t *testing.T) {
24 | bag := Make52CardBag()
25 | test0 := Card { 2, DIAMONDS }
26 | bag0 := bag.Get(0)
27 | if (test0.Compare(bag0) != 0) {
28 | t.Errorf("expected:%s. got: %s\n", test0, bag0)
29 | }
30 |
31 | test1 := Card { 3, DIAMONDS }
32 | bag1 := bag.Get(4)
33 | if (test1.Compare(bag1) != 0) {
34 | t.Errorf("expected:%s. got: %s\n", test1, bag1)
35 | }
36 |
37 | bag.Subtract( &Card {2, DIAMONDS} )
38 |
39 | test2 := &Card { 2, CLUBS }
40 | bag2 := bag.Get(0)
41 | if (test2.Compare(bag2) != 0) {
42 | t.Errorf("expected:%s. got: %s\n", test2, bag2)
43 | }
44 |
45 | test3 := &Card { 3, CLUBS }
46 | bag3 := bag.Get(4)
47 | if (test3.Compare(bag3) != 0) {
48 | t.Errorf("expected:%s. got: %s\n", test3, bag3)
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/resultSet_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Colin Patrick McCabe
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, version 2.
7 | *
8 | * This program is distributed in the hope that it will be useful,
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | * GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License
14 | * along with this program. If not, see .
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "testing"
21 | )
22 |
23 | func TestResultSet1(t *testing.T) {
24 | r1 := new(ResultSet)
25 | r1.AddHand( MakeHand(CardSlice { &Card{2, DIAMONDS}, &Card{2, CLUBS},
26 | &Card{2, HEARTS}, &Card{QUEEN_VAL, SPADES}, &Card{KING_VAL, SPADES} }))
27 | if (r1.handTyCnt[THREE_OF_A_KIND] != 1) {
28 | t.Errorf("expected ResultSet r1 to have 1 THREE_OF_A_KIND hand in it.")
29 | }
30 |
31 | r2 := new(ResultSet)
32 | r2.AddHand( MakeHand(CardSlice { &Card{8, DIAMONDS}, &Card{8, CLUBS},
33 | &Card{8, HEARTS}, &Card{QUEEN_VAL, SPADES}, &Card{KING_VAL, SPADES} }) )
34 | if (r2.handTyCnt[THREE_OF_A_KIND] != 1) {
35 | t.Errorf("expected ResultSet r2 to have 1 THREE_OF_A_KIND hand in it.")
36 | }
37 |
38 | r1.MergeResultSet(r2)
39 |
40 | if (r1.handTyCnt[THREE_OF_A_KIND] != 2) {
41 | t.Errorf("expected ResultSet r1 to have 2 THREE_OF_A_KIND hands in it.")
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/test-poker-odds.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | die() {
4 | echo $@
5 | exit 1
6 | }
7 |
8 | cleanup() {
9 | rm -f "${tmp}"
10 | rm -f "${tmp2}"
11 | }
12 |
13 | poker_odds="`dirname $0`/poker-odds"
14 | [ -x "${poker_odds}" ] || die "failed to locate the poker-odds executable"
15 | tmp=`mktemp`
16 | tmp2=`mktemp`
17 | echo > "${tmp}" || die "unable to create tmp"
18 | echo > "${tmp2}" || die "unable to create tmp2"
19 | trap cleanup INT TERM EXIT
20 |
21 | "${poker_odds}" -a "KS QS" -b "AS 3S 5S" > "${tmp}"
22 | cat << EOF > "${tmp2}"
23 | results:
24 | 99.91% chance of a flush
25 | 0.09% chance of a straight flush
26 | EOF
27 | diff "${tmp2}" "${tmp}" || die "unexpected result from test 1"
28 |
29 | "${poker_odds}" -a 'KC JC' -b '2S 3S 4S 5S 6S' > "${tmp}"
30 | grep -q "100.00% chance of a straight flush" "${tmp}" || die "example 2 failed"
31 |
32 | "${poker_odds}" -a 'KC JC' -b '2S 3S 4S 5S' > "${tmp}"
33 | cat << EOF > "${tmp2}"
34 | results:
35 | 39.13% chance of nothing
36 | 34.78% chance of a pair
37 | 6.52% chance of a straight
38 | 17.39% chance of a flush
39 | 2.17% chance of a straight flush
40 | EOF
41 | diff "${tmp2}" "${tmp}" || die "unexpected result from test 3"
42 |
43 | "${poker_odds}" -b 'KD KC 5H' -a 'KS QS' > "${tmp}"
44 | cat << EOF > "${tmp2}"
45 | results:
46 | 95.74% chance of three of a kind
47 | 4.26% chance of four of a kind
48 | EOF
49 | diff "${tmp2}" "${tmp}" || die "unexpected result from test 4"
50 |
51 | "${poker_odds}" -b "AS 7D 3D 4D" -a "KS QS" > "${tmp}"
52 | cat << EOF > "${tmp2}"
53 | results:
54 | 60.87% chance of nothing
55 | 39.13% chance of a pair
56 | EOF
57 | diff "${tmp2}" "${tmp}" || die "unexpected result from test 5"
58 |
--------------------------------------------------------------------------------
/resultSet.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Colin Patrick McCabe
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, version 2.
7 | *
8 | * This program is distributed in the hope that it will be useful,
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | * GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License
14 | * along with this program. If not, see .
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | )
22 |
23 | type ResultSet struct {
24 | handTyCnt [MAX_HANDS] int64
25 | }
26 |
27 | func (res *ResultSet) AddHand(h *Hand) {
28 | res.handTyCnt[h.ty] = res.handTyCnt[h.ty] + 1
29 | }
30 |
31 | func (res *ResultSet) AddHandTy(h int) {
32 | res.handTyCnt[h] = res.handTyCnt[h] + 1
33 | }
34 |
35 | func (res *ResultSet) GetBestHandTy() int {
36 | for i := MAX_HANDS - 1; i >= 0; i-- {
37 | if (res.handTyCnt[i] > 0) {
38 | return i
39 | }
40 | }
41 | return HIGH_CARD
42 | }
43 |
44 | func (res *ResultSet) MergeResultSet(rhs *ResultSet) {
45 | for t := HIGH_CARD; t < MAX_HANDS; t++ {
46 | res.handTyCnt[t] = res.handTyCnt[t] + rhs.handTyCnt[t]
47 | }
48 | }
49 |
50 | func (res *ResultSet) String() string {
51 | var totalHands int64
52 | totalHands = 0
53 | for i := range(res.handTyCnt) {
54 | totalHands = totalHands + res.handTyCnt[i]
55 | }
56 |
57 | ret := ""
58 | for i := range(res.handTyCnt) {
59 | percent := float32(res.handTyCnt[i])
60 | percent *= 100.0
61 | percent /= float32(totalHands);
62 | if (percent > 0.0) {
63 | ret += fmt.Sprintf("%03.2f%% chance of %s\n", percent, HandTyToStr(i))
64 | }
65 | }
66 | return ret
67 | }
68 |
69 |
--------------------------------------------------------------------------------
/cardSliceProcessor.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Colin Patrick McCabe
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, version 2.
7 | *
8 | * This program is distributed in the hope that it will be useful,
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | * GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License
14 | * along with this program. If not, see .
15 | */
16 |
17 | package main
18 |
19 | type CardSliceProcessor struct {
20 | Card chan *Card
21 | Quit chan bool
22 | Finished chan bool
23 | base CardSlice
24 | Results ResultSet
25 | }
26 |
27 | func NewCardSliceProcessor(base_ CardSlice) *CardSliceProcessor {
28 | ret := new(CardSliceProcessor)
29 | ret.Card = make(chan *Card)
30 | ret.Quit = make(chan bool)
31 | ret.Finished = make(chan bool)
32 | ret.base = base_.Copy()
33 | return ret
34 | }
35 |
36 | func (csp *CardSliceProcessor) processSpread(spread CardSlice) {
37 | setupChooser := NewSubsetChooser(SPREAD_MAX, HAND_SZ)
38 | var tmpRes ResultSet
39 | for ;; {
40 | setup := setupChooser.Cur()
41 | setupC := make(CardSlice, len(setup))
42 | for i := range(setup) {
43 | setupC[i] = spread[setup[i]]
44 | }
45 | h := MakeHand(setupC)
46 | tmpRes.AddHand(h)
47 | if (!setupChooser.Next()) {
48 | break
49 | }
50 | }
51 | csp.Results.AddHandTy(tmpRes.GetBestHandTy())
52 | }
53 |
54 | func (csp *CardSliceProcessor) GoCardSliceProcessor() {
55 | spread := make(CardSlice, SPREAD_MAX)
56 | copy(spread, csp.base)
57 | j := len(csp.base)
58 | for {
59 | if (j == len(spread)) {
60 | csp.processSpread(spread)
61 | j = len(csp.base)
62 | }
63 | select {
64 | case c := <-csp.Card:
65 | //fmt.Printf("%p: received card %s\n", csp, c.String())
66 | spread[j] = c
67 | j++
68 | case <-csp.Quit:
69 | csp.Finished <-true
70 | return
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/cardBag.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Colin Patrick McCabe
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, version 2.
7 | *
8 | * This program is distributed in the hope that it will be useful,
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | * GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License
14 | * along with this program. If not, see .
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "sort"
22 | )
23 |
24 | type CardBag struct {
25 | allCards CardSlice
26 | }
27 |
28 | func Make52CardBag() *CardBag {
29 | // generate a CardBag that has every possible card
30 | bag := &CardBag { make(CardSlice, 52) }
31 | idx := 0
32 | for val := 2; val <= 14; val++ {
33 | for suit := range([]int { DIAMONDS, CLUBS, HEARTS, SPADES }) {
34 | bag.allCards[idx] = new(Card)
35 | bag.allCards[idx].suit = suit
36 | bag.allCards[idx].val = val
37 | idx++
38 | }
39 | }
40 |
41 | sort.Sort(bag.allCards)
42 | return bag
43 | }
44 |
45 | func (bag *CardBag) Clone() *CardBag {
46 | ret := new(CardBag)
47 | ret.allCards = bag.allCards
48 | return ret
49 | }
50 |
51 | func (bag *CardBag) Subtract(c *Card) {
52 | nextAllCards := make(CardSlice, len(bag.allCards) - 1)
53 | i := 0
54 | for i = 0; i < len(bag.allCards); i++ {
55 | if (bag.allCards[i].Compare(c) == 0) {
56 | break
57 | }
58 | }
59 | if (i == len(bag.allCards)) {
60 | panic(fmt.Sprintf("tried to subtract %v from this cardbag, but " +
61 | "it doesn't currently contain that card.", c))
62 | }
63 | copy(nextAllCards[0:i], bag.allCards[0:i])
64 | copy(nextAllCards[i:], bag.allCards[i+1:])
65 | bag.allCards = nextAllCards
66 | }
67 |
68 | func (bag *CardBag) Get(num uint) *Card {
69 | return bag.allCards[num]
70 | }
71 |
72 | func (bag *CardBag) Len() int {
73 | return len(bag.allCards)
74 | }
75 |
76 | func (bag *CardBag) String() string {
77 | return fmt.Sprintf("CardBag{allCards=%v}",
78 | bag.allCards)
79 | }
80 |
--------------------------------------------------------------------------------
/subsetChooser.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Colin Patrick McCabe
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, version 2.
7 | *
8 | * This program is distributed in the hope that it will be useful,
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | * GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License
14 | * along with this program. If not, see .
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | )
22 |
23 | /*
24 | * A subsetChooser can be used to step through all the possible subarrays of an
25 | * array.
26 | * So if our starting array was [ "a", "b", "c" ], and subset size was 2, we'd
27 | * step through [ "a", "b" ], [ "a", "c" ], and [ "b", "c" ]. Etc.
28 | */
29 | type SubsetChooser struct {
30 | maxIdx uint
31 | subsetSize uint
32 | comb int64
33 | }
34 |
35 | // Creates a new SubsetChooser
36 | func NewSubsetChooser(maxIdx uint, subsetSize uint) *SubsetChooser {
37 | ch := &SubsetChooser {maxIdx, subsetSize, 0}
38 | if (subsetSize > 63) {
39 | panic("sorry, this class can't handle subset sizes greater than 63" +
40 | "due to its use of 64-bit numbers to represent subsets.")
41 | }
42 | ch.comb = pow64(2, uint(subsetSize)) - 1
43 | return ch
44 | }
45 |
46 | // Gets the current subset
47 | func (ch *SubsetChooser) Cur() []uint {
48 | var i uint
49 | ret := make([]uint, ch.subsetSize)
50 | var j uint = 0
51 | for i = 0; i < ch.maxIdx; i++ {
52 | if (((1 << i) & ch.comb) != 0) {
53 | ret[j] = i
54 | j++
55 | }
56 | }
57 | if (j != ch.subsetSize) {
58 | panic(fmt.Sprintf("logic error: failed to return a subset of size %d",
59 | ch.subsetSize))
60 | }
61 | return ret
62 | }
63 |
64 | // Advance to the next subset.
65 | // Based on HAKMEM item 175.
66 | // Returns false if there are no more subsets to view, true otherwise
67 | func (ch *SubsetChooser) Next() bool {
68 | if (ch.comb == 0) {
69 | return false
70 | }
71 | u := ch.comb & -ch.comb
72 | v := u + ch.comb
73 | if (v==0) {
74 | ch.comb = 0
75 | return false
76 | }
77 | ch.comb = v + (((v^ch.comb)/u)>>2);
78 | if (ch.comb >= (1<.
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "sort"
22 | "testing"
23 | )
24 |
25 | type uintSlice []uint
26 | type uintSliceSlice []uintSlice
27 |
28 | func (arr uintSliceSlice) Len() int {
29 | return len(arr)
30 | }
31 |
32 | func (arr uintSliceSlice) Less(i, j int) bool {
33 | c := arr[i].Compare(arr[j])
34 | return (c < 0)
35 | }
36 |
37 | func (arr uintSliceSlice) Swap(i, j int) {
38 | arr[i], arr[j] = arr[j], arr[i]
39 | }
40 |
41 | func (a uintSlice) Compare(b uintSlice) int {
42 | if (len(a) < len(b)) {
43 | return -1
44 | } else if (len(a) > len(b)) {
45 | return 1;
46 | }
47 | for i := 0; i < len(a); i++ {
48 | if (a[i] < b[i]) {
49 | return -1
50 | } else if (a[i] > b[i]) {
51 | return 1
52 | }
53 | }
54 | return 0
55 | }
56 |
57 | func (a uintSliceSlice) Compare(b uintSliceSlice) int {
58 | if (len(a) < len(b)) {
59 | return -1
60 | } else if (len(a) > len(b)) {
61 | return 1;
62 | }
63 | for i := 0; i < len(a); i++ {
64 | c := a[i].Compare(b[i])
65 | if (c != 0) {
66 | return c
67 | }
68 | }
69 | return 0
70 | }
71 |
72 | func (arr uintSlice) String() string {
73 | var ret string
74 | ret += "["
75 | sep := ""
76 | for i := 0; i < len(arr); i++ {
77 | ret += sep
78 | ret += fmt.Sprintf("%d", arr[i])
79 | sep = ", "
80 | }
81 | ret += "]"
82 | return ret
83 | }
84 |
85 | func (arr uintSliceSlice) String() string {
86 | var ret string
87 | ret += "["
88 | sep := ""
89 | for i := 0; i < len(arr); i++ {
90 | ret += sep
91 | ret += arr[i].String()
92 | sep = ", "
93 | }
94 | ret += "]"
95 | return ret
96 | }
97 |
98 | func test(t *testing.T, maxIdx uint, subsetSize uint,
99 | expected *uintSliceSlice) {
100 | var all uintSliceSlice
101 | ch := NewSubsetChooser(maxIdx, subsetSize)
102 | for ;; {
103 | s := ch.Cur()
104 | all = append(all, s)
105 | if (!ch.Next()) {
106 | break
107 | }
108 | }
109 | sort.Sort(all)
110 | sort.Sort(expected)
111 | if (all.Compare(*expected) != 0) {
112 | t.Errorf("expected:%s. got: %s\n",
113 | expected.String(), all.String())
114 | }
115 | }
116 |
117 | func TestSubsetChooser1(t *testing.T) {
118 | test(t, 3, 2, &uintSliceSlice{ {0, 1}, {0, 2}, {1, 2} } )
119 | test(t, 2, 1, &uintSliceSlice{ {0}, {1} } )
120 | test(t, 3, 1, &uintSliceSlice{ {0}, {1}, {2} } )
121 | test(t, 3, 3, &uintSliceSlice{ {0, 1, 2} } )
122 | test(t, 4, 3, &uintSliceSlice{ {0, 1, 2}, {0, 1, 3}, {0, 2, 3}, {1, 2, 3} } )
123 |
124 | test(t, 100, 0, &uintSliceSlice{ {} })
125 | }
126 |
--------------------------------------------------------------------------------
/hand_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Colin Patrick McCabe
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, version 2.
7 | *
8 | * This program is distributed in the hope that it will be useful,
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | * GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License
14 | * along with this program. If not, see .
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "testing"
22 | )
23 |
24 | func expectHand(t *testing.T, c CardSlice, eTy int, eVal [2]int, eFlushSuit int) {
25 | aHand := MakeHand(c)
26 | eHand := &Hand { eTy, eVal, eFlushSuit, c }
27 | if (!aHand.Identical(eHand)) {
28 | t.Errorf("expected MakeHand to create: %s.\n" +
29 | "Instead, it created: %s", eHand, aHand)
30 | }
31 | }
32 |
33 | func cvalToString(cval int) string {
34 | switch (cval) {
35 | case -1:
36 | return "less than"
37 | case 0:
38 | return "equal to"
39 | case 1:
40 | return "greater than"
41 | }
42 | panic(fmt.Sprintf("unknown cval %d", cval))
43 | }
44 |
45 | func compareHands(t *testing.T, eRes int, a *Hand, b *Hand) {
46 | aRes := a.Compare(b)
47 | if (aRes != eRes) {
48 | t.Errorf("expected hand:\n%s\nto be %s hand:\n%s, but it was " +
49 | "marked as %s", a, cvalToString(eRes), b, cvalToString(aRes))
50 | }
51 | }
52 |
53 | func TestHand1(t *testing.T) {
54 | c1 := CardSlice { &Card{2, DIAMONDS}, &Card{2, CLUBS}, &Card{2, HEARTS},
55 | &Card{QUEEN_VAL, SPADES}, &Card{KING_VAL, SPADES} }
56 | expectHand(t, c1, THREE_OF_A_KIND, [2]int{2, -1}, -1)
57 |
58 | c2 := CardSlice { &Card{8, DIAMONDS}, &Card{9, DIAMONDS}, &Card{10, DIAMONDS},
59 | &Card{QUEEN_VAL, DIAMONDS}, &Card{JACK_VAL, DIAMONDS} }
60 | expectHand(t, c2, STRAIGHT_FLUSH, [2]int{QUEEN_VAL, -1}, DIAMONDS)
61 |
62 | c3 := CardSlice { &Card{8, DIAMONDS}, &Card{KING_VAL, DIAMONDS}, &Card{10, DIAMONDS},
63 | &Card{QUEEN_VAL, DIAMONDS}, &Card{JACK_VAL, DIAMONDS} }
64 | expectHand(t, c3, FLUSH, [2]int{-1, -1}, DIAMONDS)
65 |
66 | c4 := CardSlice { &Card{8, CLUBS}, &Card{8, DIAMONDS}, &Card{10, HEARTS},
67 | &Card{4, DIAMONDS}, &Card{10, DIAMONDS} }
68 | expectHand(t, c4, TWO_PAIR, [2]int{8, 10}, -1)
69 |
70 | c5 := CardSlice { &Card{8, CLUBS}, &Card{9, DIAMONDS}, &Card{2, HEARTS},
71 | &Card{KING_VAL, DIAMONDS}, &Card{ACE_VAL, CLUBS} }
72 | expectHand(t, c5, HIGH_CARD, [2]int{-1, -1}, -1)
73 |
74 | c6 := CardSlice { &Card{2, DIAMONDS}, &Card{2, CLUBS}, &Card{2, HEARTS},
75 | &Card{3, DIAMONDS}, &Card{ACE_VAL, DIAMONDS} }
76 | expectHand(t, c6, THREE_OF_A_KIND, [2]int{2, -1}, -1)
77 |
78 | compareHands(t, 1, MakeHand(CardSlice { &Card{8, DIAMONDS}, &Card{9, DIAMONDS},
79 | &Card{10, DIAMONDS}, &Card{QUEEN_VAL, DIAMONDS}, &Card{JACK_VAL, DIAMONDS} }),
80 | MakeHand(CardSlice { &Card{8, DIAMONDS}, &Card{7, DIAMONDS},
81 | &Card{10, DIAMONDS}, &Card{QUEEN_VAL, DIAMONDS}, &Card{JACK_VAL, DIAMONDS} }))
82 |
83 | compareHands(t, -1, MakeHand(CardSlice { &Card{10, DIAMONDS}, &Card{KING_VAL, DIAMONDS},
84 | &Card{10, CLUBS}, &Card{QUEEN_VAL, DIAMONDS}, &Card{JACK_VAL, DIAMONDS} }),
85 | MakeHand(CardSlice { &Card{8, DIAMONDS}, &Card{7, DIAMONDS},
86 | &Card{10, DIAMONDS}, &Card{QUEEN_VAL, DIAMONDS}, &Card{JACK_VAL, DIAMONDS} }))
87 | }
88 |
--------------------------------------------------------------------------------
/poker-odds.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Colin Patrick McCabe
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, version 2.
7 | *
8 | * This program is distributed in the hope that it will be useful,
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | * GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License
14 | * along with this program. If not, see .
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "flag"
21 | "fmt"
22 | "os"
23 | )
24 |
25 | func usage() {
26 | fmt.Fprintf(os.Stderr,
27 | `%s: the Texas Hold Em' poker odds calculator.
28 |
29 | This program calculates your 'outs' for a Texas Hold Em' poker hand.
30 | Texas Hold Em' is a popular version of poker where each player receives
31 | exactly two secret cards. Then there are five rounds of betting.
32 |
33 | The format used to specify cards is as follows:
34 | [type][suit]
35 | There are 4 suits:
36 | C = clubs, D = diamonds, H = hearts, S = spades
37 | There are 13 different card types:
38 | 1 = A = ace, K = king, Q = queen, J = jack, 2 = 2, ... 10 = 10
39 |
40 | Usage:
41 | -a [your hand as a whitespace-separated list of cards]
42 | -b [the board as a whitespace-separated list of cards]
43 | If no -b is given, it will be assumed that no cards are on the board.
44 |
45 | -g [num_goroutines] Set the number of goroutines to use.
46 |
47 | -h this help message
48 |
49 | Usage Example:
50 | %s -a KS\ QS
51 | Find the outs you have pre-flop with a king and queen of spades.
52 | `, os.Args[0], os.Args[0])
53 | }
54 |
55 | func checkHoleLength(hlen int) {
56 | if (hlen == 2) {
57 | return
58 | }
59 | fmt.Printf("illegal hole length. Expected a length of 2, " +
60 | "but you gave %d hole cards.\n", hlen)
61 | os.Exit(1)
62 | }
63 |
64 | func checkBoardLength(blen int) {
65 | var validLens = []int { 0, 3, 4, 5 }
66 | for i := range(validLens) {
67 | if (blen == validLens[i]) {
68 | if (blen == 0) {
69 | fmt.Printf("Now calculating ALL possible hands that can be " +
70 | "made starting with these hole cards. This will take a " +
71 | "while! You may want to set GOMAXPROCS and use -g.\n" +
72 | "Note: It is much faster to calculate your odds " +
73 | "AFTER the flop.\n")
74 | }
75 | return
76 | }
77 | }
78 |
79 | fmt.Printf("illegal board length. Expected a length of %s, " +
80 | "but your board length was %d.\n", intsToStr(validLens), blen)
81 | os.Exit(1)
82 | }
83 |
84 | func intsToStr(s []int) (string) {
85 | ret := ""
86 | sep := ""
87 | for i := range(s) {
88 | ret += fmt.Sprintf("%s%d", sep, s[i])
89 | sep = ", "
90 | }
91 | return ret
92 | }
93 |
94 | func processHand(h *Hand) {
95 | fmt.Printf("%s\n", h.String())
96 | }
97 |
98 | /* Assumptions: we are the only players in the game
99 | * (Future enhancement: allow the user to specify cards that other players hold!)
100 | *
101 | * 1. Get inputs
102 | * a. your hand (required)
103 | * b. the board (0 cards, 3 , 4, or 5 cards)
104 | * Other numbers of cards represent errors
105 | * (Future enhancement: support other poker games besides Texas Hold em')
106 | *
107 | * 2. for all possible final boards:
108 | * Determine the best type of hand we can make with this board and the
109 | * hole cards.
110 | *
111 | * 3. Print out the odds of getting each type of hand. We can use the fact that
112 | * each distinct final board is equally likely.
113 | *
114 | */
115 | func main() {
116 | ///// Parse and validate user input /////
117 | flag.Usage = usage
118 | var verbose = flag.Bool("v", false, "verbose")
119 | var help = flag.Bool("h", false, "help")
120 | var holeStr = flag.String("a", "", "your two hole cards")
121 | var boardStr = flag.String("b", "", "the board")
122 | var numCsp = flag.Int("g", 3, "number of goprocs")
123 |
124 | flag.Parse()
125 | if (*help) {
126 | usage()
127 | os.Exit(0)
128 | }
129 | if (*holeStr == "") {
130 | fmt.Printf("You must give two hole cards with -a\n")
131 | usage()
132 | os.Exit(1)
133 | }
134 | var hole CardSlice
135 | var errIdx int
136 | hole, errIdx = StrToCards(*holeStr)
137 | if (errIdx != -1) {
138 | fmt.Printf("Error parsing your hole cards: parse error at character %d\n",
139 | errIdx)
140 | os.Exit(1)
141 | }
142 | checkHoleLength(len(hole))
143 | if (*verbose) {
144 | fmt.Printf("Your hole cards: '%s'\n", hole.String());
145 | }
146 | var board CardSlice
147 | board, errIdx = StrToCards(*boardStr)
148 | if (errIdx != -1) {
149 | fmt.Printf("Error parsing the board: parse error at character %d\n",
150 | errIdx)
151 | os.Exit(1)
152 | }
153 | checkBoardLength(len(board))
154 | if (*verbose) {
155 | fmt.Printf("The board: '%s'\n", board.String());
156 | }
157 | base := make(CardSlice, len(board) + len(hole))
158 | copy(base, board)
159 | copy(base[len(board):], hole)
160 | dupe := base.HasDuplicates()
161 | if (dupe != nil) {
162 | fmt.Printf("The card %s appears more than once in your input! " +
163 | "That is not possible.\n", dupe)
164 | os.Exit(1)
165 | }
166 |
167 | ///// Process cards /////
168 | csps := make([]*CardSliceProcessor, *numCsp)
169 | for i := range(csps) {
170 | csps[i] = NewCardSliceProcessor(base)
171 | go csps[i].GoCardSliceProcessor()
172 | }
173 |
174 | future := Make52CardBag()
175 | for i := range(base) {
176 | future.Subtract(base[i])
177 | }
178 | numFutureCards := SPREAD_MAX - len(base)
179 | futureChooser := NewSubsetChooser(uint(future.Len()), uint(numFutureCards))
180 | cspIdx := 0
181 | for ;; {
182 | futureC := futureChooser.Cur()
183 | for i := 0; i < numFutureCards; i++ {
184 | csps[cspIdx].Card <- future.Get(futureC[i])
185 | }
186 | cspIdx++
187 | if (cspIdx >= *numCsp) {
188 | cspIdx = 0
189 | }
190 | if (!futureChooser.Next()) {
191 | break
192 | }
193 | }
194 |
195 | // Tell cardSliceProcessors to finish
196 | for i := range(csps) {
197 | csps[i].Quit <- true
198 | }
199 |
200 | // Once each cardSliceProcessor is finished, get its results
201 | // Merge all results together
202 | allResults := new(ResultSet)
203 | for i := range(csps) {
204 | <-csps[i].Finished
205 | allResults.MergeResultSet(&csps[i].Results)
206 | }
207 |
208 | // Now print the final results
209 | fmt.Printf("results:\n%s", allResults.String())
210 | }
211 |
--------------------------------------------------------------------------------
/card.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Colin Patrick McCabe
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, version 2.
7 | *
8 | * This program is distributed in the hope that it will be useful,
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | * GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License
14 | * along with this program. If not, see .
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | )
22 |
23 | const (
24 | PARSE_STATE_EAT_VAL = iota
25 | PARSE_STATE_EAT_VAL_SAW_1
26 | PARSE_STATE_EAT_SUIT
27 | )
28 |
29 | const (
30 | DIAMONDS = iota
31 | CLUBS
32 | HEARTS
33 | SPADES
34 | )
35 |
36 | type Card struct {
37 | val int
38 | suit int
39 | }
40 |
41 | func (p Card) Copy() *Card {
42 | return &Card { p.val, p.suit }
43 | }
44 |
45 | func cardValToStr(v int) (string) {
46 | switch {
47 | case v == 11:
48 | return "J"
49 | case v == 12:
50 | return "Q"
51 | case v == 13:
52 | return "K"
53 | case v == 14:
54 | return "A"
55 | }
56 | return fmt.Sprintf("%d", v)
57 | }
58 |
59 | func suitToStr(s int) (string) {
60 | switch {
61 | case s == CLUBS:
62 | return "♣C"
63 | case s == DIAMONDS:
64 | return "♦D"
65 | case s == HEARTS:
66 | return "♥H"
67 | case s == SPADES:
68 | return "♠S"
69 | }
70 | panic(fmt.Sprintf("invalid suit %d", s))
71 | }
72 |
73 | func (c *Card) String() string {
74 | return fmt.Sprintf("%s%s", cardValToStr(c.val), suitToStr(c.suit))
75 | }
76 |
77 | /* It's important that the cards compare in this order. It makes detecting
78 | * straights easier because cards of a similar value (as opposed to suit) are
79 | * adjacent. Don't change this sort order without updating hand.go
80 | */
81 | func (p *Card) Compare(rhs *Card) int {
82 | if (p.suit < rhs.suit) {
83 | return -1;
84 | }
85 | if (p.suit > rhs.suit) {
86 | return 1;
87 | }
88 | if (p.val < rhs.val) {
89 | return -1;
90 | }
91 | if (p.val > rhs.val) {
92 | return 1;
93 | }
94 | return 0;
95 | }
96 |
97 | func StrToCard(str string, cnt *int) (myCard *Card) {
98 | myCard = new(Card)
99 | var parseState = PARSE_STATE_EAT_VAL
100 | for ;*cnt < len(str); {
101 | var c = str[*cnt]
102 | *cnt++
103 | switch {
104 | case parseState == PARSE_STATE_EAT_VAL:
105 | switch {
106 | case c == ' ' || c == '\t':
107 | continue
108 | case c == '1':
109 | parseState = PARSE_STATE_EAT_VAL_SAW_1
110 | case c >= '2' && c <= '9':
111 | myCard.val = (int)(c - '0')
112 | parseState = PARSE_STATE_EAT_SUIT
113 | case c == 'J':
114 | myCard.val = JACK_VAL
115 | parseState = PARSE_STATE_EAT_SUIT
116 | case c == 'Q':
117 | myCard.val = QUEEN_VAL
118 | parseState = PARSE_STATE_EAT_SUIT
119 | case c == 'K':
120 | myCard.val = KING_VAL
121 | parseState = PARSE_STATE_EAT_SUIT
122 | case c == 'A':
123 | myCard.val = ACE_VAL
124 | parseState = PARSE_STATE_EAT_SUIT
125 | default:
126 | return nil
127 | }
128 | case parseState == PARSE_STATE_EAT_VAL_SAW_1:
129 | switch {
130 | case c == '0':
131 | myCard.val = 10
132 | parseState = PARSE_STATE_EAT_SUIT
133 | default:
134 | return nil
135 | }
136 | case parseState == PARSE_STATE_EAT_SUIT:
137 | switch {
138 | case c == 'C':
139 | myCard.suit = CLUBS
140 | case c == 'D':
141 | myCard.suit = DIAMONDS
142 | case c == 'H':
143 | myCard.suit = HEARTS
144 | case c == 'S':
145 | myCard.suit = SPADES
146 | default:
147 | return nil
148 | }
149 | return myCard
150 | }
151 | }
152 | *cnt = -1
153 | return nil
154 | }
155 |
156 | type CardSlice []*Card
157 |
158 | func (arr CardSlice) Copy() CardSlice {
159 | ret := make(CardSlice, len(arr))
160 | for i := range(arr) {
161 | ret[i] = arr[i].Copy()
162 | }
163 | return ret
164 | }
165 |
166 | /* In poker, the 'kicker' breaks ties between hands of the same type.
167 | * An example:
168 | * Hand1: 6D QC QS KS KC Kicker: 6D
169 | * Hand2: 9H QH QD KC KS Kicker: 9H (wins)
170 | *
171 | * Hand1: 6D 8C 9S JS JC Kicker: 6D 8C 9S
172 | * Hand2: 2H 3H 10D JD JH Kicker: 2H 3H 10D (wins)
173 | *
174 | * Kickers are compared in lexicographical order, starting with the highest
175 | * valued card. Suit is irrelevant; only card value matters.
176 | *
177 | * This function can return 0 even if the two CardSlices are different.
178 | */
179 | func (arr CardSlice) CompareKicker(rhs CardSlice) int {
180 | var a int // needs to be signed
181 | var b int
182 | a = len(arr) - 1
183 | b = len(rhs) - 1
184 | for ;; {
185 | if (a < 0) {
186 | if (b < 0) {
187 | return 0;
188 | } else {
189 | return -1;
190 | }
191 | } else if (b < 0) {
192 | return 1;
193 | }
194 | if (arr[a].val < arr[b].val) {
195 | return -1;
196 | } else if (arr[a].val > arr[b].val) {
197 | return 1;
198 | }
199 | // ignore suit!
200 | a--
201 | b--
202 | }
203 | return 0
204 | }
205 |
206 | func (arr CardSlice) Identical(rhs CardSlice) bool {
207 | if (len(arr) != len(rhs)) {
208 | return false
209 | }
210 | for i := range(arr) {
211 | if ((arr[i].val != arr[i].val) || (arr[i].suit != arr[i].suit)) {
212 | return false
213 | }
214 | }
215 | return true
216 | }
217 |
218 | func (arr CardSlice) Len() int {
219 | return len(arr)
220 | }
221 |
222 | func (arr CardSlice) Less(i, j int) bool {
223 | if (arr[i].val < arr[j].val) {
224 | return true
225 | }
226 | if (arr[i].val > arr[j].val) {
227 | return false
228 | }
229 | if (arr[i].suit < arr[j].suit) {
230 | return true
231 | }
232 | return false
233 | }
234 |
235 | func (arr CardSlice) Swap(i, j int) {
236 | arr[i], arr[j] = arr[j], arr[i]
237 | }
238 |
239 | func (arr CardSlice) String() (string) {
240 | ret := ""
241 | sep := ""
242 | for i := range(arr) {
243 | ret += fmt.Sprintf("%s%s", sep, arr[i].String())
244 | sep = ", "
245 | }
246 | return ret
247 | }
248 |
249 | // Could do this smarter if we knew that we were sorted...
250 | func (arr CardSlice) HasDuplicates() *Card {
251 | for i := range(arr) {
252 | for j := range(arr) {
253 | if i == j {
254 | continue
255 | }
256 | if (arr[i].Compare(arr[j]) == 0) {
257 | return arr[i]
258 | }
259 | }
260 | }
261 | return nil
262 | }
263 |
264 | func StrToCards(str string) (ret CardSlice, cnt int) {
265 | for cnt = 0; cnt != -1; {
266 | var c = StrToCard(str, &cnt)
267 | if (c == nil) {
268 | return
269 | }
270 | ret = append(ret, c)
271 | }
272 | return ret, cnt
273 | }
274 |
--------------------------------------------------------------------------------
/hand.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Colin Patrick McCabe
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, version 2.
7 | *
8 | * This program is distributed in the hope that it will be useful,
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | * GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License
14 | * along with this program. If not, see .
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "sort"
22 | )
23 |
24 | const BOARD_MAX = 5
25 |
26 | const HOLE_SZ = 2
27 |
28 | const SPREAD_MAX = BOARD_MAX + HOLE_SZ
29 |
30 | const HAND_SZ = 5
31 |
32 | const (
33 | JACK_VAL = 11
34 | QUEEN_VAL = 12
35 | KING_VAL = 13
36 | ACE_VAL = 14
37 | )
38 |
39 | const (
40 | HIGH_CARD = iota
41 | PAIR
42 | TWO_PAIR
43 | THREE_OF_A_KIND
44 | STRAIGHT
45 | FLUSH
46 | FULL_HOUSE
47 | FOUR_OF_A_KIND
48 | STRAIGHT_FLUSH
49 | MAX_HANDS
50 | )
51 |
52 | func twc(a int, b int, alt int) int {
53 | if (a < b) {
54 | return -1
55 | } else if (a > b) {
56 | return 1
57 | }
58 | return alt
59 | }
60 |
61 | type Hand struct {
62 | ty int
63 | val [2]int
64 | flushSuit int
65 | cards CardSlice
66 | }
67 |
68 | func HandTyToStr(ty int) string {
69 | switch (ty) {
70 | case HIGH_CARD:
71 | return "nothing"
72 | case PAIR:
73 | return "a pair"
74 | case TWO_PAIR:
75 | return "two pair"
76 | case THREE_OF_A_KIND:
77 | return "three of a kind"
78 | case STRAIGHT:
79 | return "a straight"
80 | case FLUSH:
81 | return "a flush"
82 | case FULL_HOUSE:
83 | return "a full house"
84 | case FOUR_OF_A_KIND:
85 | return "four of a kind"
86 | case STRAIGHT_FLUSH:
87 | return "a straight flush"
88 | default:
89 | panic(fmt.Sprintf("unexpected hand type %d", ty))
90 | }
91 | return ""
92 | }
93 |
94 | func handTyHasKicker(ty int) bool {
95 | /* For a HIGH_CARD hand, all 5 cards can be considered the kicker. Most
96 | * poker players wouldn't use the terminology this way, but it works for
97 | * our purposes.
98 | *
99 | * Similarly, when comparing two flushes, you can just consider the
100 | * entirety of both hands as the kicker.
101 | *
102 | * Another thing:
103 | * In an actual game you never compare the kickers of two THREE_OF_A_KIND
104 | * or FOUR_OF_A_KIND hands, since there are only 52 cards in the deck, and
105 | * you can't make two such hands with identical values. However, in the
106 | * abstract, these hands do have a kicker and we sometimes need to use it.
107 | *
108 | * The only hands where we really know that comparing the kicker would be
109 | * useless are FULL_HOUSE, STRAIGHT, and STRAIGHT_FLUSH.
110 | */
111 | return (!((ty == FULL_HOUSE) || (ty == STRAIGHT) || (ty == STRAIGHT_FLUSH)))
112 | }
113 |
114 | func (h *Hand) GetTy() int {
115 | return h.ty
116 | }
117 |
118 | /* Compare two Hands using poker rules.
119 | *
120 | * This will return 0 sometimes even when the two Hands are not identical. This
121 | * reflects the way poker works.
122 | */
123 | func (h *Hand) Compare(rhs *Hand) int {
124 | c := twc(h.ty, rhs.ty,
125 | twc(h.val[0], rhs.val[0],
126 | twc(h.val[1], rhs.val[1], 0)))
127 | if ((c == 0) && (handTyHasKicker(h.ty))) {
128 | c = h.cards.CompareKicker(rhs.cards)
129 | }
130 | return c
131 | }
132 |
133 | /* Return true if two hands are identical.
134 | */
135 | func (h *Hand) Identical(rhs *Hand) bool {
136 | c := twc(h.ty, rhs.ty,
137 | twc(h.val[0], rhs.val[0],
138 | twc(h.val[1], rhs.val[1], 0)))
139 | if (c != 0) {
140 | return false
141 | }
142 | return h.cards.Identical(rhs.cards)
143 | }
144 |
145 | func MakeHandImpl(cardsIn CardSlice) *Hand {
146 | cards := cardsIn.Copy()
147 | // Sort the cards appropriately to make straight detection easier.
148 | sort.Sort(cards)
149 |
150 | h := &Hand{ -1, [2]int {-1, -1}, -1, cards }
151 | var vals = make(map[int] int)
152 | var suits = make(map[int] int)
153 | for i := range(cards) {
154 | c := cards[i]
155 | vals[c.val] = vals[c.val] + 1
156 | suits[c.suit] = suits[c.suit] + 1
157 | }
158 |
159 | // check for flush
160 | for i := range(suits) {
161 | if (suits[i] >= 5) {
162 | h.flushSuit = i
163 | }
164 | }
165 | // check for straight flush
166 | runEnd := -1
167 | runLen := 0
168 | prev := -1
169 | if (cards[len(cards)-1].val == ACE_VAL) {
170 | // Aces play both low and high in straights.
171 | //
172 | // This is a special case where we have a bunch of cards where the
173 | // highest card is a king, and we also have an ace.
174 | // In this situation, the last run of cards is actually one card longer
175 | // than it might seem without taking the ace into account.
176 | runLen++
177 | }
178 | for i := range(cards) {
179 | if (prev + 1 == cards[i].val) {
180 | runEnd = cards[i].val
181 | runLen++
182 | if (runLen >= 5) {
183 | // I know it may seem like by breaking out here, we may miss
184 | // some potential straights. But remember that a poker hand is
185 | // exactly 5 cards. So to have gotten to this point, all 5
186 | // cards must have played, so we must have discovered the best
187 | // possible straight.
188 | //
189 | // It's the Ace playing both high and low that makes this
190 | // weird.
191 | break
192 | }
193 | } else if (prev == cards[i].val) {
194 | // We have more than one card with the same value.
195 | // The duplicate cards don't help us get a straight, but they also
196 | // don't mean we don't have one.
197 | } else {
198 | // Clear the straight counter.
199 | runLen = 1
200 | }
201 | prev = cards[i].val
202 | }
203 | if ((runLen >= 5) && (h.flushSuit != -1)) {
204 | h.val[0] = runEnd
205 | h.ty = STRAIGHT_FLUSH
206 | return h
207 | }
208 |
209 | freqs := make(map[int] []int)
210 | for k,v := range(vals) {
211 | if (v > 4) {
212 | panic(fmt.Sprintf("on hand %s, got %d of a kind for value %d (max is 4)\n",
213 | cards.String(), v, k))
214 | }
215 | curFreqs := freqs[v]
216 | m := 0
217 | for m = 0; m < len(curFreqs); m++ {
218 | if (curFreqs[m] >= k) {
219 | break
220 | }
221 | }
222 | newFreqs := make([]int, len(curFreqs) + 1)
223 | copy(newFreqs, curFreqs[:m])
224 | newFreqs[m] = k
225 | copy(newFreqs[m+1:], curFreqs[m:])
226 | freqs[v] = newFreqs
227 | }
228 |
229 | // four of a kind
230 | if (len(freqs[4]) > 0) {
231 | h.ty = FOUR_OF_A_KIND
232 | h.val[0] = freqs[4][0]
233 | return h
234 | }
235 |
236 | // full house
237 | if (len(freqs[3]) > 0) {
238 | if (len(freqs[3]) > 1) {
239 | h.val[0] = freqs[3][0]
240 | h.val[1] = freqs[3][1]
241 | h.ty = FULL_HOUSE
242 | } else if (len(freqs[2]) > 0) {
243 | h.val[0] = freqs[3][0]
244 | h.val[1] = freqs[2][0]
245 | h.ty = FULL_HOUSE
246 | }
247 | }
248 |
249 | // flush
250 | if (h.flushSuit != -1) {
251 | h.ty = FLUSH
252 | return h
253 | }
254 |
255 | // straight
256 | if (runLen >= 5) {
257 | h.val[0] = runEnd
258 | h.ty = STRAIGHT
259 | return h
260 | }
261 |
262 | // three of a kind
263 | if (len(freqs[3]) > 0) {
264 | h.val[0] = freqs[3][0]
265 | h.ty = THREE_OF_A_KIND
266 | return h
267 | }
268 |
269 | // two pairs
270 | if (len(freqs[2]) >= 2) {
271 | h.val[0] = freqs[2][0]
272 | h.val[1] = freqs[2][1]
273 | h.ty = TWO_PAIR
274 | return h
275 | }
276 |
277 | // a pair
278 | if (len(freqs[2]) >= 1) {
279 | h.val[0] = freqs[2][0]
280 | h.ty = PAIR
281 | return h
282 | }
283 |
284 | // I guess not.
285 | h.ty = HIGH_CARD
286 | return h
287 | }
288 |
289 | func MakeHand(cards CardSlice) *Hand {
290 | ret := MakeHandImpl(cards)
291 | //fmt.Printf("input: %s\n.output:%s\n", cards, ret)
292 | return ret
293 | }
294 |
295 |
296 | func (h *Hand) String() string {
297 | ret := "Hand(ty:"
298 | switch (h.ty) {
299 | case HIGH_CARD:
300 | ret += "HIGH CARD"
301 | case PAIR:
302 | ret += "PAIR of "
303 | ret += cardValToStr(h.val[0])
304 | case TWO_PAIR:
305 | ret += "TWO PAIR of "
306 | ret += cardValToStr(h.val[0])
307 | ret += " and "
308 | ret += cardValToStr(h.val[1])
309 | case THREE_OF_A_KIND:
310 | ret += "THREE OF A KIND of "
311 | ret += cardValToStr(h.val[0])
312 | case STRAIGHT:
313 | ret += "STRAIGHT with high of "
314 | ret += cardValToStr(h.val[0])
315 | case FLUSH:
316 | ret += "FLUSH in "
317 | ret += suitToStr(h.flushSuit)
318 | case FULL_HOUSE:
319 | ret += "FULL HOUSE of "
320 | ret += cardValToStr(h.val[0])
321 | ret += " full of "
322 | ret += cardValToStr(h.val[1])
323 | case FOUR_OF_A_KIND:
324 | ret += "FOUR OF A KIND of "
325 | ret += cardValToStr(h.val[0])
326 | case STRAIGHT_FLUSH:
327 | ret += "STRAIGHT FLUSH with high of "
328 | ret += cardValToStr(h.val[0])
329 | ret += " in "
330 | ret += suitToStr(h.flushSuit)
331 | }
332 |
333 | ret += ", cards:"
334 | sep := ""
335 | for c := range(h.cards) {
336 | ret += sep
337 | ret += h.cards[c].String()
338 | sep = ", "
339 | }
340 | ret += ")"
341 |
342 | return ret
343 | }
344 |
345 | type HandSlice []*Hand
346 |
347 | func (hs HandSlice) Len() int {
348 | return len(hs)
349 | }
350 |
351 | func (hs HandSlice) Less(i, j int) bool {
352 | if (hs[i] == nil) {
353 | if (hs[j] == nil) {
354 | return false
355 | } else {
356 | return true
357 | }
358 | } else if (hs[j] == nil) {
359 | return false
360 | }
361 | c := hs[i].Compare(hs[j])
362 | return (c < 0)
363 | }
364 |
365 | func (hs HandSlice) Swap(i, j int) {
366 | hs[i], hs[j] = hs[j], hs[i]
367 | }
368 |
369 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Library General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C)
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License
307 | along with this program; if not, write to the Free Software
308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
309 |
310 |
311 | Also add information on how to contact you by electronic and paper mail.
312 |
313 | If the program is interactive, make it output a short notice like this
314 | when it starts in an interactive mode:
315 |
316 | Gnomovision version 69, Copyright (C) year name of author
317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318 | This is free software, and you are welcome to redistribute it
319 | under certain conditions; type `show c' for details.
320 |
321 | The hypothetical commands `show w' and `show c' should show the appropriate
322 | parts of the General Public License. Of course, the commands you use may
323 | be called something other than `show w' and `show c'; they could even be
324 | mouse-clicks or menu items--whatever suits your program.
325 |
326 | You should also get your employer (if you work as a programmer) or your
327 | school, if any, to sign a "copyright disclaimer" for the program, if
328 | necessary. Here is a sample; alter the names:
329 |
330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
332 |
333 | , 1 April 1989
334 | Ty Coon, President of Vice
335 |
336 | This General Public License does not permit incorporating your program into
337 | proprietary programs. If your program is a subroutine library, you may
338 | consider it more useful to permit linking proprietary applications with the
339 | library. If this is what you want to do, use the GNU Library General
340 | Public License instead of this License.
341 |
--------------------------------------------------------------------------------