Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}