Compare commits

..

4 Commits

Author SHA1 Message Date
8dbfc45ebb latest 2026-01-06 10:13:38 -08:00
e26b79751f cleanups 2024-05-18 23:55:15 -07:00
6c66adf8d3 linting and formatting updates 2024-05-18 23:50:19 -07:00
ab92f29a88 latest. passes all tests, no known bugs, has examples. 2024-05-18 23:25:55 -07:00
12 changed files with 788 additions and 519 deletions

View File

@@ -16,3 +16,9 @@ test:
examples: examples:
test -d bin || mkdir bin test -d bin || mkdir bin
go build -o bin/ ./examples/... go build -o bin/ ./examples/...
lint:
golangci-lint run
fmt:
go fmt ./...

160
card.go
View File

@@ -7,7 +7,7 @@ import (
"strings" "strings"
"unicode/utf8" "unicode/utf8"
"github.com/logrusorgru/aurora/v4" aurora "github.com/logrusorgru/aurora/v4"
) )
type Card struct { type Card struct {
@@ -19,55 +19,52 @@ func NewCardFromString(card string) (Card, error) {
// FIXME extend this later to common format strings like "9s" // FIXME extend this later to common format strings like "9s"
length := utf8.RuneCountInString(card) length := utf8.RuneCountInString(card)
if length != 2 { if length != 2 {
return Card{}, fmt.Errorf("Invalid card string %s", card) return Card{}, fmt.Errorf("invalid card string %s, must be 2 characters", card)
}
var rank Rank
var suit Suit
if strings.ContainsRune(card, rune(SPADE)) {
suit = Suit(SPADE)
} else if strings.ContainsRune(card, rune(HEART)) {
suit = Suit(HEART)
} else if strings.ContainsRune(card, rune(DIAMOND)) {
suit = Suit(DIAMOND)
} else if strings.ContainsRune(card, rune(CLUB)) {
suit = Suit(CLUB)
} else {
return Card{}, fmt.Errorf("Invalid card string %s", card)
} }
if strings.ContainsRune(card, rune(DEUCE)) { rankMap := map[rune]Rank{
rank = Rank(DEUCE) rune(DEUCE): DEUCE,
} else if strings.ContainsRune(card, rune(THREE)) { rune(THREE): THREE,
rank = Rank(THREE) rune(FOUR): FOUR,
} else if strings.ContainsRune(card, rune(FOUR)) { rune(FIVE): FIVE,
rank = Rank(FOUR) rune(SIX): SIX,
} else if strings.ContainsRune(card, rune(FIVE)) { rune(SEVEN): SEVEN,
rank = Rank(FIVE) rune(EIGHT): EIGHT,
} else if strings.ContainsRune(card, rune(SIX)) { rune(NINE): NINE,
rank = Rank(SIX) rune(TEN): TEN,
} else if strings.ContainsRune(card, rune(SEVEN)) { rune(JACK): JACK,
rank = Rank(SEVEN) rune(QUEEN): QUEEN,
} else if strings.ContainsRune(card, rune(EIGHT)) { rune(KING): KING,
rank = Rank(EIGHT) rune(ACE): ACE,
} else if strings.ContainsRune(card, rune(NINE)) { }
rank = Rank(NINE)
} else if strings.ContainsRune(card, rune(TEN)) { suitMap := map[rune]Suit{
rank = Rank(TEN) rune(SPADE): SPADE,
} else if strings.ContainsRune(card, rune(JACK)) { rune(HEART): HEART,
rank = Rank(JACK) rune(DIAMOND): DIAMOND,
} else if strings.ContainsRune(card, rune(QUEEN)) { rune(CLUB): CLUB,
rank = Rank(QUEEN) }
} else if strings.ContainsRune(card, rune(KING)) {
rank = Rank(KING) var rank Rank
} else if strings.ContainsRune(card, rune(ACE)) { var suit Suit
rank = Rank(ACE) for r := range rankMap {
} else { if strings.ContainsRune(card, r) {
return Card{}, fmt.Errorf("Invalid card string %s", card) rank = rankMap[r]
break
}
}
for s := range suitMap {
if strings.ContainsRune(card, s) {
suit = suitMap[s]
break
}
} }
if rank == Rank(0) || suit == Suit(0) { if rank == Rank(0) || suit == Suit(0) {
return Card{}, fmt.Errorf("Invalid card string %s", card) return Card{}, fmt.Errorf("invalid card string %s", card)
} }
return Card{Rank: rank, Suit: suit}, nil return Card{Rank: rank, Suit: suit}, nil
} }
@@ -75,18 +72,17 @@ func NewCardsFromString(cards string) (Cards, error) {
// supports a string like 9♠,9♣,Q♥,Q♦,K♣ // supports a string like 9♠,9♣,Q♥,Q♦,K♣
// FIXME extend this later to common format strings like "9c Qh Qd Kc" // FIXME extend this later to common format strings like "9c Qh Qd Kc"
// with or without commas // with or without commas
var newCards Cards
newCards = make(Cards, 0)
cardStrings := strings.Split(cards, ",") cardStrings := strings.Split(cards, ",")
for _, cardString := range cardStrings { newCards := make(Cards, len(cardStrings))
for i, cardString := range cardStrings {
card, err := NewCardFromString(cardString) card, err := NewCardFromString(cardString)
if err != nil { if err != nil {
return Cards{}, err return nil, err
} }
newCards = append(newCards, card) newCards[i] = card
} }
if len(newCards) == 0 { if len(newCards) == 0 {
return Cards{}, fmt.Errorf("No cards found in string %s", cards) return nil, fmt.Errorf("no cards found in string %s", cards)
} }
return newCards, nil return newCards, nil
} }
@@ -97,42 +93,41 @@ func (c *Card) String() string {
type Cards []Card type Cards []Card
func (c Cards) First() Card { func (cards Cards) First() Card {
return c[0] return cards[0]
} }
func (c Cards) Second() Card { func (cards Cards) Second() Card {
return c[1] return cards[1]
} }
func (c Cards) Third() Card { func (cards Cards) Third() Card {
return c[2] return cards[2]
} }
func (c Cards) Fourth() Card { func (cards Cards) Fourth() Card {
return c[3] return cards[3]
} }
func (c Cards) Fifth() Card { func (cards Cards) Fifth() Card {
return c[4] return cards[4]
} }
func (c Cards) Last() Card { func (cards Cards) Last() Card {
return c[len(c)-1] return cards[len(cards)-1]
} }
func (c Cards) SortByRankAscending() Cards { func (cards Cards) SortByRankAscending() Cards {
newCards := make(Cards, len(c)) sortedCards := make(Cards, len(cards))
copy(newCards, c) copy(sortedCards, cards)
sort.Slice(newCards, func(i, j int) bool { sort.Slice(sortedCards, func(i, j int) bool {
return newCards[i].Rank.Score() < newCards[j].Rank.Score() return sortedCards[i].Rank.Score() < sortedCards[j].Rank.Score()
}) })
return sortedCards
return newCards
} }
func (c Cards) PrintToTerminal() { func (cards Cards) PrintToTerminal() {
fmt.Printf("%s", c.FormatForTerminal()) fmt.Printf("%s", cards.FormatForTerminal())
} }
type SortOrder int type SortOrder int
@@ -142,18 +137,18 @@ const (
AceHighDescending AceHighDescending
) )
func (c Cards) FormatForTerminalSorted(order SortOrder) string { func (cards Cards) FormatForTerminalSorted(order SortOrder) string {
sorted := c.SortByRankAscending() // this is ascending sorted := cards.SortByRankAscending() // this is ascending
if order == AceHighDescending { if order == AceHighDescending {
slices.Reverse(sorted) slices.Reverse(sorted)
} }
return sorted.FormatForTerminal() return sorted.FormatForTerminal()
} }
func (c Cards) FormatForTerminal() string { func (cards Cards) FormatForTerminal() string {
var cardstrings []string var cardstrings []string
for i := 0; i < len(c); i++ { for i := 0; i < len(cards); i++ {
cardstrings = append(cardstrings, c[i].FormatForTerminal()) cardstrings = append(cardstrings, cards[i].FormatForTerminal())
} }
return strings.Join(cardstrings, ",") return strings.Join(cardstrings, ",")
} }
@@ -178,16 +173,15 @@ func (c Card) FormatForTerminal() string {
return fmt.Sprintf("%s%s", rank, suit) return fmt.Sprintf("%s%s", rank, suit)
} }
func (c Cards) HighestRank() Rank { func (cards Cards) HighestRank() Rank {
sorted := c.SortByRankAscending() sorted := cards.SortByRankAscending()
return sorted[len(sorted)-1].Rank return sorted[len(sorted)-1].Rank
} }
func (s Cards) String() (output string) { func (cards Cards) String() string {
var cardstrings []string var cardstrings []string
for i := 0; i < len(s); i++ { for i := 0; i < len(cards); i++ {
cardstrings = append(cardstrings, s[i].String()) cardstrings = append(cardstrings, cards[i].String())
} }
output = strings.Join(cardstrings, ",") return strings.Join(cardstrings, ",")
return output
} }

