├── .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 | --------------------------------------------------------------------------------