pokercore/pokercore/scoring.go

264 lines
6.3 KiB
Go

package pokercore
import "fmt"
type HandScore int
const (
ScoreNoPair = HandScore(iota * 100_000_000_000)
ScorePair
ScoreTwoPair
ScoreThreeOfAKind
ScoreStraight
ScoreFlush
ScoreFullHouse
ScoreFourOfAKind
ScoreStraightFlush
)
type AcesHighOrLow int
const (
AcesHigh AcesHighOrLow = iota
AcesLow
)
func rankToScore(rank Rank, AcesHighOrLow AcesHighOrLow) HandScore {
switch rank {
case ACE:
if AcesHighOrLow == AcesHigh {
return 14 // Aces are high, so we give them the highest value
} else {
return 1
}
case DEUCE:
return 2
case THREE:
return 3
case FOUR:
return 4
case FIVE:
return 5
case SIX:
return 6
case SEVEN:
return 7
case EIGHT:
return 8
case NINE:
return 9
case TEN:
return 10
case JACK:
return 11
case QUEEN:
return 12
case KING:
return 13
default:
panic("nope")
}
}
func (c Cards) ScoreHand() (HandScore, error) {
if !c.IsFiveCardPokerHand() {
return 0, fmt.Errorf("hand must have 5 cards with no duplicates to be scored")
}
if c.containsRoyalFlush() {
return ScoreStraightFlush + 1000*ACE.HandScore(AcesHigh), nil
}
if c.containsStraightFlush() {
return ScoreStraightFlush + 1000*c.HighestRank(AcesHigh).HandScore(AcesHigh), nil
}
panic("not implemented")
// FIXME finish this
return 0, nil
}
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) IsFiveCardPokerHand() bool {
return len(hand) == 5 && !hand.containsDuplicates()
}
func (hand Cards) containsFlush() bool {
if !hand.IsFiveCardPokerHand() {
panic("hand must have 5 cards to be scored")
}
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 {
if !hand.IsFiveCardPokerHand() {
panic("hand must have 5 cards to be scored")
}
sorted := hand.SortByRank(AcesHigh)
if sorted[0].Rank == ACE && sorted[1].Rank == FIVE {
// special case for A-5 straight
if sorted[1].Rank == FIVE && sorted[2].Rank == FOUR && sorted[3].Rank == THREE && sorted[4].Rank == DEUCE {
return true
}
}
return sorted[0].Rank.Int(AcesHigh) == sorted[1].Rank.Int(AcesHigh)+1 && sorted[1].Rank.Int(AcesHigh) == sorted[2].Rank.Int(AcesHigh)+1 && sorted[2].Rank.Int(AcesHigh) == sorted[3].Rank.Int(AcesHigh)+1 && sorted[3].Rank.Int(AcesHigh) == sorted[4].Rank.Int(AcesHigh)+1
}
func (hand Cards) containsStraightFlush() bool {
if !hand.IsFiveCardPokerHand() {
panic("hand must have 5 cards to be scored")
}
if hand.containsStraight() && hand.containsFlush() {
return true
}
return false
}
func (hand Cards) containsRoyalFlush() bool {
if !hand.IsFiveCardPokerHand() {
panic("hand must have 5 cards to be scored")
}
sorted := hand.SortByRank(AcesHigh)
if hand.containsStraightFlush() && sorted[0].Rank == ACE {
return true
}
return false
}
func (hand Cards) containsFourOfAKind() bool {
if !hand.IsFiveCardPokerHand() {
panic("hand must have 5 cards to be scored")
}
sorted := hand.SortByRank(AcesHigh)
if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
return true
}
if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
return true
}
return false
}
func (hand Cards) containsFullHouse() bool {
if !hand.IsFiveCardPokerHand() {
panic("hand must have 5 cards to be scored")
}
sorted := hand.SortByRank(AcesHigh)
if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
return true
}
if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
return true
}
return false
}
func (hand Cards) containsPair() bool {
if !hand.IsFiveCardPokerHand() {
panic("hand must have 5 cards to be scored")
}
sorted := hand.SortByRank(AcesHigh)
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 {
if !hand.IsFiveCardPokerHand() {
panic("hand must have 5 cards to be scored")
}
sorted := hand.SortByRank(AcesHigh)
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 {
if !hand.IsFiveCardPokerHand() {
panic("hand must have 5 cards to be scored")
}
sorted := hand.SortByRank(AcesHigh)
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 {
if !hand.IsFiveCardPokerHand() {
panic("hand must have 5 cards to be scored")
}
return !hand.containsPair() && !hand.containsTwoPair() && !hand.containsThreeOfAKind() && !hand.containsStraight() && !hand.containsFlush() && !hand.containsFullHouse() && !hand.containsFourOfAKind() && !hand.containsStraightFlush() && !hand.containsRoyalFlush()
}
// this method makes a n new hands where n is the number of cards in the hand
// each of the new hands has one card removed from the original hand
// then it calls the identifyBestFiveCardPokerHand method on each of the new hands
// and returns the best hand by score. this is recursion.
func (hand Cards) identifyBestFiveCardPokerHand() (Cards, error) {
newHands := make([]Cards, len(hand))
for i := 0; i < len(hand); i++ {
newHand := hand[:i]
newHand = append(newHand, hand[i+1:]...)
newHands[i] = newHand
}
var bestHand Cards
var bestScore HandScore
for _, h := range newHands {
if h.IsFiveCardPokerHand() {
score, _ := h.ScoreHand()
if score > bestScore {
bestScore = score
bestHand = h
}
} else {
rh, _ := h.identifyBestFiveCardPokerHand()
score, _ := rh.ScoreHand()
if score > bestScore {
bestScore = score
bestHand = rh
}
}
}
return bestHand, nil
}