2024-05-18 09:21:41 +00:00
|
|
|
package pokercore
|
|
|
|
|
2024-05-19 05:31:55 +00:00
|
|
|
// these helper functions are used in a Cards.PokerHand() constructor and in
|
|
|
|
// the calculateScore() function called from it.
|
|
|
|
// they only work with exactly five cards, no duplicates, and no wild cards.
|
|
|
|
// they are not intended to be used in any other context, which is why
|
|
|
|
// they are not exported
|
|
|
|
|
2024-05-18 09:21:41 +00:00
|
|
|
func (c Cards) pairRank() Rank {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := c.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank {
|
|
|
|
return sorted[0].Rank
|
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank {
|
|
|
|
return sorted[1].Rank
|
|
|
|
}
|
|
|
|
if sorted[2].Rank == sorted[3].Rank {
|
|
|
|
return sorted[2].Rank
|
|
|
|
}
|
|
|
|
if sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return sorted[3].Rank
|
|
|
|
}
|
|
|
|
panic("nope")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) pairFirstKicker() Card {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := c.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank {
|
|
|
|
return sorted[4]
|
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank {
|
|
|
|
return sorted[4]
|
|
|
|
}
|
|
|
|
if sorted[2].Rank == sorted[3].Rank {
|
|
|
|
return sorted[4]
|
|
|
|
}
|
|
|
|
if sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return sorted[2]
|
|
|
|
}
|
|
|
|
panic("nope")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) pairSecondKicker() Card {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := c.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank {
|
|
|
|
// first kicker is [4]
|
|
|
|
return sorted[3]
|
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank {
|
|
|
|
// first kicker is [4]
|
|
|
|
return sorted[3]
|
|
|
|
}
|
|
|
|
if sorted[2].Rank == sorted[3].Rank {
|
|
|
|
// first kicker is [4]
|
|
|
|
return sorted[1]
|
|
|
|
}
|
|
|
|
if sorted[3].Rank == sorted[4].Rank {
|
|
|
|
// first kicker is [2]
|
|
|
|
return sorted[1]
|
|
|
|
}
|
|
|
|
panic("nope")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) pairThirdKicker() Card {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := c.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank {
|
|
|
|
// first kicker is [4]
|
|
|
|
// second kicker is [3]
|
|
|
|
return sorted[2]
|
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank {
|
|
|
|
// first kicker is [4]
|
|
|
|
// second kicker is [3]
|
|
|
|
return sorted[0]
|
|
|
|
}
|
|
|
|
if sorted[2].Rank == sorted[3].Rank {
|
|
|
|
// first kicker is [4]
|
|
|
|
// second kicker is [1]
|
|
|
|
return sorted[0]
|
|
|
|
}
|
|
|
|
if sorted[3].Rank == sorted[4].Rank {
|
|
|
|
// first kicker is [2]
|
|
|
|
// second kicker is [1]
|
|
|
|
return sorted[0]
|
|
|
|
}
|
|
|
|
panic("nope")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) twoPairBiggestPair() Rank {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := c.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
|
|
|
|
return sorted[2].Rank
|
|
|
|
}
|
|
|
|
|
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[3].Rank == sorted[4].Rank {
|
2024-05-19 04:18:16 +00:00
|
|
|
return sorted[3].Rank
|
2024-05-18 09:21:41 +00:00
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
|
2024-05-19 04:18:16 +00:00
|
|
|
return sorted[3].Rank
|
2024-05-18 09:21:41 +00:00
|
|
|
}
|
|
|
|
panic("nope")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) twoPairSmallestPair() Rank {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := c.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
|
2024-05-19 03:26:08 +00:00
|
|
|
return sorted[0].Rank
|
2024-05-18 09:21:41 +00:00
|
|
|
}
|
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[3].Rank == sorted[4].Rank {
|
2024-05-19 03:26:08 +00:00
|
|
|
return sorted[0].Rank
|
2024-05-18 09:21:41 +00:00
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
|
2024-05-19 03:26:08 +00:00
|
|
|
return sorted[1].Rank
|
2024-05-18 09:21:41 +00:00
|
|
|
}
|
|
|
|
panic("nope")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) twoPairKicker() Card {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := c.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
|
|
|
|
return sorted[4]
|
|
|
|
}
|
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return sorted[2]
|
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return sorted[0]
|
|
|
|
}
|
|
|
|
panic("nope")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) threeOfAKindTripsRank() Rank {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := c.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank {
|
|
|
|
return sorted[0].Rank
|
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
|
|
|
|
return sorted[1].Rank
|
|
|
|
}
|
|
|
|
if sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return sorted[2].Rank
|
|
|
|
}
|
|
|
|
panic("nope")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) threeOfAKindKickers() Cards {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := c.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank {
|
|
|
|
return Cards{sorted[3], sorted[4]}
|
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
|
|
|
|
return Cards{sorted[0], sorted[4]}
|
|
|
|
}
|
|
|
|
if sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return Cards{sorted[0], sorted[1]}
|
|
|
|
}
|
|
|
|
panic("nope")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) threeOfAKindFirstKicker() Card {
|
|
|
|
x := c.threeOfAKindKickers()
|
|
|
|
return x[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) threeOfAKindSecondKicker() Card {
|
|
|
|
x := c.threeOfAKindKickers()
|
|
|
|
return x[1]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) fourOfAKindRank() Rank {
|
|
|
|
if !c.containsFourOfAKind() {
|
|
|
|
panic("hand must have four of a kind to have a four of a kind rank")
|
|
|
|
}
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := c.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
|
|
|
|
return sorted[0].Rank
|
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return sorted[1].Rank
|
|
|
|
}
|
|
|
|
panic("nope")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) fourOfAKindKicker() Card {
|
|
|
|
if !c.containsFourOfAKind() {
|
|
|
|
panic("hand must have four of a kind to have a four of a kind kicker")
|
|
|
|
}
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := c.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
|
|
|
|
return sorted[4]
|
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return sorted[0]
|
|
|
|
}
|
|
|
|
panic("nope")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) fullHouseTripsRank() Rank {
|
|
|
|
if !c.containsFullHouse() {
|
|
|
|
panic("hand must have a full house to have a trips rank")
|
|
|
|
}
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := c.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return sorted[0].Rank
|
|
|
|
}
|
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return sorted[4].Rank
|
|
|
|
}
|
|
|
|
panic("nope")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) fullHousePairRank() Rank {
|
|
|
|
if !c.containsFullHouse() {
|
|
|
|
panic("hand must have a full house to have a pair rank")
|
|
|
|
}
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := c.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return sorted[4].Rank
|
|
|
|
}
|
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return sorted[0].Rank
|
|
|
|
}
|
|
|
|
panic("nope")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hand Cards) containsDuplicates() bool {
|
|
|
|
seen := make(map[Card]bool)
|
|
|
|
for _, card := range hand {
|
|
|
|
if _, ok := seen[card]; ok {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
seen[card] = true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hand Cards) containsFlush() bool {
|
|
|
|
suit := hand[0].Suit
|
|
|
|
for i := 1; i < len(hand); i++ {
|
|
|
|
if hand[i].Suit != suit {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hand Cards) containsStraight() bool {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := hand.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
|
|
|
|
if sorted[4].Rank == ACE && sorted[3].Rank == FIVE {
|
|
|
|
// special case for A-5 straight
|
|
|
|
if sorted[0].Rank == DEUCE && sorted[1].Rank == THREE && sorted[2].Rank == FOUR {
|
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sorted[0].Rank.Int()+1 == sorted[1].Rank.Int() &&
|
|
|
|
sorted[1].Rank.Int()+1 == sorted[2].Rank.Int() &&
|
|
|
|
sorted[2].Rank.Int()+1 == sorted[3].Rank.Int() &&
|
|
|
|
sorted[3].Rank.Int()+1 == sorted[4].Rank.Int()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hand Cards) containsStraightFlush() bool {
|
2024-05-19 05:31:55 +00:00
|
|
|
// this of course only works on five card hands
|
|
|
|
// but these hand helpers are only ever called from a five card hand
|
2024-05-18 09:21:41 +00:00
|
|
|
if hand.containsStraight() && hand.containsFlush() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hand Cards) containsRoyalFlush() bool {
|
2024-05-19 00:33:15 +00:00
|
|
|
// This seems like it works, but a five-high straight flush is not a royal flush
|
|
|
|
// and the highest ranked card in five-high straigh flush is an ace.
|
|
|
|
//if hand.containsStraightFlush() && hand.HighestRank() == ACE {
|
|
|
|
// return true
|
|
|
|
//}
|
|
|
|
sorted := hand.SortByRankAscending()
|
|
|
|
if hand.containsStraightFlush() && hand.HighestRank() == ACE && sorted[0].Rank == TEN {
|
2024-05-18 09:21:41 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hand Cards) containsFourOfAKind() bool {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := hand.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
|
|
|
|
// the quads precede the kicker
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
// the kicker is the first card
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hand Cards) containsFullHouse() bool {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := hand.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
|
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
// the trips precede the pair
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
// the pair is first
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hand Cards) containsPair() bool {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := hand.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if sorted[2].Rank == sorted[3].Rank {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hand Cards) containsThreeOfAKind() bool {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := hand.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hand Cards) containsTwoPair() bool {
|
2024-05-19 00:33:15 +00:00
|
|
|
sorted := hand.SortByRankAscending()
|
2024-05-18 09:21:41 +00:00
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if sorted[0].Rank == sorted[1].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hand Cards) isUnmadeHand() bool {
|
2024-05-19 05:31:55 +00:00
|
|
|
// i suspect this is expensive but we use it only in tests
|
2024-05-18 09:21:41 +00:00
|
|
|
return !hand.containsPair() && !hand.containsTwoPair() && !hand.containsThreeOfAKind() && !hand.containsStraight() && !hand.containsFlush() && !hand.containsFullHouse() && !hand.containsFourOfAKind() && !hand.containsStraightFlush() && !hand.containsRoyalFlush()
|
|
|
|
}
|