diff --git a/examples/montecarlo/main.go b/examples/montecarlo/main.go index a83f37b..d438012 100644 --- a/examples/montecarlo/main.go +++ b/examples/montecarlo/main.go @@ -4,9 +4,9 @@ import ( "fmt" "time" - "git.eeqj.de/sneak/pokercore" "github.com/rcrowley/go-metrics" "github.com/schollz/progressbar/v3" + "sneak.berlin/go/pokercore" ) var gameCount = 50_000 @@ -101,13 +101,16 @@ func main() { timer := metrics.NewTimer() registry.Register("pokerGame", timer) - bar := progressbar.NewOptions(gameCount, + bar := progressbar.NewOptions( + gameCount, progressbar.OptionSetDescription("Gambling..."), progressbar.OptionShowCount(), progressbar.OptionShowIts(), progressbar.OptionSetPredictTime(true), progressbar.OptionClearOnFinish(), - progressbar.OptionThrottle(100*time.Millisecond), // Update every 100ms + progressbar.OptionThrottle( + 100*time.Millisecond, + ), // Update every 100ms ) for i := 0; i < gameCount; i++ { @@ -128,11 +131,13 @@ func main() { fmt.Printf("Max: %d ns\n", timer.Max()) fmt.Printf("Mean: %0.2f ns\n", timer.Mean()) fmt.Printf("StdDev: %0.2f ns\n", timer.StdDev()) - fmt.Printf("Percentiles: 50%%: %0.2f ns, 75%%: %0.2f ns, 95%%: %0.2f ns, 99%%: %0.2f ns\n", + fmt.Printf( + "Percentiles: 50%%: %0.2f ns, 75%%: %0.2f ns, 95%%: %0.2f ns, 99%%: %0.2f ns\n", timer.Percentile(0.50), timer.Percentile(0.75), timer.Percentile(0.95), - timer.Percentile(0.99)) + timer.Percentile(0.99), + ) oneWinPercentage := float64(oneWins) / float64(gameCount) * 100 twoWinPercentage := float64(twoWins) / float64(gameCount) * 100 fmt.Printf("Player 1 won: %d (%0.2f%%)\n", oneWins, oneWinPercentage) diff --git a/examples/sf/main.go b/examples/sf/main.go new file mode 100644 index 0000000..58589c4 --- /dev/null +++ b/examples/sf/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + + "sneak.berlin/go/pokercore" +) + +func main() { + var sfp bool + var tries int + var found int + + const maxTries = 100_000_000 + + for i := 0; i < maxTries; i++ { + sfp = searchStraightFlush() + if sfp { + found++ + } + tries++ + if tries%1000 == 0 { + fmt.Printf("Tries: %d, Found: %d\n", tries, found) + } + } + fmt.Printf("Tries: %d, Found: %d\n", tries, found) +} + +func searchStraightFlush() bool { + + d := pokercore.NewDeck() + + d.ShuffleRandomly() + + var hand pokercore.Cards + + hand = d.Deal(7) + + ph, err := hand.PokerHand() + + if err != nil { + fmt.Println("Error: ", err) + return false + } + + if ph.Type == pokercore.StraightFlush || + ph.Type == pokercore.RoyalFlush { + fmt.Println("straight flush found") + fmt.Println("Hand: ", hand.FormatForTerminal()) + fmt.Println("PokerHand: ", ph) + return true + } + + return false + +} diff --git a/go.mod b/go.mod index 48fd901..5464692 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,14 @@ -module git.eeqj.de/sneak/pokercore +module sneak.berlin/go/pokercore go 1.22.2 require ( - git.eeqj.de/sneak/timingbench v0.0.0-20240519025145-fb13c5c56a02 github.com/logrusorgru/aurora/v4 v4.0.0 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 github.com/schollz/progressbar/v3 v3.14.2 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.0 + sneak.berlin/go/timingbench v0.0.0-20240522212031-a6243a470213 ) require ( diff --git a/go.sum b/go.sum index a556f4a..791fb76 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -git.eeqj.de/sneak/timingbench v0.0.0-20240519025145-fb13c5c56a02 h1:b/v1EDAlsfvINIeV4znI/vH7SY7mUJOO1KWeBD+IW90= -git.eeqj.de/sneak/timingbench v0.0.0-20240519025145-fb13c5c56a02/go.mod h1:iKAlgt/liDtXifmn7fPJK+KYMr0c4lXYFJ+j5d3gfEQ= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -43,3 +41,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +sneak.berlin/go/timingbench v0.0.0-20240522212031-a6243a470213 h1:jgfwL2lUUp6aII87vgkgFenfKftsbKvUR3jlsRdS2yo= +sneak.berlin/go/timingbench v0.0.0-20240522212031-a6243a470213/go.mod h1:W+0S+VhiuNIU/06KPhWJCmNhMaCztg2MuHitNEVEFG0= diff --git a/perf_test.go b/perf_test.go index 7f3b1c8..ebd666f 100644 --- a/perf_test.go +++ b/perf_test.go @@ -5,11 +5,12 @@ import ( "testing" "time" - "git.eeqj.de/sneak/timingbench" "github.com/stretchr/testify/assert" + "sneak.berlin/go/timingbench" ) func TestShuffleSpeed(t *testing.T) { + t.Parallel() iterations := 1000 t.Logf("Running %d iterations of shuffle speed test", iterations) // Create a context with a timeout for cancellation. @@ -27,18 +28,21 @@ func TestShuffleSpeed(t *testing.T) { } func TestHandFindingSpeedFiveCard(t *testing.T) { + t.Parallel() iterations := 1000 t.Logf("Running %d iterations of hand finding speed test for 5 card hand", iterations) measureHandFinding(t, iterations, 5) } func TestHandFindingSpeedSevenCard(t *testing.T) { + t.Parallel() iterations := 1000 t.Logf("Running %d iterations of hand finding speed test for 7 card hand", iterations) measureHandFinding(t, iterations, 7) } func TestHandFindingSpeedNineCard(t *testing.T) { + t.Parallel() iterations := 100 t.Logf("Running %d iterations of hand finding speed test for 9 card hand", iterations) measureHandFinding(t, iterations, 9) diff --git a/pokercore_test.go b/pokercore_test.go index 461f992..193dc6c 100644 --- a/pokercore_test.go +++ b/pokercore_test.go @@ -12,6 +12,7 @@ type ShuffleTestResults []struct { } func TestPokerDeck(t *testing.T) { + t.Parallel() d := NewDeck() //fmt.Printf("newdeck: %+v\n", d) d.ShuffleDeterministically(437) @@ -35,6 +36,7 @@ func TestPokerDeck(t *testing.T) { } func TestDealing(t *testing.T) { + t.Parallel() d := NewDeckFromCards(Cards{ Card{Rank: ACE, Suit: HEART}, Card{Rank: DEUCE, Suit: HEART}, @@ -50,6 +52,7 @@ func TestDealing(t *testing.T) { } func TestSpecialCaseOfFiveHighStraightFlush(t *testing.T) { + t.Parallel() // actual bug from first implementation d := NewDeckFromCards(Cards{ Card{Rank: ACE, Suit: HEART}, @@ -68,6 +71,7 @@ func TestSpecialCaseOfFiveHighStraightFlush(t *testing.T) { } func TestSpecialCaseOfFiveHighStraight(t *testing.T) { + t.Parallel() // actual bug from first implementation d := NewDeckFromCards(Cards{ Card{Rank: ACE, Suit: HEART}, diff --git a/scoring_test.go b/scoring_test.go index 0f6feaa..073fdff 100644 --- a/scoring_test.go +++ b/scoring_test.go @@ -6,15 +6,16 @@ import ( "github.com/stretchr/testify/assert" ) -func TestHandDescripionBug(t *testing.T) { +func TestHandDescriptionBug(t *testing.T) { + t.Parallel() playerCount := 8 d := NewDeck() d.ShuffleDeterministically(1337) players := make([]*Cards, playerCount) - for i := 1; i-1 < playerCount; i++ { + for i := 0; i < playerCount; i++ { c := d.Deal(2) - players[i-1] = &c - t.Logf("Player %d dealt: %+v\n", i, c) + players[i] = &c + t.Logf("Player %d dealt: %+v\n", i+1, c) } t.Logf("Players: %+v\n", players) @@ -23,17 +24,17 @@ func TestHandDescripionBug(t *testing.T) { var playerResults []*PokerHand - for i := 1; i-1 < playerCount; i++ { - t.Logf("Player %d hole cards: %+v\n", i, *players[i-1]) - pc := append(*players[i-1], community...) - t.Logf("Player %d cards available: %+v\n", i, pc) + 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.Nil(t, err, "Expected no error") + assert.NoError(t, err, "Expected no error") ph, err := hand.PokerHand() - assert.Nil(t, err, "Expected no error") - t.Logf("Player %d five cards used: %+v\n", i, hand) - t.Logf("Player %d poker hand: %+v\n", i, ph) - t.Logf("Player %d best hand description: %s\n", i, ph.Description()) + 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) } @@ -43,347 +44,380 @@ func TestHandDescripionBug(t *testing.T) { t.Logf("Weird one description: %s\n", weirdOne.Description()) // T♠,7♠,9♦,7♣,T♥ - assert.Equal(t, weirdOne.Description(), "two pair, tens and sevens with a nine") + 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, weirdOne.Score, scoreShouldBe) + 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, bp, TEN, "Expected biggest pair to be a ten") + assert.Equal(t, TEN, bp, "Expected biggest pair to be a ten") sp := cards.twoPairSmallestPair() // returns Rank, because describing a pair - assert.Equal(t, sp, SEVEN, "Expected smallest pair to be a seven") + 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, k.Rank, NINE, "Expected kicker to be a nine") - + assert.Equal(t, NINE, k.Rank, "Expected kicker to be a nine") } func TestAceLowStraight(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.Nil(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, ph.Description(), "a five high straight") - assert.True(t, hand.HighestRank() == ACE, "Expected highest rank to be an ace") - s := hand.SortByRankAscending() - assert.True(t, s.First().Rank == DEUCE, "Expected first card to be a deuce") - assert.True(t, s.Last().Rank == ACE, "Expected last card in sorted to be a ace") - assert.True(t, s.Second().Rank == THREE, "Expected second card to be a three") - assert.True(t, s.Third().Rank == FOUR, "Expected third card to be a four") - assert.True(t, s.Fourth().Rank == FIVE, "Expected fourth card to be a five") - assert.True(t, s.Fifth().Rank == ACE, "Expected fifth card to be an ace") + 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) { - hand := Cards{ - TenOfSpades(), - JackOfHearts(), - KingOfClubs(), - AceOfSpades(), - QueenOfDiamonds(), - } + 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.True(t, shuffledHand.HighestRank() == ACE, "Expected highest rank to be an ace") - sortedHand := shuffledHand.SortByRankAscending() - assert.True(t, sortedHand[0].Rank == TEN, "Expected lowest rank to be a ten") - assert.True(t, sortedHand[1].Rank == JACK, "Expected second lowest rank to be a jack") - assert.True(t, sortedHand[2].Rank == QUEEN, "Expected third lowest rank to be a queen") - assert.True(t, sortedHand[3].Rank == KING, "Expected fourth lowest rank to be a king") - assert.True(t, sortedHand[4].Rank == ACE, "Expected highest rank to be an ace") + 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) { - hand := Cards{ - DeuceOfSpades(), - ThreeOfHearts(), - FourOfDiamonds(), - FiveOfClubs(), - SixOfSpades(), - } - assert.True(t, hand.containsStraight(), "Expected hand to be a straight") + 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) - //fmt.Printf("Shuffled deck: %s\n", newDeck.String()) - //fmt.Printf("new deck has %d cards\n", newDeck.Count()) - 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.True(t, shuffledHand.HighestRank() == SIX, "Expected highest rank to be a six") - assert.True(t, shuffledHand.SortByRankAscending().First().Rank == DEUCE, "Expected first card to be a deuce") + 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) { - hand := Cards{ - AceOfSpades(), - DeuceOfSpades(), - ThreeOfSpades(), - FourOfSpades(), - SixOfSpades(), - } - assert.True(t, hand.containsFlush(), "Expected hand to be a flush") - newDeck := NewDeckFromCards(hand) - newDeck.ShuffleDeterministically(123456789) - //fmt.Printf("Shuffled deck: %s\n", newDeck.String()) - //fmt.Printf("new deck has %d cards\n", newDeck.Count()) - shuffledHand := newDeck.Deal(5) - assert.True(t, shuffledHand.containsFlush(), "Expected hand to still be a flush after shuffle") + 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() - //fmt.Printf("a-2-3-4-6 flush score should be: %d\n", x) + // 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.Nil(t, err, "Expected no error") - assert.Greater(t, ph.Score, 0, "Expected score to be nonzero 0") - assert.Equal(t, ph.Score, x) + 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) { - 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") + 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.True(t, hand.HighestRank() == SIX, "Expected highest rank to be a six") + assert.Equal(t, SIX, hand.HighestRank(), "Expected highest rank to be a six") - nd := NewDeckFromCards(hand) - nd.ShuffleDeterministically(123456789) - //fmt.Printf("Shuffled deck: %s\n", nd.String()) - //fmt.Printf("new deck has %d cards\n", nd.Count()) - shuffledHand := nd.Deal(5) - assert.True(t, shuffledHand.containsStraightFlush(), "Expected hand to still be a straight flush after shuffle") - assert.True(t, shuffledHand.HighestRank() == SIX, "Expected highest rank to still be a six after shuffle") - assert.True(t, shuffledHand.HighestRank() == SIX, "Expected highest rank to be a six after shuffle even with aces low") + 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) { - 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") + 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. + // 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") + // 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.True(t, hand.HighestRank() == SIX, "Expected highest rank to be a six") + assert.Equal(t, SIX, hand.HighestRank(), "Expected highest rank to be a six") + }) } func TestRoyalFlush(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") + 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.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.True(t, hand.HighestRank() == ACE, "Expected highest rank to be an ace") - assert.False(t, hand.HighestRank() == TEN, "Expected highest rank to not be an ace") + 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) { - 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.True(t, hand.HighestRank() == KING, "Expected highest rank to be a king") - assert.True(t, hand.isUnmadeHand(), "Expected hand to be unmade") + 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) { - 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.True(t, hand.HighestRank() == KING, "Expected highest rank to be a king") - assert.False(t, hand.isUnmadeHand(), "Expected hand to not be unmade") + 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) { - hand := Cards{ - KingOfSpades(), - JackOfDiamonds(), - JackOfSpades(), - KingOfSpades(), - TenOfSpades(), - } - assert.True(t, hand.containsDuplicates(), "Expected hand to contain duplicates") + 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) { - hand := Cards{ - KingOfSpades(), - JackOfDiamonds(), - JackOfSpades(), - KingOfDiamonds(), - TenOfSpades(), - } + t.Parallel() + t.Run("Test Hand Score", func(t *testing.T) { + hand := Cards{ + KingOfSpades(), + JackOfDiamonds(), + JackOfSpades(), + KingOfDiamonds(), + TenOfSpades(), + } - ph, error := hand.PokerHand() - assert.Nil(t, error, "Expected no error") - assert.True(t, ph.Score > 0, "Expected score to be nonzero 0") - assert.True(t, ph.Score < 100000000000000000, "Expected score to be less than 100000000000000000") - - //fmt.Printf("PokerHand: %v+\n", ph) - //fmt.Printf("PH score: %d\n", ph.Score) + 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) { - // this is an actual bug in the first implementation - c, err := NewCardsFromString("9♠,9♣,Q♥,Q♦,K♣") - assert.Nil(t, err, "Expected no error") + 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") - //fmt.Printf("Cards: %v+\n", c) - - ph, err := c.PokerHand() - assert.Nil(t, err, "Expected no error") - assert.Greater(t, ph.Score, 0, "Expected score to be nonzero 0") - desc := ph.Description() - assert.Equal(t, desc, "two pair, queens and nines with a king") - - //fmt.Printf("PokerHand: %v+\n", ph) - //fmt.Printf("PH score: %d\n", ph.Score) + 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) { - handA := Cards{ - DeuceOfSpades(), - DeuceOfHearts(), - DeuceOfDiamonds(), - DeuceOfClubs(), - AceOfSpades(), - } + 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(), - } + handB := Cards{ + ThreeOfSpades(), + ThreeOfHearts(), + ThreeOfDiamonds(), + ThreeOfClubs(), + DeuceOfSpades(), + } - phA, err := handA.PokerHand() - assert.Nil(t, err, "Expected no error") - phB, err := handB.PokerHand() - assert.Nil(t, err, "Expected no error") - assert.Greater(t, phB.Score, phA.Score, "Expected hand B to be higher than hand A") + 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) { - handA := Cards{ - DeuceOfSpades(), - DeuceOfHearts(), - DeuceOfDiamonds(), - AceOfSpades(), - AceOfHearts(), - } + 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.Nil(t, err, "Expected no error") - deucesFullOfAcesScore := phA.Score + 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.Nil(t, err, "Expected no error") - threesFullOfDeucesScore := phB.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") + assert.Greater(t, threesFullOfDeucesScore, deucesFullOfAcesScore, "Expected Threes full of deuces to beat deuces full of aces") + }) }