119
examples/flip/main.go Normal file
View File

@@ -0,0 +1,119 @@
package main
import (
"fmt"
"git.eeqj.de/sneak/pokercore"
)
var playerCount = 2
type Player struct {
Hand pokercore.Cards
ScoredHand *pokercore.PokerHand
Position int
}
type Game struct {
Deck *pokercore.Deck
Players []*Player
Community pokercore.Cards
Street int
}
func NewGame() *Game {
g := &Game{}
g.Street = 0
g.Deck = pokercore.NewDeck()
g.Deck.ShuffleRandomly()
return g
}
func (g *Game) StreetAsString() string {
switch g.Street {
case 0:
return "pre-flop"
case 1:
return "flop"
case 2:
return "turn"
case 3:
return "river"
}
return "unknown"
}
func (g *Game) DealPlayersIn() {
for i := 0; i < playerCount; i++ {
p := Player{Hand: g.Deck.Deal(2), Position: i + 1}
g.Players = append(g.Players, &p)
}
}
func (g *Game) ShowGameStatus() {
fmt.Printf("Street: %s\n", g.StreetAsString())
fmt.Printf("Community cards: %s\n", g.Community.FormatForTerminal())
for _, p := range g.Players {
if p != nil {
fmt.Printf("Player %d: %s\n", p.Position, p.Hand.FormatForTerminal())
if g.Street > 0 {
ac := append(p.Hand, g.Community...)
ph, err := ac.PokerHand()
if err != nil {
panic(err)
}
fmt.Printf("Player %d has %s\n", p.Position, ph.Description())
fmt.Printf("Player %d Score: %d\n", p.Position, ph.Score)
}
}
}
}
func (g *Game) DealFlop() {
g.Community = g.Deck.Deal(3)
g.Street = 1
}
func (g *Game) DealTurn() {
g.Community = append(g.Community, g.Deck.Deal(1)...)
g.Street = 2
}
func (g *Game) DealRiver() {
g.Community = append(g.Community, g.Deck.Deal(1)...)
g.Street = 3
}
func (g *Game) ShowWinner() {
var winner *Player
var winningHand *pokercore.PokerHand
for _, p := range g.Players {
if p != nil {
ac := append(p.Hand, g.Community...)
ph, err := ac.PokerHand()
if err != nil {
panic(err)
}
if winner == nil {
winner = p
winningHand = ph
} else {
if ph.Score > winningHand.Score {
winner = p
winningHand = ph
}
}
}
}
fmt.Printf("Winner: Player %d with %s.\n", winner.Position, winningHand.Description())
}
func main() {
g := NewGame()
g.DealPlayersIn()
g.DealFlop()
g.DealTurn()
g.DealRiver()
g.ShowGameStatus()
g.ShowWinner()
}

