264 lines
6.3 KiB
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
|
|
}
|