From 336c0d953b181b8de6a1d090ef5d291cba2436f7 Mon Sep 17 00:00:00 2001 From: sneak Date: Sat, 18 May 2024 20:26:08 -0700 Subject: [PATCH] latest, passes all tests, no known bugs, woo --- Makefile | 2 +- card.go | 97 ++++++++++++++++++++++---------------------- findhand.go | 8 ++++ go.mod | 20 ++++++++- go.sum | 101 ++++++++++++++++++++++++++++++++++++++++++++++ handhelpers.go | 6 +-- perf_test.go | 74 +++++++++++++++++++++++++++++++++ pokercore_test.go | 9 ++--- scoring.go | 7 +++- scoring_test.go | 50 +++++++++++++---------- 10 files changed, 290 insertions(+), 84 deletions(-) create mode 100644 perf_test.go diff --git a/Makefile b/Makefile index 087bb44..4825ebc 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ default: test test: - go test -v ./... + go test -v -count=1 ./... diff --git a/card.go b/card.go index 812147f..1790f16 100644 --- a/card.go +++ b/card.go @@ -5,6 +5,7 @@ import ( "slices" "sort" "strings" + "unicode/utf8" "github.com/logrusorgru/aurora/v4" ) @@ -14,59 +15,56 @@ type Card struct { Suit Suit } -func NewRankFromString(rank string) Rank { - switch rank { - case "2": - return Rank(DEUCE) - case "3": - return Rank(THREE) - case "4": - return Rank(FOUR) - case "5": - return Rank(FIVE) - case "6": - return Rank(SIX) - case "7": - return Rank(SEVEN) - case "8": - return Rank(EIGHT) - case "9": - return Rank(NINE) - case "T": - return Rank(TEN) - case "J": - return Rank(JACK) - case "Q": - return Rank(QUEEN) - case "K": - return Rank(KING) - case "A": - return Rank(ACE) - } - return Rank(0) -} - -func NewSuitFromString(suit string) Suit { - switch suit { - case string(SPADE): - return Suit(SPADE) - case string(CLUB): - return Suit(CLUB) - case string(HEART): - return Suit(HEART) - case string(DIAMOND): - return Suit(DIAMOND) - } - return Suit(0) -} - func NewCardFromString(card string) (Card, error) { // FIXME extend this later to common format strings like "9s" - if len(card) != 2 { + length := utf8.RuneCountInString(card) + if length != 2 { return Card{}, fmt.Errorf("Invalid card string %s", card) } - rank := NewRankFromString(card[0:1]) - suit := NewSuitFromString(card[1:2]) + 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)) { + rank = Rank(DEUCE) + } else if strings.ContainsRune(card, rune(THREE)) { + rank = Rank(THREE) + } else if strings.ContainsRune(card, rune(FOUR)) { + rank = Rank(FOUR) + } else if strings.ContainsRune(card, rune(FIVE)) { + rank = Rank(FIVE) + } else if strings.ContainsRune(card, rune(SIX)) { + rank = Rank(SIX) + } else if strings.ContainsRune(card, rune(SEVEN)) { + rank = Rank(SEVEN) + } else if strings.ContainsRune(card, rune(EIGHT)) { + rank = Rank(EIGHT) + } else if strings.ContainsRune(card, rune(NINE)) { + rank = Rank(NINE) + } else if strings.ContainsRune(card, rune(TEN)) { + rank = Rank(TEN) + } else if strings.ContainsRune(card, rune(JACK)) { + rank = Rank(JACK) + } else if strings.ContainsRune(card, rune(QUEEN)) { + rank = Rank(QUEEN) + } else if strings.ContainsRune(card, rune(KING)) { + rank = Rank(KING) + } else if strings.ContainsRune(card, rune(ACE)) { + rank = Rank(ACE) + } else { + return Card{}, fmt.Errorf("Invalid card string %s", card) + } + if rank == Rank(0) || suit == Suit(0) { return Card{}, fmt.Errorf("Invalid card string %s", card) } @@ -78,6 +76,7 @@ func NewCardsFromString(cards string) (Cards, error) { // FIXME extend this later to common format strings like "9c Qh Qd Kc" // with or without commas var newCards Cards + newCards = make(Cards, 0) cardStrings := strings.Split(cards, ",") for _, cardString := range cardStrings { card, err := NewCardFromString(cardString) diff --git a/findhand.go b/findhand.go index fe173d4..59d2e46 100644 --- a/findhand.go +++ b/findhand.go @@ -17,6 +17,14 @@ func (hand Cards) IdentifyBestFiveCardPokerHand() (Cards, error) { return nil, ErrDuplicateCard } + if len(hand) == 5 { + return hand, nil + } + + if len(hand) < 5 { + return nil, errors.New("hand must have at least 5 cards to identify the best 5 card poker hand") + } + newHands := make([]Cards, len(hand)) for i := 0; i < len(hand); i++ { newHand := make(Cards, len(hand)-1) diff --git a/go.mod b/go.mod index 5113c5a..a92ac2a 100644 --- a/go.mod +++ b/go.mod @@ -4,14 +4,30 @@ go 1.22.2 require ( github.com/logrusorgru/aurora v2.0.3+incompatible + github.com/logrusorgru/aurora/v4 v4.0.0 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.0 ) require ( + git.eeqj.de/sneak/timingbench v0.0.0-20240519025145-fb13c5c56a02 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/logrusorgru/aurora/v4 v4.0.0 // 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/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 - golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // 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/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 + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4d7d5e1..ac46b71 100644 --- a/go.sum +++ b/go.sum @@ -1,24 +1,125 @@ +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= +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/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/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ= +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/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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +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/go.mod h1:aQAZQnhF4JGFtRJiw/eobaXpsqpVQAftEQ+hLGXaRc4= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 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/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.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/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +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.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.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/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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/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/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +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/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/handhelpers.go b/handhelpers.go index 57e1dd1..5645463 100644 --- a/handhelpers.go +++ b/handhelpers.go @@ -145,13 +145,13 @@ func (c Cards) twoPairSmallestPair() Rank { } sorted := c.SortByRankAscending() if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank { - return sorted[2].Rank + return sorted[0].Rank } if sorted[0].Rank == sorted[1].Rank && sorted[3].Rank == sorted[4].Rank { - return sorted[3].Rank + return sorted[0].Rank } if sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank { - return sorted[3].Rank + return sorted[1].Rank } panic("nope") } diff --git a/perf_test.go b/perf_test.go new file mode 100644 index 0000000..2847e8d --- /dev/null +++ b/perf_test.go @@ -0,0 +1,74 @@ +package pokercore + +import ( + "context" + "testing" + "time" + + "git.eeqj.de/sneak/timingbench" + "github.com/stretchr/testify/assert" +) + +func TestShuffleSpeed(t *testing.T) { + iterations := 1000 + t.Logf("Running %d iterations of shuffle speed test", iterations) + // Create a context with a timeout for cancellation. + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + defer cancel() + + // Measure the execution times of the sample function. + d := NewDeck() + result, err := timingbench.TimeFunction(ctx, func() { d.ShuffleRandomly() }, iterations) + if err != nil { + t.Fatalf("Error measuring function: %v", err) + } + // Print the timing results. + t.Logf(result.String()) +} + +func TestHandFindingSpeedFiveCard(t *testing.T) { + 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) { + 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) { + iterations := 100 + t.Logf("Running %d iterations of hand finding speed test for 9 card hand", iterations) + measureHandFinding(t, iterations, 9) +} + +func measureHandFinding(t *testing.T, iterations int, cardCount int) { + // Create a context with a timeout for cancellation. + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + defer cancel() + + // Measure the execution times of the sample function. + result, err := timingbench.TimeFunction(ctx, func() { + findHandInRandomCards(t, int(123456789), cardCount) // check for hand in 10 cards + }, iterations) + if err != nil { + t.Fatalf("Error measuring function: %v", err) + } + // Print the timing results. + t.Logf(result.String()) +} + +func findHandInRandomCards(t *testing.T, shuffleSeed int, cardCount int) { + d := NewDeck() + d.ShuffleDeterministically(int64(shuffleSeed)) + cards := d.Deal(cardCount) + hand, err := cards.IdentifyBestFiveCardPokerHand() + assert.Nil(t, err, "Expected no error") + ph, err := hand.PokerHand() + assert.Nil(t, err, "Expected no error") + //assert.Greater(t, ph.Score, 0, "Expected score to be nonzero 0") + desc := ph.Description() + assert.NotEmpty(t, desc, "Expected description to not be empty") +} diff --git a/pokercore_test.go b/pokercore_test.go index 51e7355..461f992 100644 --- a/pokercore_test.go +++ b/pokercore_test.go @@ -1,7 +1,6 @@ package pokercore import ( - "fmt" "testing" "github.com/stretchr/testify/assert" @@ -14,13 +13,13 @@ type ShuffleTestResults []struct { func TestPokerDeck(t *testing.T) { d := NewDeck() - fmt.Printf("newdeck: %+v\n", d) + //fmt.Printf("newdeck: %+v\n", d) d.ShuffleDeterministically(437) - fmt.Printf("deterministically shuffled deck: %+v\n", d) + //fmt.Printf("deterministically shuffled deck: %+v\n", d) cards := d.Deal(7) expected := "6♥,A♦,7♥,9♣,6♠,9♥,8♣" - fmt.Printf("deterministically shuffled deck after dealing: %+v\n", d) - fmt.Printf("cards: %+v\n", cards) + //fmt.Printf("deterministically shuffled deck after dealing: %+v\n", d) + //fmt.Printf("cards: %+v\n", cards) assert.Equal(t, expected, cards.String()) x := d.Count() diff --git a/scoring.go b/scoring.go index 7d10d17..c33d90c 100644 --- a/scoring.go +++ b/scoring.go @@ -1,6 +1,9 @@ package pokercore -import "fmt" +import ( + "errors" + "fmt" +) type HandScore int @@ -23,7 +26,7 @@ func (c Card) Score() HandScore { func (c Cards) PokerHandScore() (HandScore, error) { if len(c) != 5 { - return 0, fmt.Errorf("hand must have 5 cards to be scored as a poker hand") + return 0, errors.New("hand must have 5 cards to be scored as a poker hand") } ph, err := c.PokerHand() if err != nil { diff --git a/scoring_test.go b/scoring_test.go index 5561520..5488368 100644 --- a/scoring_test.go +++ b/scoring_test.go @@ -1,7 +1,6 @@ package pokercore import ( - "fmt" "testing" "github.com/stretchr/testify/assert" @@ -20,7 +19,7 @@ func TestAceLowStraight(t *testing.T) { assert.Nil(t, err, "Expected no error") assert.Greater(t, ph.Score, 0, "Expected score to be nonzero 0") assert.Less(t, ph.Score, 100000000000000000, "Expected score to be less than 100000000000000000") - assert.Equal(t, ph.Score, ScoreStraight+100*FIVE.Score()) + assert.Equal(t, ph.Score, ScoreStraight+1000*FIVE.Score()) assert.Equal(t, ph.Description(), "a five high straight") assert.True(t, hand.HighestRank() == ACE, "Expected highest rank to be an ace") assert.True(t, hand.SortByRankAscending().First().Rank == DEUCE, "Expected first card to be a deuce") @@ -66,12 +65,14 @@ func TestOtherStraight(t *testing.T) { newDeck := NewDeckFromCards(hand) newDeck.ShuffleDeterministically(123456789) - fmt.Printf("Shuffled deck: %s\n", newDeck.String()) - fmt.Printf("new deck has %d cards\n", newDeck.Count()) + //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") } func TestFlush(t *testing.T) { @@ -85,21 +86,24 @@ func TestFlush(t *testing.T) { 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()) + //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") + // flush value is the sum of the ranks, just like high card x := ScoreFlush - var multiplier HandScore = 100 - x += multiplier * DEUCE.Score() - multiplier *= 100 - x += multiplier * THREE.Score() - multiplier *= 100 - x += multiplier * FOUR.Score() - multiplier *= 100 - x += multiplier * SIX.Score() - fmt.Printf("a-2-3-4-6 flush score should be: %d\n", x) + 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) + + 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) } func TestStraightFlush(t *testing.T) { @@ -124,8 +128,8 @@ func TestStraightFlush(t *testing.T) { nd := NewDeckFromCards(hand) nd.ShuffleDeterministically(123456789) - fmt.Printf("Shuffled deck: %s\n", nd.String()) - fmt.Printf("new deck has %d cards\n", nd.Count()) + //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") @@ -211,8 +215,8 @@ func TestHandScore(t *testing.T) { 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) + //fmt.Printf("PokerHand: %v+\n", ph) + //fmt.Printf("PH score: %d\n", ph.Score) } func TestTwoPairBug(t *testing.T) { @@ -220,12 +224,14 @@ func TestTwoPairBug(t *testing.T) { c, err := NewCardsFromString("9♠,9♣,Q♥,Q♦,K♣") assert.Nil(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) + //fmt.Printf("PokerHand: %v+\n", ph) + //fmt.Printf("PH score: %d\n", ph.Score) }