155
examples/montecarlo/main.go Normal file
View File

@@ -0,0 +1,155 @@
package main
import (
"fmt"
"time"
"github.com/rcrowley/go-metrics"
"github.com/schollz/progressbar/v3"
"sneak.berlin/go/pokercore"
)
var gameCount = 50_000
var playerCount = 2
type Player struct {
Hand pokercore.Cards
ScoredHand *pokercore.PokerHand
Position int
}
type Game struct {
Deck *pokercore.Deck
Players []*Player
Community pokercore.Cards
Street int
}
func NewGame() *Game {
g := &Game{}
g.Street = 0
g.Deck = pokercore.NewDeck()
g.Deck.ShuffleRandomly()
return g
}
func (g *Game) StreetAsString() string {
switch g.Street {
case 0:
return "pre-flop"
case 1:
return "flop"
case 2:
return "turn"
case 3:
return "river"
}
return "unknown"
}
func (g *Game) DealPlayersIn() {
for i := 0; i < playerCount; i++ {
p := Player{Hand: g.Deck.Deal(2), Position: i + 1}
g.Players = append(g.Players, &p)
}
}
func (g *Game) DealFlop() {
g.Community = g.Deck.Deal(3)
g.Street = 1
}
func (g *Game) DealTurn() {
g.Community = append(g.Community, g.Deck.Deal(1)...)
g.Street = 2
}
func (g *Game) DealRiver() {
g.Community = append(g.Community, g.Deck.Deal(1)...)
g.Street = 3
}
func (g *Game) ShowWinner() int {
var winner *Player
var winningHand *pokercore.PokerHand
for _, p := range g.Players {
if p != nil {
ac := append(p.Hand, g.Community...)
ph, err := ac.PokerHand()
if err != nil {
panic(err)
}
if winner == nil {
winner = p
winningHand = ph
} else {
if ph.Score > winningHand.Score {
winner = p
winningHand = ph
}
}
}
}
return winner.Position
}
func main() {
oneWins := 0
twoWins := 0
registry := metrics.NewRegistry()
timer := metrics.NewTimer()
registry.Register("pokerGame", timer)
bar := progressbar.NewOptions(
gameCount,
progressbar.OptionSetDescription("Gambling..."),
progressbar.OptionShowCount(),
progressbar.OptionShowIts(),
progressbar.OptionSetPredictTime(true),
progressbar.OptionClearOnFinish(),
progressbar.OptionThrottle(
100*time.Millisecond,
), // Update every 100ms
)
for i := 0; i < gameCount; i++ {
start := time.Now()
w := runFlip()
duration := time.Since(start)
timer.Update(duration)
bar.Add(1)
if w == 1 {
oneWins++
} else {
twoWins++
}
}
fmt.Printf("%d games played\n", gameCount)
fmt.Printf("Min: %d ns\n", timer.Min())
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",
timer.Percentile(0.50),
timer.Percentile(0.75),
timer.Percentile(0.95),
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)
fmt.Printf("Player 2 won: %d (%0.2f%%)\n", twoWins, twoWinPercentage)
}
func runFlip() int {
g := NewGame()
g.DealPlayersIn()
g.DealFlop()
g.DealTurn()
g.DealRiver()
winnerPosition := g.ShowWinner()
return winnerPosition
}

