mirror of
https://github.com/Stone-Red-Code/se-poker.git
synced 2026-09-04 00:56:18 +02:00
223 lines
5.2 KiB
Go
223 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
type Hand struct {
|
|
Cards []Card
|
|
}
|
|
|
|
// NewHand parses an input string and returns a new Hand.
|
|
func NewHand(input string) (*Hand, error) {
|
|
parts := strings.Fields(input)
|
|
if len(parts) != 5 {
|
|
return nil, fmt.Errorf("invalid number of cards: %d", len(parts))
|
|
}
|
|
|
|
var cards []Card
|
|
valueMap := map[byte]int{
|
|
'2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
|
|
'T': 10, 'J': 11, 'Q': 12, 'K': 13, 'A': 14,
|
|
}
|
|
|
|
for _, p := range parts {
|
|
if len(p) != 2 {
|
|
return nil, fmt.Errorf("invalid card format: %s", p)
|
|
}
|
|
suitChar := string(p[0])
|
|
valChar := p[1]
|
|
|
|
val, ok := valueMap[valChar]
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid value: %c", valChar)
|
|
}
|
|
cards = append(cards, Card{Suit: suitChar, Value: val})
|
|
}
|
|
return &Hand{Cards: cards}, nil
|
|
}
|
|
|
|
type HandCategory int
|
|
|
|
const (
|
|
HighCard HandCategory = iota
|
|
Pair
|
|
TwoPair
|
|
ThreeOfAKind
|
|
Straight
|
|
Flush
|
|
FullHouse
|
|
FourOfAKind
|
|
StraightFlush
|
|
RoyalFlush
|
|
)
|
|
|
|
var handNames = []string{
|
|
"High Card", "Pair", "Two Pair", "Three of a Kind", "Straight",
|
|
"Flush", "Full House", "Four of a Kind", "Straight Flush", "Royal Flush",
|
|
}
|
|
|
|
type HandResult struct {
|
|
Category HandCategory
|
|
TieBreakers []int
|
|
}
|
|
|
|
// Evaluate evaluates the hand and returns its category and tiebreakers.
|
|
func (h *Hand) Evaluate() HandResult {
|
|
sort.Slice(h.Cards, func(i, j int) bool {
|
|
return h.Cards[i].Value > h.Cards[j].Value
|
|
})
|
|
|
|
counts := make(map[int]int)
|
|
suits := make(map[string]int)
|
|
for _, c := range h.Cards {
|
|
counts[c.Value]++
|
|
suits[c.Suit]++
|
|
}
|
|
|
|
isFlush := len(suits) == 1
|
|
isStraight, straightHigh := h.checkStraight()
|
|
|
|
if isFlush && isStraight {
|
|
if straightHigh == 14 {
|
|
return HandResult{Category: RoyalFlush, TieBreakers: []int{}}
|
|
}
|
|
return HandResult{Category: StraightFlush, TieBreakers: []int{straightHigh}}
|
|
}
|
|
|
|
type group struct {
|
|
val int
|
|
count int
|
|
}
|
|
var groups []group
|
|
for v, c := range counts {
|
|
groups = append(groups, group{v, c})
|
|
}
|
|
sort.Slice(groups, func(i, j int) bool {
|
|
if groups[i].count != groups[j].count {
|
|
return groups[i].count > groups[j].count
|
|
}
|
|
return groups[i].val > groups[j].val
|
|
})
|
|
|
|
var tieBreakers []int
|
|
for _, g := range groups {
|
|
tieBreakers = append(tieBreakers, g.val)
|
|
}
|
|
if len(groups) > 1 && groups[0].count == 1 {
|
|
// Fallback for Straight/Flush Sortierung: sicherstellen, dass es rein nach Wert geht
|
|
tieBreakers = nil
|
|
for _, c := range h.Cards {
|
|
tieBreakers = append(tieBreakers, c.Value)
|
|
}
|
|
}
|
|
|
|
|
|
if groups[0].count == 4 {
|
|
return HandResult{Category: FourOfAKind, TieBreakers: []int{groups[0].val, groups[1].val}}
|
|
}
|
|
if groups[0].count == 3 && groups[1].count == 2 {
|
|
return HandResult{Category: FullHouse, TieBreakers: []int{groups[0].val, groups[1].val}}
|
|
}
|
|
if isFlush {
|
|
return HandResult{Category: Flush, TieBreakers: tieBreakers}
|
|
}
|
|
if isStraight {
|
|
return HandResult{Category: Straight, TieBreakers: []int{straightHigh}}
|
|
}
|
|
if groups[0].count == 3 {
|
|
return HandResult{Category: ThreeOfAKind, TieBreakers: []int{groups[0].val, groups[1].val, groups[2].val}}
|
|
}
|
|
if groups[0].count == 2 && groups[1].count == 2 {
|
|
return HandResult{Category: TwoPair, TieBreakers: []int{groups[0].val, groups[1].val, groups[2].val}}
|
|
}
|
|
if groups[0].count == 2 {
|
|
return HandResult{Category: Pair, TieBreakers: []int{groups[0].val, groups[1].val, groups[2].val, groups[3].val}}
|
|
}
|
|
|
|
return HandResult{Category: HighCard, TieBreakers: tieBreakers}
|
|
}
|
|
|
|
func (h *Hand) checkStraight() (bool, int) {
|
|
isStd := true
|
|
for i := 0; i < 4; i++ {
|
|
if h.Cards[i].Value != h.Cards[i+1].Value+1 {
|
|
isStd = false
|
|
break
|
|
}
|
|
}
|
|
if isStd {
|
|
return true, h.Cards[0].Value
|
|
}
|
|
|
|
if h.Cards[0].Value == 14 && h.Cards[1].Value == 5 && h.Cards[2].Value == 4 && h.Cards[3].Value == 3 && h.Cards[4].Value == 2 {
|
|
return true, 5
|
|
}
|
|
|
|
return false, 0
|
|
}
|
|
|
|
// Compare compares the current hand with another hand.
|
|
// Returns 1 if the current hand wins, 2 if the other hand wins, and 0 for a tie.
|
|
func (h *Hand) Compare(other *Hand) int {
|
|
h1Result := h.Evaluate()
|
|
h2Result := other.Evaluate()
|
|
|
|
if h1Result.Category > h2Result.Category {
|
|
return 1
|
|
}
|
|
if h2Result.Category > h1Result.Category {
|
|
return 2
|
|
}
|
|
|
|
for i := 0; i < len(h1Result.TieBreakers); i++ {
|
|
if h1Result.TieBreakers[i] > h2Result.TieBreakers[i] {
|
|
return 1
|
|
}
|
|
if h2Result.TieBreakers[i] > h1Result.TieBreakers[i] {
|
|
return 2
|
|
}
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
// FindBestHand finds the best 5-card hand from 7 given cards.
|
|
func FindBestHand(sevenCards []Card) (*Hand, error) {
|
|
if len(sevenCards) != 7 {
|
|
return nil, fmt.Errorf("expected 7 cards, got %d", len(sevenCards))
|
|
}
|
|
|
|
var bestHand *Hand
|
|
|
|
// Generate all combinations of 5 cards from 7
|
|
for i := 0; i < 7; i++ {
|
|
for j := i + 1; j < 7; j++ {
|
|
for k := j + 1; k < 7; k++ {
|
|
for l := k + 1; l < 7; l++ {
|
|
for m := l + 1; m < 7; m++ {
|
|
currentCards := []Card{
|
|
sevenCards[i], sevenCards[j], sevenCards[k], sevenCards[l], sevenCards[m],
|
|
}
|
|
currentHand := &Hand{Cards: currentCards}
|
|
|
|
if bestHand == nil {
|
|
bestHand = currentHand
|
|
} else {
|
|
// Compare currentHand with bestHand
|
|
// h.Compare(other) returns 1 if h wins, 2 if other wins, 0 for tie
|
|
if currentHand.Compare(bestHand) == 1 {
|
|
bestHand = currentHand
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestHand, nil
|
|
}
|