hand_eval.gno
5.22 Kb ยท 236 lines
1package poker
2
3// EvaluateHand evaluates the best 5-card poker hand from 7 cards
4// (2 hole cards + 5 community cards)
5func EvaluateHand(holeCards [2]Card, community []Card) HandResult {
6 // Combine all available cards
7 allCards := make([]Card, 0, 7)
8 allCards = append(allCards, holeCards[0], holeCards[1])
9 allCards = append(allCards, community...)
10
11 if len(allCards) < 5 {
12 return HandResult{Rank: HighCard}
13 }
14
15 // Generate all C(n, 5) combinations and find the best hand
16 best := HandResult{Rank: HighCard}
17 n := len(allCards)
18
19 for i := 0; i < n-4; i++ {
20 for j := i + 1; j < n-3; j++ {
21 for k := j + 1; k < n-2; k++ {
22 for l := k + 1; l < n-1; l++ {
23 for m := l + 1; m < n; m++ {
24 hand := [5]Card{allCards[i], allCards[j], allCards[k], allCards[l], allCards[m]}
25 result := evaluate5(hand)
26 if compareHands(result, best) > 0 {
27 best = result
28 }
29 }
30 }
31 }
32 }
33 }
34
35 return best
36}
37
38// evaluate5 evaluates exactly 5 cards
39func evaluate5(cards [5]Card) HandResult {
40 // Sort cards by value descending
41 sorted := sortCards(cards)
42
43 isFlush := checkFlush(sorted)
44 isStraight, highCard := checkStraight(sorted)
45
46 // Count value occurrences
47 counts := make(map[int]int)
48 for _, c := range sorted {
49 counts[c.Value]++
50 }
51
52 // Classify hand
53 if isFlush && isStraight {
54 if highCard == 14 {
55 return HandResult{Rank: RoyalFlush, TieBreak: [5]int{14, 0, 0, 0, 0}, BestCards: sorted}
56 }
57 return HandResult{Rank: StraightFlush, TieBreak: [5]int{highCard, 0, 0, 0, 0}, BestCards: sorted}
58 }
59
60 // Four of a kind
61 if hasNOfAKind(counts, 4) {
62 quad := getValuesWithCount(counts, 4)
63 kicker := getValuesWithCount(counts, 1)
64 return HandResult{
65 Rank: FourOfAKind,
66 TieBreak: [5]int{quad[0], kicker[0], 0, 0, 0},
67 BestCards: sorted,
68 }
69 }
70
71 // Full house
72 if hasNOfAKind(counts, 3) && hasNOfAKind(counts, 2) {
73 trips := getValuesWithCount(counts, 3)
74 pair := getValuesWithCount(counts, 2)
75 return HandResult{
76 Rank: FullHouse,
77 TieBreak: [5]int{trips[0], pair[0], 0, 0, 0},
78 BestCards: sorted,
79 }
80 }
81
82 if isFlush {
83 return HandResult{
84 Rank: Flush,
85 TieBreak: [5]int{sorted[0].Value, sorted[1].Value, sorted[2].Value, sorted[3].Value, sorted[4].Value},
86 BestCards: sorted,
87 }
88 }
89
90 if isStraight {
91 return HandResult{
92 Rank: Straight,
93 TieBreak: [5]int{highCard, 0, 0, 0, 0},
94 BestCards: sorted,
95 }
96 }
97
98 // Three of a kind
99 if hasNOfAKind(counts, 3) {
100 trips := getValuesWithCount(counts, 3)
101 kickers := getValuesWithCount(counts, 1)
102 sortDescending(kickers)
103 tb := [5]int{trips[0], 0, 0, 0, 0}
104 for i, v := range kickers {
105 if i+1 < 5 {
106 tb[i+1] = v
107 }
108 }
109 return HandResult{Rank: ThreeOfAKind, TieBreak: tb, BestCards: sorted}
110 }
111
112 // Two pair
113 pairs := getValuesWithCount(counts, 2)
114 if len(pairs) == 2 {
115 sortDescending(pairs)
116 kickers := getValuesWithCount(counts, 1)
117 return HandResult{
118 Rank: TwoPair,
119 TieBreak: [5]int{pairs[0], pairs[1], kickers[0], 0, 0},
120 BestCards: sorted,
121 }
122 }
123
124 // One pair
125 if len(pairs) == 1 {
126 kickers := getValuesWithCount(counts, 1)
127 sortDescending(kickers)
128 tb := [5]int{pairs[0], 0, 0, 0, 0}
129 for i, v := range kickers {
130 if i+1 < 5 {
131 tb[i+1] = v
132 }
133 }
134 return HandResult{Rank: OnePair, TieBreak: tb, BestCards: sorted}
135 }
136
137 // High card
138 return HandResult{
139 Rank: HighCard,
140 TieBreak: [5]int{sorted[0].Value, sorted[1].Value, sorted[2].Value, sorted[3].Value, sorted[4].Value},
141 BestCards: sorted,
142 }
143}
144
145// compareHands compares two hand results: returns >0 if a is better, <0 if b is better, 0 if equal
146func compareHands(a, b HandResult) int {
147 if a.Rank != b.Rank {
148 return int(a.Rank) - int(b.Rank)
149 }
150 for i := 0; i < 5; i++ {
151 if a.TieBreak[i] != b.TieBreak[i] {
152 return a.TieBreak[i] - b.TieBreak[i]
153 }
154 }
155 return 0
156}
157
158// CompareHandResults is exported for use in the realm
159func CompareHandResults(a, b HandResult) int {
160 return compareHands(a, b)
161}
162
163// Helper functions
164
165func sortCards(cards [5]Card) [5]Card {
166 sorted := cards
167 for i := 0; i < 4; i++ {
168 for j := i + 1; j < 5; j++ {
169 if sorted[j].Value > sorted[i].Value {
170 sorted[i], sorted[j] = sorted[j], sorted[i]
171 }
172 }
173 }
174 return sorted
175}
176
177func checkFlush(cards [5]Card) bool {
178 suit := cards[0].Suit
179 for i := 1; i < 5; i++ {
180 if cards[i].Suit != suit {
181 return false
182 }
183 }
184 return true
185}
186
187func checkStraight(cards [5]Card) (bool, int) {
188 // Normal straight check (cards are sorted descending)
189 isSeq := true
190 for i := 0; i < 4; i++ {
191 if cards[i].Value-cards[i+1].Value != 1 {
192 isSeq = false
193 break
194 }
195 }
196 if isSeq {
197 return true, cards[0].Value
198 }
199
200 // Check for A-2-3-4-5 (wheel)
201 if cards[0].Value == 14 && cards[1].Value == 5 && cards[2].Value == 4 && cards[3].Value == 3 && cards[4].Value == 2 {
202 return true, 5 // 5-high straight
203 }
204
205 return false, 0
206}
207
208func hasNOfAKind(counts map[int]int, n int) bool {
209 for _, c := range counts {
210 if c == n {
211 return true
212 }
213 }
214 return false
215}
216
217func getValuesWithCount(counts map[int]int, n int) []int {
218 result := []int{}
219 for v, c := range counts {
220 if c == n {
221 result = append(result, v)
222 }
223 }
224 sortDescending(result)
225 return result
226}
227
228func sortDescending(arr []int) {
229 for i := 0; i < len(arr)-1; i++ {
230 for j := i + 1; j < len(arr); j++ {
231 if arr[j] > arr[i] {
232 arr[i], arr[j] = arr[j], arr[i]
233 }
234 }
235 }
236}