424 lines
15 KiB
Go
424 lines
15 KiB
Go
package pokercore
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestHandDescriptionBug(t *testing.T) {
|
|
t.Parallel()
|
|
playerCount := 8
|
|
d := NewDeck()
|
|
d.ShuffleDeterministically(1337)
|
|
players := make([]*Cards, playerCount)
|
|
for i := 0; i < playerCount; i++ {
|
|
c := d.Deal(2)
|
|
players[i] = &c
|
|
t.Logf("Player %d dealt: %+v\n", i+1, c)
|
|
}
|
|
t.Logf("Players: %+v\n", players)
|
|
|
|
community := d.Deal(5)
|
|
t.Logf("Community: %+v\n", community)
|
|
|
|
var playerResults []*PokerHand
|
|
|
|
for i := 0; i < playerCount; i++ {
|
|
t.Logf("Player %d hole cards: %+v\n", i+1, *players[i])
|
|
pc := append(*players[i], community...)
|
|
t.Logf("Player %d cards available: %+v\n", i+1, pc)
|
|
hand, err := pc.IdentifyBestFiveCardPokerHand()
|
|
assert.NoError(t, err, "Expected no error")
|
|
ph, err := hand.PokerHand()
|
|
assert.NoError(t, err, "Expected no error")
|
|
t.Logf("Player %d five cards used: %+v\n", i+1, hand)
|
|
t.Logf("Player %d poker hand: %+v\n", i+1, ph)
|
|
t.Logf("Player %d best hand description: %s\n", i+1, ph.Description())
|
|
playerResults = append(playerResults, ph)
|
|
}
|
|
|
|
weirdOne := playerResults[7]
|
|
|
|
t.Logf("Weird one: %v\n", weirdOne)
|
|
t.Logf("Weird one description: %s\n", weirdOne.Description())
|
|
|
|
// T♠,7♠,9♦,7♣,T♥
|
|
assert.Equal(t, "two pair, tens and sevens with a nine", weirdOne.Description())
|
|
|
|
scoreShouldBe := ScoreTwoPair
|
|
scoreShouldBe += 10000 * TEN.Score()
|
|
scoreShouldBe += 100 * SEVEN.Score()
|
|
scoreShouldBe += NINE.Score()
|
|
assert.Equal(t, scoreShouldBe, weirdOne.Score)
|
|
|
|
cards := weirdOne.Hand
|
|
|
|
assert.True(t, cards.containsTwoPair(), "Expected hand to be two pair")
|
|
|
|
bp := cards.twoPairBiggestPair() // returns Rank, because describing a pair
|
|
assert.Equal(t, TEN, bp, "Expected biggest pair to be a ten")
|
|
|
|
sp := cards.twoPairSmallestPair() // returns Rank, because describing a pair
|
|
assert.Equal(t, SEVEN, sp, "Expected smallest pair to be a seven")
|
|
|
|
k := cards.twoPairKicker() // returns Card, because describing a single card
|
|
assert.Equal(t, NINE, k.Rank, "Expected kicker to be a nine")
|
|
}
|
|
|
|
func TestAceLowStraight(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Test Ace-Low Straight", func(t *testing.T) {
|
|
hand := Cards{
|
|
AceOfSpades(),
|
|
DeuceOfHearts(),
|
|
ThreeOfDiamonds(),
|
|
FourOfClubs(),
|
|
FiveOfSpades(),
|
|
}
|
|
assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
|
|
ph, err := hand.PokerHand()
|
|
assert.NoError(t, err, "Expected no error")
|
|
assert.Greater(t, ph.Score, 0, "Expected score to be greater than 0")
|
|
assert.Less(t, ph.Score, 100000000000000000, "Expected score to be less than 100000000000000000")
|
|
assert.Equal(t, ph.Score, ScoreStraight+FIVE.Score())
|
|
assert.Equal(t, "a five high straight", ph.Description())
|
|
assert.Equal(t, ACE, hand.HighestRank(), "Expected highest rank to be an ace")
|
|
s := hand.SortByRankAscending()
|
|
assert.Equal(t, DEUCE, s.First().Rank, "Expected first card to be a deuce")
|
|
assert.Equal(t, ACE, s.Last().Rank, "Expected last card in sorted to be an ace")
|
|
assert.Equal(t, THREE, s.Second().Rank, "Expected second card to be a three")
|
|
assert.Equal(t, FOUR, s.Third().Rank, "Expected third card to be a four")
|
|
assert.Equal(t, FIVE, s.Fourth().Rank, "Expected fourth card to be a five")
|
|
assert.Equal(t, ACE, s.Fifth().Rank, "Expected fifth card to be an ace")
|
|
})
|
|
}
|
|
|
|
func TestAceHighStraight(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Test Ace-High Straight", func(t *testing.T) {
|
|
hand := Cards{
|
|
TenOfSpades(),
|
|
JackOfHearts(),
|
|
KingOfClubs(),
|
|
AceOfSpades(),
|
|
QueenOfDiamonds(),
|
|
}
|
|
|
|
assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
|
|
newDeck := NewDeckFromCards(hand)
|
|
newDeck.ShuffleDeterministically(123456789)
|
|
shuffledHand := newDeck.Deal(5)
|
|
assert.True(t, shuffledHand.containsStraight(), "Expected hand to still be a straight after shuffle")
|
|
assert.Equal(t, ACE, shuffledHand.HighestRank(), "Expected highest rank to be an ace")
|
|
sortedHand := shuffledHand.SortByRankAscending()
|
|
assert.Equal(t, TEN, sortedHand[0].Rank, "Expected lowest rank to be a ten")
|
|
assert.Equal(t, JACK, sortedHand[1].Rank, "Expected second lowest rank to be a jack")
|
|
assert.Equal(t, QUEEN, sortedHand[2].Rank, "Expected third lowest rank to be a queen")
|
|
assert.Equal(t, KING, sortedHand[3].Rank, "Expected fourth lowest rank to be a king")
|
|
assert.Equal(t, ACE, sortedHand[4].Rank, "Expected highest rank to be an ace")
|
|
})
|
|
}
|
|
|
|
func TestOtherStraight(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Test Other Straight", func(t *testing.T) {
|
|
hand := Cards{
|
|
DeuceOfSpades(),
|
|
ThreeOfHearts(),
|
|
FourOfDiamonds(),
|
|
FiveOfClubs(),
|
|
SixOfSpades(),
|
|
}
|
|
assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
|
|
|
|
newDeck := NewDeckFromCards(hand)
|
|
newDeck.ShuffleDeterministically(123456789)
|
|
shuffledHand := newDeck.Deal(5)
|
|
assert.True(t, shuffledHand.containsStraight(), "Expected hand to still be a straight after shuffle")
|
|
assert.False(t, shuffledHand.containsTwoPair(), "Expected hand to not be two pair")
|
|
assert.False(t, shuffledHand.containsPair(), "Expected hand to not be a pair")
|
|
assert.Equal(t, SIX, shuffledHand.HighestRank(), "Expected highest rank to be a six")
|
|
assert.Equal(t, DEUCE, shuffledHand.SortByRankAscending().First().Rank, "Expected first card to be a deuce")
|
|
})
|
|
}
|
|
|
|
func TestFlush(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Test Flush", func(t *testing.T) {
|
|
hand := Cards{
|
|
AceOfSpades(),
|
|
DeuceOfSpades(),
|
|
ThreeOfSpades(),
|
|
FourOfSpades(),
|
|
SixOfSpades(),
|
|
}
|
|
assert.True(t, hand.containsFlush(), "Expected hand to be a flush")
|
|
newDeck := NewDeckFromCards(hand)
|
|
newDeck.ShuffleDeterministically(123456789)
|
|
shuffledHand := newDeck.Deal(5)
|
|
assert.True(t, shuffledHand.containsFlush(), "Expected hand to still be a flush after shuffle")
|
|
|
|
// flush value is the sum of the ranks, just like high card
|
|
x := ScoreFlush
|
|
x += ACE.Score()
|
|
x += DEUCE.Score()
|
|
x += THREE.Score()
|
|
x += FOUR.Score()
|
|
x += SIX.Score()
|
|
|
|
ph, err := shuffledHand.PokerHand()
|
|
assert.NoError(t, err, "Expected no error")
|
|
assert.Greater(t, ph.Score, 0, "Expected score to be nonzero")
|
|
assert.Equal(t, ph.Score, x)
|
|
})
|
|
}
|
|
|
|
func TestStraightFlush(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Test Straight Flush", func(t *testing.T) {
|
|
hand := Cards{
|
|
SixOfSpades(),
|
|
DeuceOfSpades(),
|
|
ThreeOfSpades(),
|
|
FourOfSpades(),
|
|
FiveOfSpades(),
|
|
}
|
|
assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
|
|
assert.True(t, hand.containsFlush(), "Expected hand to be a flush")
|
|
assert.True(t, hand.containsStraightFlush(), "Expected hand to be a straight flush")
|
|
assert.False(t, hand.containsRoyalFlush(), "Expected hand to not be a royal flush")
|
|
assert.False(t, hand.containsFourOfAKind(), "Expected hand to not be four of a kind")
|
|
assert.False(t, hand.containsFullHouse(), "Expected hand to not be a full house")
|
|
assert.False(t, hand.containsThreeOfAKind(), "Expected hand to not be three of a kind")
|
|
assert.False(t, hand.containsTwoPair(), "Expected hand to not be two pair")
|
|
assert.False(t, hand.containsPair(), "Expected hand to not be a pair")
|
|
|
|
assert.Equal(t, SIX, hand.HighestRank(), "Expected highest rank to be a six")
|
|
|
|
nd := NewDeckFromCards(hand)
|
|
nd.ShuffleDeterministically(123456789)
|
|
shuffledHand := nd.Deal(5)
|
|
assert.True(t, shuffledHand.containsStraightFlush(), "Expected hand to still be a straight flush after shuffle")
|
|
assert.Equal(t, SIX, shuffledHand.HighestRank(), "Expected highest rank to still be a six after shuffle")
|
|
assert.Equal(t, SIX, shuffledHand.HighestRank(), "Expected highest rank to be a six after shuffle even with aces low")
|
|
})
|
|
}
|
|
|
|
func TestFourOfAKind(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Test Four of a Kind", func(t *testing.T) {
|
|
hand := Cards{
|
|
SixOfSpades(),
|
|
SixOfHearts(),
|
|
SixOfDiamonds(),
|
|
SixOfClubs(),
|
|
FiveOfSpades(),
|
|
}
|
|
assert.False(t, hand.containsStraight(), "Expected hand to not be a straight")
|
|
assert.False(t, hand.containsFlush(), "Expected hand to not be a flush")
|
|
assert.False(t, hand.containsStraightFlush(), "Expected hand to not be a straight flush")
|
|
assert.False(t, hand.containsRoyalFlush(), "Expected hand to not be a royal flush")
|
|
assert.True(t, hand.containsFourOfAKind(), "Expected hand to be four of a kind")
|
|
assert.False(t, hand.containsFullHouse(), "Expected hand to not be a full house")
|
|
|
|
// note that these are *expected* to be true. the contains* functions
|
|
// are used in the PokerHand.calculateScore method to determine the best hand
|
|
// and are checked in sequence of descending value, so if a hand is four of a kind
|
|
// it will not be checked for full house, three of a kind, etc.
|
|
|
|
// technically quads *is* two pair also, and a hand with quads does
|
|
// indeed contain three of a kind, and contains a pair.
|
|
assert.True(t, hand.containsThreeOfAKind(), "Expected hand to contain three of a kind")
|
|
assert.True(t, hand.containsTwoPair(), "Expected hand to contain two pair")
|
|
assert.True(t, hand.containsPair(), "Expected hand to contain a pair")
|
|
|
|
assert.Equal(t, SIX, hand.HighestRank(), "Expected highest rank to be a six")
|
|
})
|
|
}
|
|
|
|
func TestRoyalFlush(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Test Royal Flush", func(t *testing.T) {
|
|
hand := Cards{
|
|
TenOfSpades(),
|
|
JackOfSpades(),
|
|
QueenOfSpades(),
|
|
KingOfSpades(),
|
|
AceOfSpades(),
|
|
}
|
|
assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
|
|
assert.True(t, hand.containsFlush(), "Expected hand to be a flush")
|
|
assert.True(t, hand.containsStraightFlush(), "Expected hand to be a straight flush")
|
|
assert.True(t, hand.containsRoyalFlush(), "Expected hand to be a royal flush")
|
|
|
|
assert.False(t, hand.containsFourOfAKind(), "Expected hand to not be four of a kind")
|
|
assert.False(t, hand.containsFullHouse(), "Expected hand to not be a full house")
|
|
assert.False(t, hand.containsThreeOfAKind(), "Expected hand to not be three of a kind")
|
|
assert.False(t, hand.containsTwoPair(), "Expected hand to not be two pair")
|
|
assert.False(t, hand.containsPair(), "Expected hand to not be a pair")
|
|
|
|
assert.Equal(t, ACE, hand.HighestRank(), "Expected highest rank to be an ace")
|
|
assert.NotEqual(t, TEN, hand.HighestRank(), "Expected highest rank to not be a ten")
|
|
})
|
|
}
|
|
|
|
func TestUnmadeHand(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Test Unmade Hand", func(t *testing.T) {
|
|
hand := Cards{
|
|
TenOfSpades(),
|
|
JackOfDiamonds(),
|
|
QueenOfSpades(),
|
|
KingOfSpades(),
|
|
DeuceOfSpades(),
|
|
}
|
|
assert.False(t, hand.containsStraight(), "Expected hand to not be a straight")
|
|
assert.False(t, hand.containsFlush(), "Expected hand to not be a flush")
|
|
assert.False(t, hand.containsStraightFlush(), "Expected hand to not be a straight flush")
|
|
assert.False(t, hand.containsRoyalFlush(), "Expected hand to not be a royal flush")
|
|
assert.False(t, hand.containsFourOfAKind(), "Expected hand to not be four of a kind")
|
|
assert.False(t, hand.containsFullHouse(), "Expected hand to not be a full house")
|
|
assert.False(t, hand.containsThreeOfAKind(), "Expected hand to not be three of a kind")
|
|
assert.False(t, hand.containsTwoPair(), "Expected hand to not be two pair")
|
|
assert.False(t, hand.containsPair(), "Expected hand to not be a pair")
|
|
assert.Equal(t, KING, hand.HighestRank(), "Expected highest rank to be a king")
|
|
assert.True(t, hand.isUnmadeHand(), "Expected hand to be unmade")
|
|
})
|
|
}
|
|
|
|
func TestTwoPair(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Test Two Pair", func(t *testing.T) {
|
|
hand := Cards{
|
|
KingOfSpades(),
|
|
JackOfDiamonds(),
|
|
JackOfSpades(),
|
|
KingOfDiamonds(),
|
|
TenOfSpades(),
|
|
}
|
|
assert.False(t, hand.containsStraight(), "Expected hand to not be a straight")
|
|
assert.False(t, hand.containsFlush(), "Expected hand to not be a flush")
|
|
assert.False(t, hand.containsStraightFlush(), "Expected hand to not be a straight flush")
|
|
assert.False(t, hand.containsRoyalFlush(), "Expected hand to not be a royal flush")
|
|
assert.False(t, hand.containsFourOfAKind(), "Expected hand to not be four of a kind")
|
|
assert.False(t, hand.containsFullHouse(), "Expected hand to not be a full house")
|
|
assert.False(t, hand.containsThreeOfAKind(), "Expected hand to not be three of a kind")
|
|
assert.True(t, hand.containsTwoPair(), "Expected hand to be two pair")
|
|
assert.True(t, hand.containsPair(), "Expected hand to also be a pair")
|
|
assert.Equal(t, KING, hand.HighestRank(), "Expected highest rank to be a king")
|
|
assert.False(t, hand.isUnmadeHand(), "Expected hand to not be unmade")
|
|
})
|
|
}
|
|
|
|
func TestDetectDuplicates(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Test Detect Duplicates", func(t *testing.T) {
|
|
hand := Cards{
|
|
KingOfSpades(),
|
|
JackOfDiamonds(),
|
|
JackOfSpades(),
|
|
KingOfSpades(),
|
|
TenOfSpades(),
|
|
}
|
|
assert.True(t, hand.containsDuplicates(), "Expected hand to contain duplicates")
|
|
})
|
|
}
|
|
|
|
func TestHandScore(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Test Hand Score", func(t *testing.T) {
|
|
hand := Cards{
|
|
KingOfSpades(),
|
|
JackOfDiamonds(),
|
|
JackOfSpades(),
|
|
KingOfDiamonds(),
|
|
TenOfSpades(),
|
|
}
|
|
|
|
ph, err := hand.PokerHand()
|
|
assert.NoError(t, err, "Expected no error")
|
|
assert.True(t, ph.Score > 0, "Expected score to be nonzero")
|
|
assert.True(t, ph.Score < 100000000000000000, "Expected score to be less than 100000000000000000")
|
|
// write more assertions FIXME
|
|
})
|
|
}
|
|
|
|
func TestTwoPairBug(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Test Two Pair Bug", func(t *testing.T) {
|
|
// this is an actual bug in the first implementation
|
|
c, err := NewCardsFromString("9♠,9♣,Q♥,Q♦,K♣")
|
|
assert.NoError(t, err, "Expected no error")
|
|
|
|
ph, err := c.PokerHand()
|
|
assert.NoError(t, err, "Expected no error")
|
|
assert.Greater(t, ph.Score, 0, "Expected score to be nonzero")
|
|
desc := ph.Description()
|
|
assert.Equal(t, "two pair, queens and nines with a king", desc)
|
|
})
|
|
}
|
|
|
|
func TestScoringStructureQuads(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Test Scoring Structure Quads", func(t *testing.T) {
|
|
// this test case was for a bug, but is now fixed
|
|
handA := Cards{
|
|
DeuceOfSpades(),
|
|
DeuceOfHearts(),
|
|
DeuceOfDiamonds(),
|
|
DeuceOfClubs(),
|
|
AceOfSpades(),
|
|
}
|
|
|
|
handB := Cards{
|
|
ThreeOfSpades(),
|
|
ThreeOfHearts(),
|
|
ThreeOfDiamonds(),
|
|
ThreeOfClubs(),
|
|
DeuceOfSpades(),
|
|
}
|
|
|
|
phA, err := handA.PokerHand()
|
|
assert.NoError(t, err, "Expected no error")
|
|
phB, err := handB.PokerHand()
|
|
assert.NoError(t, err, "Expected no error")
|
|
assert.Greater(t, phB.Score, phA.Score, "Expected hand B to be higher than hand A")
|
|
})
|
|
}
|
|
|
|
func TestScoringStructureFullHouse(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Test Scoring Structure Full House", func(t *testing.T) {
|
|
// this test case documents an actual bug i found in my scoring code
|
|
// related to the fact that i was multiplying by 100 then by 1000,
|
|
// instead of by 100 then by 10000 in the scoring. because the ranks
|
|
// are 2-14, the different levels of kickers (or in the case of a full
|
|
// house, the pair) were not distinguishing sufficiently.
|
|
handA := Cards{
|
|
DeuceOfSpades(),
|
|
DeuceOfHearts(),
|
|
DeuceOfDiamonds(),
|
|
AceOfSpades(),
|
|
AceOfHearts(),
|
|
}
|
|
|
|
phA, err := handA.PokerHand()
|
|
assert.NoError(t, err, "Expected no error")
|
|
deucesFullOfAcesScore := phA.Score
|
|
|
|
handB := Cards{
|
|
ThreeOfSpades(),
|
|
ThreeOfHearts(),
|
|
ThreeOfDiamonds(),
|
|
DeuceOfSpades(),
|
|
DeuceOfHearts(),
|
|
}
|
|
phB, err := handB.PokerHand()
|
|
assert.NoError(t, err, "Expected no error")
|
|
threesFullOfDeucesScore := phB.Score
|
|
|
|
assert.Greater(t, threesFullOfDeucesScore, deucesFullOfAcesScore, "Expected Threes full of deuces to beat deuces full of aces")
|
|
})
|
|
}
|