2024-05-19 00:33:15 +00:00
|
|
|
package pokercore
|
|
|
|
|
2024-05-19 03:26:08 +00:00
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
)
|
2024-05-19 00:33:15 +00:00
|
|
|
|
|
|
|
type HandScore int
|
|
|
|
|
|
|
|
const (
|
|
|
|
ScoreHighCard = HandScore(iota * 100_000_000_000)
|
|
|
|
ScorePair
|
|
|
|
ScoreTwoPair
|
|
|
|
ScoreThreeOfAKind
|
|
|
|
ScoreStraight
|
|
|
|
ScoreFlush
|
|
|
|
ScoreFullHouse
|
|
|
|
ScoreFourOfAKind
|
|
|
|
ScoreStraightFlush
|
|
|
|
ScoreRoyalFlush
|
|
|
|
)
|
|
|
|
|
|
|
|
func (c Card) Score() HandScore {
|
|
|
|
return HandScore(c.Rank.Score())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Cards) PokerHandScore() (HandScore, error) {
|
|
|
|
ph, err := c.PokerHand()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return ph.Score, nil
|
|
|
|
}
|
2024-05-19 05:10:53 +00:00
|
|
|
|
2024-05-19 00:33:15 +00:00
|
|
|
func (x HandScore) String() string {
|
|
|
|
return fmt.Sprintf("<HandScore %d>", x)
|
|
|
|
}
|
2024-05-19 05:23:14 +00:00
|
|
|
|
|
|
|
func (ph *PokerHand) calculateScore() {
|
|
|
|
|
|
|
|
// sanity check, we should only be called in the PokerHand() method from
|
|
|
|
// a Cards, but just in case
|
|
|
|
if len(ph.Hand) != 5 {
|
|
|
|
// normally we don't panic in a library but this is a "should never
|
|
|
|
// happen"
|
|
|
|
panic("PokerHand.calculateScore() called on a PokerHand with != 5 cards")
|
|
|
|
}
|
|
|
|
|
|
|
|
if ph.Hand.containsRoyalFlush() {
|
|
|
|
ph.Type = RoyalFlush
|
|
|
|
ph.Score = ScoreRoyalFlush
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if ph.Hand.containsStraightFlush() {
|
|
|
|
ph.Type = StraightFlush
|
|
|
|
ph.Score = ScoreStraightFlush
|
|
|
|
ph.Score += 1000 * ph.Hand.HighestRank().Score()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if ph.Hand.containsFourOfAKind() {
|
|
|
|
ph.Type = FourOfAKind
|
|
|
|
ph.Score = ScoreFourOfAKind
|
|
|
|
ph.Score += 1000 * ph.Hand.fourOfAKindRank().Score()
|
|
|
|
ph.Score += 100 * ph.Hand.fourOfAKindKicker().Score()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if ph.Hand.containsFullHouse() {
|
|
|
|
ph.Type = FullHouse
|
|
|
|
ph.Score = ScoreFullHouse
|
|
|
|
// FIXME write a good test for this
|
|
|
|
ph.Score += 1000 * ph.Hand.fullHouseTripsRank().Score()
|
|
|
|
ph.Score += 100 * ph.Hand.fullHousePairRank().Score()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if ph.Hand.containsFlush() {
|
|
|
|
ph.Type = Flush
|
|
|
|
ph.Score = ScoreFlush
|
|
|
|
// flush base score plus sum of card ranks
|
|
|
|
ph.Score += ph.Hand[0].Score()
|
|
|
|
ph.Score += ph.Hand[1].Score()
|
|
|
|
ph.Score += ph.Hand[2].Score()
|
|
|
|
ph.Score += ph.Hand[3].Score()
|
|
|
|
ph.Score += ph.Hand[4].Score()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if ph.Hand.containsStraight() {
|
|
|
|
ph.Type = Straight
|
|
|
|
ph.Score = ScoreStraight
|
|
|
|
|
|
|
|
// note that ph.Hand is already sorted by rank ascending with ace
|
|
|
|
// high
|
|
|
|
|
|
|
|
// Straights are scored by the highest card in the straight
|
|
|
|
// UNLESS the second highest card is a 5 and the highest card is an Ace
|
|
|
|
// In that case, the straight is a 5-high straight, not an Ace-high straight
|
|
|
|
if ph.Hand[3].Rank == FIVE && ph.Hand[4].Rank == ACE {
|
|
|
|
// 5-high straight, scored by the five's rank
|
|
|
|
ph.Score += ph.Hand[3].Score()
|
|
|
|
} else {
|
|
|
|
// All other straights are scored by the highest card in the straight
|
|
|
|
ph.Score += ph.Hand[4].Score()
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if ph.Hand.containsThreeOfAKind() {
|
|
|
|
ph.Type = ThreeOfAKind
|
|
|
|
ph.Score = ScoreThreeOfAKind
|
|
|
|
ph.Score += 1000 * ph.Hand.threeOfAKindTripsRank().Score()
|
|
|
|
ph.Score += 100 * ph.Hand.threeOfAKindFirstKicker().Score()
|
|
|
|
ph.Score += 10 * ph.Hand.threeOfAKindSecondKicker().Score()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if ph.Hand.containsTwoPair() {
|
|
|
|
ph.Type = TwoPair
|
|
|
|
ph.Score = ScoreTwoPair
|
|
|
|
ph.Score += 1000 * ph.Hand.twoPairBiggestPair().Score()
|
|
|
|
ph.Score += 100 * ph.Hand.twoPairSmallestPair().Score()
|
|
|
|
ph.Score += 10 * ph.Hand.twoPairKicker().Score()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if ph.Hand.containsPair() {
|
|
|
|
ph.Type = Pair
|
|
|
|
ph.Score = ScorePair
|
|
|
|
ph.Score += 1000 * ph.Hand.pairRank().Score()
|
|
|
|
ph.Score += 100 * ph.Hand.pairFirstKicker().Score()
|
|
|
|
ph.Score += 10 * ph.Hand.pairSecondKicker().Score()
|
|
|
|
ph.Score += ph.Hand.pairThirdKicker().Score()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ph.Type = HighCard
|
|
|
|
ph.Score = ScoreHighCard // base score
|
|
|
|
// unmade hands are scored like flushes, just add up the values
|
|
|
|
ph.Score += ph.Hand[0].Score()
|
|
|
|
ph.Score += ph.Hand[1].Score()
|
|
|
|
ph.Score += ph.Hand[2].Score()
|
|
|
|
ph.Score += ph.Hand[3].Score()
|
|
|
|
ph.Score += ph.Hand[4].Score()
|
|
|
|
return
|
|
|
|
}
|