56
examples/sf/main.go Normal file
View File

@@ -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
}

21
go.mod
View File

@@ -1,36 +1,23 @@
module git.eeqj.de/sneak/pokercore module sneak.berlin/go/pokercore
go 1.22.2 go 1.22.2
require ( require (
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/logrusorgru/aurora/v4 v4.0.0 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/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.0
sneak.berlin/go/timingbench v0.0.0-20240522212031-a6243a470213
) )
require ( require (
git.eeqj.de/sneak/timingbench v0.0.0-20240519025145-fb13c5c56a02 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/k0kubun/pp/v3 v3.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.33.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/schollz/progressbar/v3 v3.14.2 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sys v0.20.0 // indirect golang.org/x/sys v0.20.0 // indirect
golang.org/x/term v0.20.0 // indirect golang.org/x/term v0.20.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.20.0 // indirect
gonum.org/v1/gonum v0.15.0 // indirect gonum.org/v1/gonum v0.15.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

102
go.sum
View File

@@ -1,53 +1,16 @@
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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs=
github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA= github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA=
github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ= github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/schollz/progressbar/v3 v3.14.2 h1:EducH6uNLIWsr560zSV1KrTeUb/wZGAHqyMFIEa99ks= github.com/schollz/progressbar/v3 v3.14.2 h1:EducH6uNLIWsr560zSV1KrTeUb/wZGAHqyMFIEa99ks=
@@ -57,77 +20,26 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ= gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ=
gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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=

View File

@@ -5,11 +5,12 @@ import (
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/timingbench"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sneak.berlin/go/timingbench"
) )
func TestShuffleSpeed(t *testing.T) { func TestShuffleSpeed(t *testing.T) {
t.Parallel()
iterations := 1000 iterations := 1000
t.Logf("Running %d iterations of shuffle speed test", iterations) t.Logf("Running %d iterations of shuffle speed test", iterations)
// Create a context with a timeout for cancellation. // Create a context with a timeout for cancellation.
@@ -27,18 +28,21 @@ func TestShuffleSpeed(t *testing.T) {
} }
func TestHandFindingSpeedFiveCard(t *testing.T) { func TestHandFindingSpeedFiveCard(t *testing.T) {
t.Parallel()
iterations := 1000 iterations := 1000
t.Logf("Running %d iterations of hand finding speed test for 5 card hand", iterations) t.Logf("Running %d iterations of hand finding speed test for 5 card hand", iterations)
measureHandFinding(t, iterations, 5) measureHandFinding(t, iterations, 5)
} }
func TestHandFindingSpeedSevenCard(t *testing.T) { func TestHandFindingSpeedSevenCard(t *testing.T) {
t.Parallel()
iterations := 1000 iterations := 1000
t.Logf("Running %d iterations of hand finding speed test for 7 card hand", iterations) t.Logf("Running %d iterations of hand finding speed test for 7 card hand", iterations)
measureHandFinding(t, iterations, 7) measureHandFinding(t, iterations, 7)
} }
func TestHandFindingSpeedNineCard(t *testing.T) { func TestHandFindingSpeedNineCard(t *testing.T) {
t.Parallel()
iterations := 100 iterations := 100
t.Logf("Running %d iterations of hand finding speed test for 9 card hand", iterations) t.Logf("Running %d iterations of hand finding speed test for 9 card hand", iterations)
measureHandFinding(t, iterations, 9) measureHandFinding(t, iterations, 9)

View File

@@ -12,6 +12,7 @@ type ShuffleTestResults []struct {
} }
func TestPokerDeck(t *testing.T) { func TestPokerDeck(t *testing.T) {
t.Parallel()
d := NewDeck() d := NewDeck()
//fmt.Printf("newdeck: %+v\n", d) //fmt.Printf("newdeck: %+v\n", d)
d.ShuffleDeterministically(437) d.ShuffleDeterministically(437)
@@ -35,6 +36,7 @@ func TestPokerDeck(t *testing.T) {
} }
func TestDealing(t *testing.T) { func TestDealing(t *testing.T) {
t.Parallel()
d := NewDeckFromCards(Cards{ d := NewDeckFromCards(Cards{
Card{Rank: ACE, Suit: HEART}, Card{Rank: ACE, Suit: HEART},
Card{Rank: DEUCE, Suit: HEART}, Card{Rank: DEUCE, Suit: HEART},
@@ -50,6 +52,7 @@ func TestDealing(t *testing.T) {
} }
func TestSpecialCaseOfFiveHighStraightFlush(t *testing.T) { func TestSpecialCaseOfFiveHighStraightFlush(t *testing.T) {
t.Parallel()
// actual bug from first implementation // actual bug from first implementation
d := NewDeckFromCards(Cards{ d := NewDeckFromCards(Cards{
Card{Rank: ACE, Suit: HEART}, Card{Rank: ACE, Suit: HEART},
@@ -68,6 +71,7 @@ func TestSpecialCaseOfFiveHighStraightFlush(t *testing.T) {
} }
func TestSpecialCaseOfFiveHighStraight(t *testing.T) { func TestSpecialCaseOfFiveHighStraight(t *testing.T) {
t.Parallel()
// actual bug from first implementation // actual bug from first implementation
d := NewDeckFromCards(Cards{ d := NewDeckFromCards(Cards{
Card{Rank: ACE, Suit: HEART}, Card{Rank: ACE, Suit: HEART},

View File

@@ -62,13 +62,13 @@ func (c Cards) PokerHand() (*PokerHand, error) {
return ph, nil return ph, nil
} }
func (ph PokerHand) ToSortedCards() Cards { func (ph *PokerHand) ToSortedCards() Cards {
// I believe ph.Hand is already sorted, but in any case it's only 5 // I believe ph.Hand is already sorted, but in any case it's only 5
// cards and sorting it costs ~nothing // cards and sorting it costs ~nothing
return ph.Hand.SortByRankAscending() return ph.Hand.SortByRankAscending()
} }
func (ph PokerHand) Compare(other PokerHand) int { func (ph *PokerHand) Compare(other PokerHand) int {
if ph.Score > other.Score { if ph.Score > other.Score {
return 1 return 1
} }
@@ -78,74 +78,74 @@ func (ph PokerHand) Compare(other PokerHand) int {
return 0 return 0
} }
func (ph PokerHand) HighestRank() Rank { func (ph *PokerHand) HighestRank() Rank {
return ph.Hand.HighestRank() return ph.Hand.HighestRank()
} }
func (ph PokerHand) String() string { func (ph *PokerHand) String() string {
return fmt.Sprintf("<PokerHand: %s (%s)>", ph.Hand.String(), ph.Description()) return fmt.Sprintf("<PokerHand: %s (%s)>", ph.Hand.String(), ph.Description())
} }
func (c PokerHand) Description() string { func (ph *PokerHand) Description() string {
if c.Type == RoyalFlush { if ph.Type == RoyalFlush {
return fmt.Sprintf("a royal flush in %s", c.Hand[0].Suit) return fmt.Sprintf("a royal flush in %s", ph.Hand[0].Suit)
} }
if c.Hand.containsStraightFlush() { if ph.Hand.containsStraightFlush() {
if c.Hand[3].Rank == FIVE && c.Hand[4].Rank == ACE { if ph.Hand[3].Rank == FIVE && ph.Hand[4].Rank == ACE {
// special case for steel wheel // special case for steel wheel
return fmt.Sprintf("%s high straight flush in %s", c.Hand[3].Rank.WithArticle(), c.Hand[4].Suit) return fmt.Sprintf("%s high straight flush in %s", ph.Hand[3].Rank.WithArticle(), ph.Hand[4].Suit)
} }
return fmt.Sprintf("%s high straight flush in %s", c.HighestRank().WithArticle(), c.Hand[4].Suit) return fmt.Sprintf("%s high straight flush in %s", ph.HighestRank().WithArticle(), ph.Hand[4].Suit)
} }
if c.Hand.containsFourOfAKind() { if ph.Hand.containsFourOfAKind() {
return fmt.Sprintf("four %s with %s", c.Hand.fourOfAKindRank().Pluralize(), c.Hand.fourOfAKindKicker().Rank.WithArticle()) return fmt.Sprintf("four %s with %s", ph.Hand.fourOfAKindRank().Pluralize(), ph.Hand.fourOfAKindKicker().Rank.WithArticle())
} }
if c.Hand.containsFullHouse() { if ph.Hand.containsFullHouse() {
return fmt.Sprintf("a full house, %s full of %s", c.Hand.fullHouseTripsRank().Pluralize(), c.Hand.fullHousePairRank().Pluralize()) return fmt.Sprintf("a full house, %s full of %s", ph.Hand.fullHouseTripsRank().Pluralize(), ph.Hand.fullHousePairRank().Pluralize())
} }
if c.Hand.containsFlush() { if ph.Hand.containsFlush() {
return fmt.Sprintf("%s high flush in %s", c.HighestRank().WithArticle(), c.Hand[4].Suit) return fmt.Sprintf("%s high flush in %s", ph.HighestRank().WithArticle(), ph.Hand[4].Suit)
} }
if c.Hand.containsStraight() { if ph.Hand.containsStraight() {
if c.Hand[3].Rank == FIVE && c.Hand[4].Rank == ACE { if ph.Hand[3].Rank == FIVE && ph.Hand[4].Rank == ACE {
// special case for wheel straight // special case for wheel straight
return fmt.Sprintf("%s high straight", c.Hand[3].Rank.WithArticle()) return fmt.Sprintf("%s high straight", ph.Hand[3].Rank.WithArticle())
} }
return fmt.Sprintf("%s high straight", c.HighestRank().WithArticle()) return fmt.Sprintf("%s high straight", ph.HighestRank().WithArticle())
} }
if c.Hand.containsThreeOfAKind() { if ph.Hand.containsThreeOfAKind() {
return fmt.Sprintf( return fmt.Sprintf(
"three %s with %s and %s", "three %s with %s and %s",
c.Hand.threeOfAKindTripsRank().Pluralize(), ph.Hand.threeOfAKindTripsRank().Pluralize(),
c.Hand.threeOfAKindFirstKicker().Rank.WithArticle(), ph.Hand.threeOfAKindFirstKicker().Rank.WithArticle(),
c.Hand.threeOfAKindSecondKicker().Rank.WithArticle(), ph.Hand.threeOfAKindSecondKicker().Rank.WithArticle(),
) )
} }
if c.Hand.containsTwoPair() { if ph.Hand.containsTwoPair() {
return fmt.Sprintf( return fmt.Sprintf(
"two pair, %s and %s with %s", "two pair, %s and %s with %s",
c.Hand.twoPairBiggestPair().Pluralize(), ph.Hand.twoPairBiggestPair().Pluralize(),
c.Hand.twoPairSmallestPair().Pluralize(), ph.Hand.twoPairSmallestPair().Pluralize(),
c.Hand.twoPairKicker().Rank.WithArticle(), ph.Hand.twoPairKicker().Rank.WithArticle(),
) )
} }
if c.Hand.containsPair() { if ph.Hand.containsPair() {
return fmt.Sprintf( return fmt.Sprintf(
"a pair of %s with %s, %s, and %s", "a pair of %s with %s, %s, and %s",
c.Hand.pairRank().Pluralize(), ph.Hand.pairRank().Pluralize(),
c.Hand.pairFirstKicker().Rank.WithArticle(), ph.Hand.pairFirstKicker().Rank.WithArticle(),
c.Hand.pairSecondKicker().Rank.WithArticle(), ph.Hand.pairSecondKicker().Rank.WithArticle(),
c.Hand.pairThirdKicker().Rank.WithArticle(), ph.Hand.pairThirdKicker().Rank.WithArticle(),
) )
} }
return fmt.Sprintf( return fmt.Sprintf(
// "ace high with an eight, a seven, a six, and a deuce" // "ace high with an eight, a seven, a six, and a deuce"
"%s high with %s, %s, %s, and %s", "%s high with %s, %s, %s, and %s",
c.Hand[4].Rank, ph.Hand[4].Rank,
c.Hand[3].Rank.WithArticle(), ph.Hand[3].Rank.WithArticle(),
c.Hand[2].Rank.WithArticle(), ph.Hand[2].Rank.WithArticle(),
c.Hand[1].Rank.WithArticle(), ph.Hand[1].Rank.WithArticle(),
c.Hand[0].Rank.WithArticle(), ph.Hand[0].Rank.WithArticle(),
) )
} }

View File

@@ -142,5 +142,4 @@ func (ph *PokerHand) calculateScore() {
ph.Score += ph.Hand[2].Score() ph.Score += ph.Hand[2].Score()
ph.Score += ph.Hand[3].Score() ph.Score += ph.Hand[3].Score()
ph.Score += ph.Hand[4].Score() ph.Score += ph.Hand[4].Score()
return
} }

View File

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