Compare commits
5 Commits
Author | SHA1 | Date |
---|---|---|
Jeffrey Paul | f0a7b23efb | |
Jeffrey Paul | c2dfd09e9f | |
Jeffrey Paul | 5289841027 | |
Jeffrey Paul | daa52b9f3c | |
Jeffrey Paul | d973c1e1ea |
|
@ -10,4 +10,3 @@
|
||||||
|
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
*.out
|
*.out
|
||||||
.DS_Store
|
|
||||||
|
|
11
Makefile
11
Makefile
|
@ -1,4 +1,11 @@
|
||||||
default: test
|
default: test
|
||||||
|
|
||||||
test:
|
test: *.go
|
||||||
cd pokercore && go test -v ./...
|
go get -t -v
|
||||||
|
go test -v
|
||||||
|
|
||||||
|
run:
|
||||||
|
cd cmd/* && go get -v && go build .
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
go fmt *.go
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
package poker
|
||||||
|
|
||||||
|
import . "github.com/logrusorgru/aurora"
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
type Suit rune
|
||||||
|
type Rank rune
|
||||||
|
|
||||||
|
const (
|
||||||
|
CLUB Suit = '\u2663'
|
||||||
|
SPADE Suit = '\u2660'
|
||||||
|
DIAMOND Suit = '\u2666'
|
||||||
|
HEART Suit = '\u2665'
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ACE Rank = 'A'
|
||||||
|
DEUCE Rank = '2'
|
||||||
|
THREE Rank = '3'
|
||||||
|
FOUR Rank = '4'
|
||||||
|
FIVE Rank = '5'
|
||||||
|
SIX Rank = '6'
|
||||||
|
SEVEN Rank = '7'
|
||||||
|
EIGHT Rank = '8'
|
||||||
|
NINE Rank = '9'
|
||||||
|
TEN Rank = 'T'
|
||||||
|
JACK Rank = 'J'
|
||||||
|
QUEEN Rank = 'Q'
|
||||||
|
KING Rank = 'K'
|
||||||
|
)
|
||||||
|
|
||||||
|
type Card struct {
|
||||||
|
Rank Rank
|
||||||
|
Suit Suit
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cards []*Card
|
||||||
|
|
||||||
|
func NewCardsFromString(input string) (*Cards, error) {
|
||||||
|
c := make(Cards, 0)
|
||||||
|
sl := strings.Split(input, ",")
|
||||||
|
for _, pc := range sl {
|
||||||
|
newCard, err := NewCardFromString(pc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c = append(c, newCard)
|
||||||
|
}
|
||||||
|
return &c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCardFromString(input string) (*Card, error) {
|
||||||
|
if len(input) != 2 {
|
||||||
|
return nil, fmt.Errorf("invalid card string: '%s'", input)
|
||||||
|
}
|
||||||
|
nc := new(Card)
|
||||||
|
pr := input[0:1]
|
||||||
|
ps := input[1:2]
|
||||||
|
panic(fmt.Sprintf("pr=%s ps=%s nc=%s\n", pr, ps, nc))
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cards) formatForTerminal() (output string) {
|
||||||
|
var cardstrings []string
|
||||||
|
for _, card := range *c {
|
||||||
|
cardstrings = append(cardstrings, card.formatForTerminal())
|
||||||
|
}
|
||||||
|
output = strings.Join(cardstrings, ",")
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Card) formatForTerminal() (output string) {
|
||||||
|
var rank string
|
||||||
|
var suit string
|
||||||
|
color := Red
|
||||||
|
switch c.Suit {
|
||||||
|
case Suit(DIAMOND):
|
||||||
|
color = Blue
|
||||||
|
case Suit(HEART):
|
||||||
|
color = Red
|
||||||
|
case Suit(CLUB):
|
||||||
|
color = Green
|
||||||
|
case Suit(SPADE):
|
||||||
|
color = Black
|
||||||
|
}
|
||||||
|
|
||||||
|
rank = fmt.Sprintf("%s", BgGray(12, Bold(color(c.Rank))))
|
||||||
|
suit = fmt.Sprintf("%s", BgGray(12, Bold(color(c.Suit))))
|
||||||
|
output = fmt.Sprintf("%s%s", rank, suit)
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Card) String() string {
|
||||||
|
return fmt.Sprintf("%s%s", string(self.Rank), string(self.Suit))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Cards) String() (output string) {
|
||||||
|
var cardstrings []string
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
cardstrings = append(cardstrings, s[i].String())
|
||||||
|
}
|
||||||
|
output = strings.Join(cardstrings, ",")
|
||||||
|
return output
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package poker
|
||||||
|
|
||||||
|
import "encoding/binary"
|
||||||
|
import crand "crypto/rand"
|
||||||
|
import log "github.com/sirupsen/logrus"
|
||||||
|
import rand "math/rand"
|
||||||
|
|
||||||
|
type Deck struct {
|
||||||
|
Cards Cards
|
||||||
|
DealIndex int
|
||||||
|
ShuffleSeedVal int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func cryptoUint64() (v uint64) {
|
||||||
|
err := binary.Read(crand.Reader, binary.BigEndian, &v)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Debugf("crand cryptosource is returning Uint64: %d", v)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewShuffledDeck() *Deck {
|
||||||
|
d := newDeck()
|
||||||
|
d.ShuffleSeedVal = int64(cryptoUint64())
|
||||||
|
d.Shuffle()
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDeckFromSeed(seed int64) *Deck {
|
||||||
|
d := newDeck()
|
||||||
|
d.ShuffleSeedVal = seed
|
||||||
|
d.Shuffle()
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDeck() *Deck {
|
||||||
|
self := new(Deck)
|
||||||
|
|
||||||
|
ranks := []Rank{
|
||||||
|
ACE, DEUCE, THREE, FOUR, FIVE,
|
||||||
|
SIX, SEVEN, EIGHT, NINE, TEN, JACK,
|
||||||
|
QUEEN, KING}
|
||||||
|
|
||||||
|
suits := []Suit{HEART, DIAMOND, CLUB, SPADE}
|
||||||
|
|
||||||
|
self.Cards = make([]*Card, 52)
|
||||||
|
|
||||||
|
tot := 0
|
||||||
|
for i := 0; i < len(ranks); i++ {
|
||||||
|
for n := 0; n < len(suits); n++ {
|
||||||
|
self.Cards[tot] = &Card{
|
||||||
|
Rank: ranks[i],
|
||||||
|
Suit: suits[n],
|
||||||
|
}
|
||||||
|
tot++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.DealIndex = 0
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Deck) Shuffle() {
|
||||||
|
//FIXME(sneak) not sure if this is constant time or not
|
||||||
|
rnd := rand.New(rand.NewSource(self.ShuffleSeedVal))
|
||||||
|
rnd.Shuffle(len(self.Cards), func(i, j int) { self.Cards[i], self.Cards[j] = self.Cards[j], self.Cards[i] })
|
||||||
|
self.DealIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Deck) Deal(n int) (output Cards) {
|
||||||
|
|
||||||
|
if (self.DealIndex + n) > len(self.Cards) {
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
output = append(output, self.Cards[self.DealIndex+1])
|
||||||
|
self.DealIndex++
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Deck) Dealt() int {
|
||||||
|
return self.DealIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Deck) Remaining() int {
|
||||||
|
return (len(self.Cards) - self.DealIndex)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package poker
|
||||||
|
|
||||||
|
import "github.com/stretchr/testify/assert"
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
type ShuffleTestResults []struct {
|
||||||
|
SeedVal int64
|
||||||
|
Expected string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPokerDeck(t *testing.T) {
|
||||||
|
d := NewDeckFromSeed(437)
|
||||||
|
cards := d.Deal(7)
|
||||||
|
expected := "7♣,5♠,Q♠,2♦,6♦,Q♣,3♥"
|
||||||
|
assert.Equal(t, cards.String(), expected)
|
||||||
|
|
||||||
|
x := d.Remaining()
|
||||||
|
assert.Equal(t, 45, x)
|
||||||
|
|
||||||
|
d = NewDeckFromSeed(123456789)
|
||||||
|
cards = d.Deal(10)
|
||||||
|
expected = "5♥,4♥,5♠,2♣,6♦,6♣,3♦,Q♠,8♥,A♣"
|
||||||
|
assert.Equal(t, expected, cards.String())
|
||||||
|
x = d.Remaining()
|
||||||
|
assert.Equal(t, 42, x)
|
||||||
|
|
||||||
|
}
|
18
go.mod
18
go.mod
|
@ -1,17 +1,9 @@
|
||||||
module git.eeqj.de/sneak/go-poker
|
module github.com/sneak/poker
|
||||||
|
|
||||||
go 1.22.2
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.4.2
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.4.0
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
||||||
github.com/logrusorgru/aurora/v4 v4.0.0 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
32
go.sum
32
go.sum
|
@ -1,25 +1,21 @@
|
||||||
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/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA=
|
github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23 h1:Wp7NjqGKGN9te9N/rvXYRhlVcrulGdxnz8zadXWs7fc=
|
||||||
github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ=
|
github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
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/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/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=
|
|
||||||
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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
package poker
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
type HEPokerHand struct {
|
||||||
|
HoleCards Cards
|
||||||
|
CommCardsNotUsed Cards
|
||||||
|
CommCardsUsed Cards
|
||||||
|
CommCardsAll Cards
|
||||||
|
HandScore HandScore
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandScoreType uint8
|
||||||
|
type HandScoreRanking Rank
|
||||||
|
|
||||||
|
type HandScore struct {
|
||||||
|
Type HandScoreType
|
||||||
|
PrimaryRanking HandScoreRanking
|
||||||
|
SecondaryRanking HandScoreRanking
|
||||||
|
Kickers Cards
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
HIGH_CARD HandScoreType = 0
|
||||||
|
ONE_PAIR HandScoreType = 1
|
||||||
|
TWO_PAIR HandScoreType = 2
|
||||||
|
SET HandScoreType = 3
|
||||||
|
STRAIGHT HandScoreType = 4
|
||||||
|
FLUSH HandScoreType = 5
|
||||||
|
FULL_HOUSE HandScoreType = 6
|
||||||
|
QUADS HandScoreType = 7
|
||||||
|
STRAIGHT_FLUSH HandScoreType = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *HandScore) BeatsHand(v *HandScore) bool {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *HEPokerHand) IsPair() bool {
|
||||||
|
panic("not implemented")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *HEPokerHand) IsTwoPair() bool {
|
||||||
|
panic("not implemented")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *HEPokerHand) IsSet() bool {
|
||||||
|
panic("not implemented")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *HEPokerHand) IsStraight() bool {
|
||||||
|
panic("not implemented")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *HEPokerHand) IsFlush() bool {
|
||||||
|
panic("not implemented")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *HEPokerHand) IsFullHouse() bool {
|
||||||
|
panic("not implemented")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// this takes a list of pointers to 5 or more cards, permutes them into every
|
||||||
|
// possible five-card hand, by removing each in turn and recursing to
|
||||||
|
// itself, and then returns the best one as an *HEPokerHand
|
||||||
|
// out of all possible five-card permutations.
|
||||||
|
func ScoreCardsForHoldEm(input *Cards) (*HEPokerHand, error) {
|
||||||
|
if len(*input) < 5 {
|
||||||
|
return nil, errors.New("need at least 5 cards for an HEPokerHand")
|
||||||
|
}
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *HEPokerHand) ScoreHand() *HandScore {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package poker
|
||||||
|
|
||||||
|
//import "github.com/stretchr/testify/assert"
|
||||||
|
import "fmt"
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestPokerHand(t *testing.T) {
|
||||||
|
|
||||||
|
var v int64
|
||||||
|
for {
|
||||||
|
v = int64(cryptoUint64())
|
||||||
|
d := NewDeckFromSeed(v)
|
||||||
|
holeCards := d.Deal(2)
|
||||||
|
commCards := d.Deal(5)
|
||||||
|
fmt.Printf("%s %s\n", holeCards, commCards)
|
||||||
|
}
|
||||||
|
}
|
101
main.go
101
main.go
|
@ -1,101 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/rpc"
|
|
||||||
"net/rpc/jsonrpc"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
type JSONRPCServer struct {
|
|
||||||
*rpc.Server
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewJSONRPCServer() *JSONRPCServer {
|
|
||||||
return &JSONRPCServer{rpc.NewServer()}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *JSONRPCServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
||||||
log.Println("rpc server got a request")
|
|
||||||
conn, _, err := w.(http.Hijacker).Hijack()
|
|
||||||
if err != nil {
|
|
||||||
log.Print("rpc hijacking ", req.RemoteAddr, ": ", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
io.WriteString(conn, "HTTP/1.0 200 Connected to Go JSON-RPC\n\n")
|
|
||||||
codec := jsonrpc.NewServerCodec(conn)
|
|
||||||
log.Println("ServeCodec")
|
|
||||||
s.Server.ServeCodec(codec)
|
|
||||||
log.Println("finished serving request")
|
|
||||||
}
|
|
||||||
|
|
||||||
type Args struct {
|
|
||||||
A, B int
|
|
||||||
}
|
|
||||||
|
|
||||||
type Quotient struct {
|
|
||||||
Quo, Rem int
|
|
||||||
}
|
|
||||||
|
|
||||||
type Arith int
|
|
||||||
|
|
||||||
func (t *Arith) Multiply(args *Args, reply *int) error {
|
|
||||||
*reply = args.A * args.B
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Arith) Divide(args *Args, quo *Quotient) error {
|
|
||||||
if args.B == 0 {
|
|
||||||
return errors.New("divide by zero")
|
|
||||||
}
|
|
||||||
quo.Quo = args.A / args.B
|
|
||||||
quo.Rem = args.A % args.B
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
//log.SetFormatter(&log.JSONFormatter{})
|
|
||||||
|
|
||||||
// Output to stdout instead of the default stderr
|
|
||||||
// Can be any io.Writer, see below for File example
|
|
||||||
log.SetOutput(os.Stdout)
|
|
||||||
|
|
||||||
// Only log the warning severity or above.
|
|
||||||
//log.SetLevel(log.WarnLevel)
|
|
||||||
|
|
||||||
log.Infof("starting up")
|
|
||||||
|
|
||||||
go runHttpServer()
|
|
||||||
|
|
||||||
running := true
|
|
||||||
|
|
||||||
for running {
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runHttpServer() {
|
|
||||||
js := NewJSONRPCServer()
|
|
||||||
arith := new(Arith)
|
|
||||||
js.Register(arith)
|
|
||||||
|
|
||||||
port := 8080
|
|
||||||
|
|
||||||
listenaddr := fmt.Sprintf("0.0.0.0:%d", port)
|
|
||||||
|
|
||||||
s := &http.Server{
|
|
||||||
Addr: listenaddr,
|
|
||||||
Handler: js,
|
|
||||||
ReadTimeout: 10 * time.Second,
|
|
||||||
WriteTimeout: 10 * time.Second,
|
|
||||||
MaxHeaderBytes: 1 << 20,
|
|
||||||
}
|
|
||||||
log.Infof("starting up http server %s", listenaddr)
|
|
||||||
log.Fatal(s.ListenAndServe())
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.eeqj.de/sneak/go-poker/pokercore"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
myDeck := pokercore.NewDeck()
|
|
||||||
myDeck.ShuffleRandomly()
|
|
||||||
a := myDeck.Deal(5)
|
|
||||||
b := myDeck.Deal(7)
|
|
||||||
a.PrintToTerminal()
|
|
||||||
fmt.Printf("\n%#v\n", a)
|
|
||||||
b.PrintToTerminal()
|
|
||||||
fmt.Printf("\n%#v\n", b)
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
package pokercore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
rand "math/rand"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Deck struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
Cards Cards
|
|
||||||
ShuffleSeedVal int64
|
|
||||||
Dealt int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDeckFromCards(cards Cards) *Deck {
|
|
||||||
d := new(Deck)
|
|
||||||
d.Cards = make([]Card, len(cards))
|
|
||||||
copy(d.Cards, cards)
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEmptyDeck() *Deck {
|
|
||||||
d := new(Deck)
|
|
||||||
d.Cards = make([]Card, 0)
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDeck returns a new deck of 52 sorted cards.
|
|
||||||
func NewDeck() *Deck {
|
|
||||||
d := NewEmptyDeck()
|
|
||||||
|
|
||||||
ranks := []Rank{
|
|
||||||
ACE, DEUCE, THREE, FOUR, FIVE,
|
|
||||||
SIX, SEVEN, EIGHT, NINE, TEN, JACK,
|
|
||||||
QUEEN, KING}
|
|
||||||
|
|
||||||
// This is the suit order used by dealers at
|
|
||||||
// The Golden Nugget in Las Vegas.
|
|
||||||
suits := []Suit{SPADE, HEART, DIAMOND, CLUB}
|
|
||||||
|
|
||||||
for _, s := range suits {
|
|
||||||
for _, r := range ranks {
|
|
||||||
d.AddCard(Card{Rank: r, Suit: s})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Deck) AddCard(c Card) {
|
|
||||||
d.mu.Lock()
|
|
||||||
defer d.mu.Unlock()
|
|
||||||
d.Cards = append(d.Cards, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShuffleRandomly shuffles the deck using cryptographically random numbers.
|
|
||||||
func (d *Deck) ShuffleRandomly() {
|
|
||||||
d.mu.Lock()
|
|
||||||
defer d.mu.Unlock()
|
|
||||||
rnd := rand.New(rand.NewSource(int64(cryptoUint64())))
|
|
||||||
//FIXME(sneak) not sure if this is constant time or not
|
|
||||||
rnd.Shuffle(len(d.Cards), func(i, j int) { d.Cards[i], d.Cards[j] = d.Cards[j], d.Cards[i] })
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShuffleDeterministically shuffles the deck using a deterministic seed for testing.
|
|
||||||
func (d *Deck) ShuffleDeterministically(seed int64) {
|
|
||||||
d.mu.Lock()
|
|
||||||
defer d.mu.Unlock()
|
|
||||||
r := rand.New(rand.NewSource(seed))
|
|
||||||
//FIXME(sneak) not sure if this is constant time or not
|
|
||||||
r.Shuffle(len(d.Cards), func(i, j int) { d.Cards[i], d.Cards[j] = d.Cards[j], d.Cards[i] })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deal removes n cards from the top of the deck and returns them. The Deck is
|
|
||||||
// modified in place.to remove the dealt cards, just like in real life.
|
|
||||||
func (d *Deck) Deal(n int) (output Cards) {
|
|
||||||
d.mu.Lock()
|
|
||||||
defer d.mu.Unlock()
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
output = append(output, d.Cards[i])
|
|
||||||
}
|
|
||||||
d.Cards = d.Cards[n:]
|
|
||||||
d.Dealt += n
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Deck) String() string {
|
|
||||||
return fmt.Sprintf("Deck{%s size=%d dealt=%d}", d.Cards, d.Count(), d.Dealt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Deck) Count() int {
|
|
||||||
return len(d.Cards)
|
|
||||||
}
|
|
|
@ -1,175 +0,0 @@
|
||||||
package pokercore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
crand "crypto/rand"
|
|
||||||
|
|
||||||
"github.com/logrusorgru/aurora/v4"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Suit rune
|
|
||||||
|
|
||||||
func (s Suit) String() string {
|
|
||||||
return string(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Rank rune
|
|
||||||
|
|
||||||
func (r Rank) String() string {
|
|
||||||
return string(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
CLUB Suit = '\u2663' // ♣
|
|
||||||
SPADE Suit = '\u2660' // ♠
|
|
||||||
DIAMOND Suit = '\u2666' // ♦
|
|
||||||
HEART Suit = '\u2665' // ♥
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ACE Rank = 'A'
|
|
||||||
DEUCE Rank = '2'
|
|
||||||
THREE Rank = '3'
|
|
||||||
FOUR Rank = '4'
|
|
||||||
FIVE Rank = '5'
|
|
||||||
SIX Rank = '6'
|
|
||||||
SEVEN Rank = '7'
|
|
||||||
EIGHT Rank = '8'
|
|
||||||
NINE Rank = '9'
|
|
||||||
TEN Rank = 'T'
|
|
||||||
JACK Rank = 'J'
|
|
||||||
QUEEN Rank = 'Q'
|
|
||||||
KING Rank = 'K'
|
|
||||||
)
|
|
||||||
|
|
||||||
type Card struct {
|
|
||||||
Rank Rank
|
|
||||||
Suit Suit
|
|
||||||
}
|
|
||||||
|
|
||||||
type Cards []Card
|
|
||||||
|
|
||||||
func (r Rank) Int(x AcesHighOrLow) int {
|
|
||||||
return int(rankToScore(r, x))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Rank) HandScore(x AcesHighOrLow) HandScore {
|
|
||||||
return HandScore(r.Int(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cards) SortByRank(x AcesHighOrLow) Cards {
|
|
||||||
|
|
||||||
newCards := make(Cards, len(c))
|
|
||||||
copy(newCards, c)
|
|
||||||
|
|
||||||
sort.Slice(newCards, func(i, j int) bool {
|
|
||||||
return rankToScore(newCards[i].Rank, x) > rankToScore(newCards[j].Rank, x)
|
|
||||||
})
|
|
||||||
return newCards
|
|
||||||
}
|
|
||||||
|
|
||||||
type TestGenerationIteration struct {
|
|
||||||
Deck *Deck
|
|
||||||
Seed int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cards) PrintToTerminal() {
|
|
||||||
fmt.Printf("%s", c.FormatCardsForTerminal())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cards) FormatCardsForTerminal() string {
|
|
||||||
var cardstrings []string
|
|
||||||
for i := 0; i < len(c); i++ {
|
|
||||||
cardstrings = append(cardstrings, c[i].FormatForTerminal())
|
|
||||||
}
|
|
||||||
return strings.Join(cardstrings, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Card) FormatForTerminal() string {
|
|
||||||
var rank string
|
|
||||||
var suit string
|
|
||||||
color := aurora.Red
|
|
||||||
switch c.Suit {
|
|
||||||
case Suit(DIAMOND):
|
|
||||||
color = aurora.Blue
|
|
||||||
case Suit(HEART):
|
|
||||||
color = aurora.Red
|
|
||||||
case Suit(CLUB):
|
|
||||||
color = aurora.Green
|
|
||||||
case Suit(SPADE):
|
|
||||||
color = aurora.Black
|
|
||||||
}
|
|
||||||
|
|
||||||
rank = fmt.Sprintf("%s", aurora.Bold(color(c.Rank.String())))
|
|
||||||
suit = fmt.Sprintf("%s", aurora.Bold(color(c.Suit.String())))
|
|
||||||
return fmt.Sprintf("%s%s", rank, suit)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cryptoUint64() (v uint64) {
|
|
||||||
err := binary.Read(crand.Reader, binary.BigEndian, &v)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
log.Debugf("crand cryptosource is returning Uint64: %d", v)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Card) String() string {
|
|
||||||
return fmt.Sprintf("%s%s", string(c.Rank), string(c.Suit))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cards) HighestRank(x AcesHighOrLow) Rank {
|
|
||||||
c = c.SortByRank(x)
|
|
||||||
return c[0].Rank
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Cards) String() (output string) {
|
|
||||||
var cardstrings []string
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
cardstrings = append(cardstrings, s[i].String())
|
|
||||||
}
|
|
||||||
output = strings.Join(cardstrings, ",")
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
func generate() {
|
|
||||||
log.SetLevel(log.DebugLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func proto() {
|
|
||||||
myDeck := NewDeck()
|
|
||||||
myDeck.ShuffleDeterministically(42)
|
|
||||||
myHand := myDeck.Deal(2)
|
|
||||||
//spew.Dump(myHand)
|
|
||||||
fmt.Printf("my hand: %s\n", myHand)
|
|
||||||
cmty := myDeck.Deal(5)
|
|
||||||
fmt.Printf("community: %s\n", cmty)
|
|
||||||
}
|
|
||||||
|
|
||||||
func contains(ranks []Rank, rank Rank) bool {
|
|
||||||
for _, r := range ranks {
|
|
||||||
if r == rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func unique(intSlice []int) []int {
|
|
||||||
keys := make(map[int]bool)
|
|
||||||
list := []int{}
|
|
||||||
for _, entry := range intSlice {
|
|
||||||
if _, value := keys[entry]; !value {
|
|
||||||
keys[entry] = true
|
|
||||||
list = append(list, entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return list
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
package pokercore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ShuffleTestResults []struct {
|
|
||||||
SeedVal int64
|
|
||||||
Expected string
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPokerDeck(t *testing.T) {
|
|
||||||
d := NewDeck()
|
|
||||||
fmt.Printf("newdeck: %+v\n", d)
|
|
||||||
d.ShuffleDeterministically(437)
|
|
||||||
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)
|
|
||||||
assert.Equal(t, expected, cards.String())
|
|
||||||
|
|
||||||
x := d.Count()
|
|
||||||
assert.Equal(t, 45, x)
|
|
||||||
|
|
||||||
d.ShuffleDeterministically(123456789)
|
|
||||||
cards = d.Deal(10)
|
|
||||||
expected = "A♣,7♠,8♠,4♠,7♦,K♠,2♣,J♦,A♠,2♦"
|
|
||||||
assert.Equal(t, expected, cards.String())
|
|
||||||
x = d.Count()
|
|
||||||
assert.Equal(t, 35, x)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDealing(t *testing.T) {
|
|
||||||
d := NewDeckFromCards(Cards{
|
|
||||||
Card{Rank: ACE, Suit: HEART},
|
|
||||||
Card{Rank: DEUCE, Suit: HEART},
|
|
||||||
Card{Rank: THREE, Suit: HEART},
|
|
||||||
Card{Rank: FOUR, Suit: HEART},
|
|
||||||
Card{Rank: FIVE, Suit: HEART},
|
|
||||||
})
|
|
||||||
cards := d.Deal(5)
|
|
||||||
expected := "A♥,2♥,3♥,4♥,5♥"
|
|
||||||
assert.Equal(t, expected, cards.String())
|
|
||||||
x := d.Count()
|
|
||||||
assert.Equal(t, 0, x)
|
|
||||||
}
|
|
|
@ -1,263 +0,0 @@
|
||||||
package pokercore
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
type HandScore int
|
|
||||||
|
|
||||||
const (
|
|
||||||
ScoreNoPair = HandScore(iota * 100_000_000_000)
|
|
||||||
ScorePair
|
|
||||||
ScoreTwoPair
|
|
||||||
ScoreThreeOfAKind
|
|
||||||
ScoreStraight
|
|
||||||
ScoreFlush
|
|
||||||
ScoreFullHouse
|
|
||||||
ScoreFourOfAKind
|
|
||||||
ScoreStraightFlush
|
|
||||||
)
|
|
||||||
|
|
||||||
type AcesHighOrLow int
|
|
||||||
|
|
||||||
const (
|
|
||||||
AcesHigh AcesHighOrLow = iota
|
|
||||||
AcesLow
|
|
||||||
)
|
|
||||||
|
|
||||||
func rankToScore(rank Rank, AcesHighOrLow AcesHighOrLow) HandScore {
|
|
||||||
switch rank {
|
|
||||||
case ACE:
|
|
||||||
if AcesHighOrLow == AcesHigh {
|
|
||||||
return 14 // Aces are high, so we give them the highest value
|
|
||||||
} else {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
case DEUCE:
|
|
||||||
return 2
|
|
||||||
case THREE:
|
|
||||||
return 3
|
|
||||||
case FOUR:
|
|
||||||
return 4
|
|
||||||
case FIVE:
|
|
||||||
return 5
|
|
||||||
case SIX:
|
|
||||||
return 6
|
|
||||||
case SEVEN:
|
|
||||||
return 7
|
|
||||||
case EIGHT:
|
|
||||||
return 8
|
|
||||||
case NINE:
|
|
||||||
return 9
|
|
||||||
case TEN:
|
|
||||||
return 10
|
|
||||||
case JACK:
|
|
||||||
return 11
|
|
||||||
case QUEEN:
|
|
||||||
return 12
|
|
||||||
case KING:
|
|
||||||
return 13
|
|
||||||
default:
|
|
||||||
panic("nope")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Cards) ScoreHand() (HandScore, error) {
|
|
||||||
if !c.IsFiveCardPokerHand() {
|
|
||||||
return 0, fmt.Errorf("hand must have 5 cards with no duplicates to be scored")
|
|
||||||
}
|
|
||||||
if c.containsRoyalFlush() {
|
|
||||||
return ScoreStraightFlush + 1000*ACE.HandScore(AcesHigh), nil
|
|
||||||
}
|
|
||||||
if c.containsStraightFlush() {
|
|
||||||
return ScoreStraightFlush + 1000*c.HighestRank(AcesHigh).HandScore(AcesHigh), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
panic("not implemented")
|
|
||||||
// FIXME finish this
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hand Cards) containsDuplicates() bool {
|
|
||||||
seen := make(map[Card]bool)
|
|
||||||
for _, card := range hand {
|
|
||||||
if _, ok := seen[card]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
seen[card] = true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hand Cards) IsFiveCardPokerHand() bool {
|
|
||||||
return len(hand) == 5 && !hand.containsDuplicates()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hand Cards) containsFlush() bool {
|
|
||||||
if !hand.IsFiveCardPokerHand() {
|
|
||||||
panic("hand must have 5 cards to be scored")
|
|
||||||
}
|
|
||||||
suit := hand[0].Suit
|
|
||||||
for i := 1; i < len(hand); i++ {
|
|
||||||
if hand[i].Suit != suit {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hand Cards) containsStraight() bool {
|
|
||||||
if !hand.IsFiveCardPokerHand() {
|
|
||||||
panic("hand must have 5 cards to be scored")
|
|
||||||
}
|
|
||||||
sorted := hand.SortByRank(AcesHigh)
|
|
||||||
|
|
||||||
if sorted[0].Rank == ACE && sorted[1].Rank == FIVE {
|
|
||||||
// special case for A-5 straight
|
|
||||||
if sorted[1].Rank == FIVE && sorted[2].Rank == FOUR && sorted[3].Rank == THREE && sorted[4].Rank == DEUCE {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sorted[0].Rank.Int(AcesHigh) == sorted[1].Rank.Int(AcesHigh)+1 && sorted[1].Rank.Int(AcesHigh) == sorted[2].Rank.Int(AcesHigh)+1 && sorted[2].Rank.Int(AcesHigh) == sorted[3].Rank.Int(AcesHigh)+1 && sorted[3].Rank.Int(AcesHigh) == sorted[4].Rank.Int(AcesHigh)+1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hand Cards) containsStraightFlush() bool {
|
|
||||||
if !hand.IsFiveCardPokerHand() {
|
|
||||||
panic("hand must have 5 cards to be scored")
|
|
||||||
}
|
|
||||||
if hand.containsStraight() && hand.containsFlush() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hand Cards) containsRoyalFlush() bool {
|
|
||||||
if !hand.IsFiveCardPokerHand() {
|
|
||||||
panic("hand must have 5 cards to be scored")
|
|
||||||
}
|
|
||||||
sorted := hand.SortByRank(AcesHigh)
|
|
||||||
if hand.containsStraightFlush() && sorted[0].Rank == ACE {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hand Cards) containsFourOfAKind() bool {
|
|
||||||
if !hand.IsFiveCardPokerHand() {
|
|
||||||
panic("hand must have 5 cards to be scored")
|
|
||||||
}
|
|
||||||
sorted := hand.SortByRank(AcesHigh)
|
|
||||||
if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hand Cards) containsFullHouse() bool {
|
|
||||||
if !hand.IsFiveCardPokerHand() {
|
|
||||||
panic("hand must have 5 cards to be scored")
|
|
||||||
}
|
|
||||||
sorted := hand.SortByRank(AcesHigh)
|
|
||||||
if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hand Cards) containsPair() bool {
|
|
||||||
if !hand.IsFiveCardPokerHand() {
|
|
||||||
panic("hand must have 5 cards to be scored")
|
|
||||||
}
|
|
||||||
sorted := hand.SortByRank(AcesHigh)
|
|
||||||
if sorted[0].Rank == sorted[1].Rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if sorted[1].Rank == sorted[2].Rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if sorted[2].Rank == sorted[3].Rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if sorted[3].Rank == sorted[4].Rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hand Cards) containsThreeOfAKind() bool {
|
|
||||||
if !hand.IsFiveCardPokerHand() {
|
|
||||||
panic("hand must have 5 cards to be scored")
|
|
||||||
}
|
|
||||||
sorted := hand.SortByRank(AcesHigh)
|
|
||||||
if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hand Cards) containsTwoPair() bool {
|
|
||||||
if !hand.IsFiveCardPokerHand() {
|
|
||||||
panic("hand must have 5 cards to be scored")
|
|
||||||
}
|
|
||||||
sorted := hand.SortByRank(AcesHigh)
|
|
||||||
if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if sorted[0].Rank == sorted[1].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hand Cards) isUnmadeHand() bool {
|
|
||||||
if !hand.IsFiveCardPokerHand() {
|
|
||||||
panic("hand must have 5 cards to be scored")
|
|
||||||
}
|
|
||||||
return !hand.containsPair() && !hand.containsTwoPair() && !hand.containsThreeOfAKind() && !hand.containsStraight() && !hand.containsFlush() && !hand.containsFullHouse() && !hand.containsFourOfAKind() && !hand.containsStraightFlush() && !hand.containsRoyalFlush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// this method makes a n new hands where n is the number of cards in the hand
|
|
||||||
// each of the new hands has one card removed from the original hand
|
|
||||||
// then it calls the identifyBestFiveCardPokerHand method on each of the new hands
|
|
||||||
// and returns the best hand by score. this is recursion.
|
|
||||||
func (hand Cards) identifyBestFiveCardPokerHand() (Cards, error) {
|
|
||||||
newHands := make([]Cards, len(hand))
|
|
||||||
for i := 0; i < len(hand); i++ {
|
|
||||||
newHand := hand[:i]
|
|
||||||
newHand = append(newHand, hand[i+1:]...)
|
|
||||||
newHands[i] = newHand
|
|
||||||
}
|
|
||||||
var bestHand Cards
|
|
||||||
var bestScore HandScore
|
|
||||||
|
|
||||||
for _, h := range newHands {
|
|
||||||
if h.IsFiveCardPokerHand() {
|
|
||||||
score, _ := h.ScoreHand()
|
|
||||||
if score > bestScore {
|
|
||||||
bestScore = score
|
|
||||||
bestHand = h
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rh, _ := h.identifyBestFiveCardPokerHand()
|
|
||||||
score, _ := rh.ScoreHand()
|
|
||||||
if score > bestScore {
|
|
||||||
bestScore = score
|
|
||||||
bestHand = rh
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bestHand, nil
|
|
||||||
}
|
|
|
@ -1,181 +0,0 @@
|
||||||
package pokercore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAceLowStraight(t *testing.T) {
|
|
||||||
hand := Cards{
|
|
||||||
AceOfSpades(),
|
|
||||||
DeuceOfHearts(),
|
|
||||||
ThreeOfDiamonds(),
|
|
||||||
FourOfClubs(),
|
|
||||||
FiveOfSpades(),
|
|
||||||
}
|
|
||||||
assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAceHighStraight(t *testing.T) {
|
|
||||||
hand := Cards{
|
|
||||||
TenOfSpades(),
|
|
||||||
JackOfHearts(),
|
|
||||||
KingOfClubs(),
|
|
||||||
AceOfSpades(),
|
|
||||||
QueenOfDiamonds(),
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
|
|
||||||
|
|
||||||
newDeck := NewDeckFromCards(hand)
|
|
||||||
newDeck.ShuffleDeterministically(123456789)
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOtherStraight(t *testing.T) {
|
|
||||||
hand := Cards{
|
|
||||||
DeuceOfSpades(),
|
|
||||||
ThreeOfHearts(),
|
|
||||||
FourOfDiamonds(),
|
|
||||||
FiveOfClubs(),
|
|
||||||
SixOfSpades(),
|
|
||||||
}
|
|
||||||
assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
|
|
||||||
|
|
||||||
newDeck := NewDeckFromCards(hand)
|
|
||||||
newDeck.ShuffleDeterministically(123456789)
|
|
||||||
fmt.Printf("Shuffled deck: %s\n", newDeck.String())
|
|
||||||
fmt.Printf("new deck has %d cards\n", newDeck.Count())
|
|
||||||
shuffledHand := newDeck.Deal(5)
|
|
||||||
assert.True(t, shuffledHand.containsStraight(), "Expected hand to still be a straight after shuffle")
|
|
||||||
assert.False(t, shuffledHand.containsTwoPair(), "Expected hand to not be two pair")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlush(t *testing.T) {
|
|
||||||
hand := Cards{
|
|
||||||
AceOfSpades(),
|
|
||||||
DeuceOfSpades(),
|
|
||||||
ThreeOfSpades(),
|
|
||||||
FourOfSpades(),
|
|
||||||
SixOfSpades(),
|
|
||||||
}
|
|
||||||
assert.True(t, hand.containsFlush(), "Expected hand to be a flush")
|
|
||||||
newDeck := NewDeckFromCards(hand)
|
|
||||||
newDeck.ShuffleDeterministically(123456789)
|
|
||||||
fmt.Printf("Shuffled deck: %s\n", newDeck.String())
|
|
||||||
fmt.Printf("new deck has %d cards\n", newDeck.Count())
|
|
||||||
shuffledHand := newDeck.Deal(5)
|
|
||||||
assert.True(t, shuffledHand.containsFlush(), "Expected hand to still be a flush after shuffle")
|
|
||||||
|
|
||||||
x := ScoreFlush
|
|
||||||
var multiplier HandScore = 100
|
|
||||||
x += multiplier * DEUCE.HandScore(AcesHigh)
|
|
||||||
multiplier *= 100
|
|
||||||
x += multiplier * THREE.HandScore(AcesHigh)
|
|
||||||
multiplier *= 100
|
|
||||||
x += multiplier * FOUR.HandScore(AcesHigh)
|
|
||||||
multiplier *= 100
|
|
||||||
x += multiplier * SIX.HandScore(AcesHigh)
|
|
||||||
fmt.Printf("a-2-3-4-6 flush score should be: %d\n", x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStraightFlush(t *testing.T) {
|
|
||||||
hand := Cards{
|
|
||||||
SixOfSpades(),
|
|
||||||
DeuceOfSpades(),
|
|
||||||
ThreeOfSpades(),
|
|
||||||
FourOfSpades(),
|
|
||||||
FiveOfSpades(),
|
|
||||||
}
|
|
||||||
assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
|
|
||||||
assert.True(t, hand.containsFlush(), "Expected hand to be a flush")
|
|
||||||
assert.True(t, hand.containsStraightFlush(), "Expected hand to be a straight flush")
|
|
||||||
assert.False(t, hand.containsRoyalFlush(), "Expected hand to not be a royal flush")
|
|
||||||
assert.False(t, hand.containsFourOfAKind(), "Expected hand to not be four of a kind")
|
|
||||||
assert.False(t, hand.containsFullHouse(), "Expected hand to not be a full house")
|
|
||||||
assert.False(t, hand.containsThreeOfAKind(), "Expected hand to not be three of a kind")
|
|
||||||
assert.False(t, hand.containsTwoPair(), "Expected hand to not be two pair")
|
|
||||||
assert.False(t, hand.containsPair(), "Expected hand to not be a pair")
|
|
||||||
|
|
||||||
assert.True(t, hand.HighestRank(AcesHigh) == SIX, "Expected highest rank to be a six")
|
|
||||||
|
|
||||||
nd := NewDeckFromCards(hand)
|
|
||||||
nd.ShuffleDeterministically(123456789)
|
|
||||||
fmt.Printf("Shuffled deck: %s\n", nd.String())
|
|
||||||
fmt.Printf("new deck has %d cards\n", nd.Count())
|
|
||||||
shuffledHand := nd.Deal(5)
|
|
||||||
assert.True(t, shuffledHand.containsStraightFlush(), "Expected hand to still be a straight flush after shuffle")
|
|
||||||
assert.True(t, shuffledHand.HighestRank(AcesHigh) == SIX, "Expected highest rank to still be a six after shuffle")
|
|
||||||
assert.True(t, shuffledHand.HighestRank(AcesLow) == SIX, "Expected highest rank to be a six after shuffle even with aces low")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRoyalFlush(t *testing.T) {
|
|
||||||
hand := Cards{
|
|
||||||
TenOfSpades(),
|
|
||||||
JackOfSpades(),
|
|
||||||
QueenOfSpades(),
|
|
||||||
KingOfSpades(),
|
|
||||||
AceOfSpades(),
|
|
||||||
}
|
|
||||||
assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
|
|
||||||
assert.True(t, hand.containsFlush(), "Expected hand to be a flush")
|
|
||||||
assert.True(t, hand.containsStraightFlush(), "Expected hand to be a straight flush")
|
|
||||||
assert.True(t, hand.containsRoyalFlush(), "Expected hand to be a royal flush")
|
|
||||||
|
|
||||||
assert.False(t, hand.containsFourOfAKind(), "Expected hand to not be four of a kind")
|
|
||||||
assert.False(t, hand.containsFullHouse(), "Expected hand to not be a full house")
|
|
||||||
assert.False(t, hand.containsThreeOfAKind(), "Expected hand to not be three of a kind")
|
|
||||||
assert.False(t, hand.containsTwoPair(), "Expected hand to not be two pair")
|
|
||||||
assert.False(t, hand.containsPair(), "Expected hand to not be a pair")
|
|
||||||
|
|
||||||
assert.True(t, hand.HighestRank(AcesHigh) == ACE, "Expected highest rank to be an ace")
|
|
||||||
assert.False(t, hand.HighestRank(AcesHigh) == TEN, "Expected highest rank to not be an ace")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmadeHand(t *testing.T) {
|
|
||||||
hand := Cards{
|
|
||||||
TenOfSpades(),
|
|
||||||
JackOfDiamonds(),
|
|
||||||
QueenOfSpades(),
|
|
||||||
KingOfSpades(),
|
|
||||||
DeuceOfSpades(),
|
|
||||||
}
|
|
||||||
assert.False(t, hand.containsStraight(), "Expected hand to not be a straight")
|
|
||||||
assert.False(t, hand.containsFlush(), "Expected hand to not be a flush")
|
|
||||||
assert.False(t, hand.containsStraightFlush(), "Expected hand to not be a straight flush")
|
|
||||||
assert.False(t, hand.containsRoyalFlush(), "Expected hand to not be a royal flush")
|
|
||||||
assert.False(t, hand.containsFourOfAKind(), "Expected hand to not be four of a kind")
|
|
||||||
assert.False(t, hand.containsFullHouse(), "Expected hand to not be a full house")
|
|
||||||
assert.False(t, hand.containsThreeOfAKind(), "Expected hand to not be three of a kind")
|
|
||||||
assert.False(t, hand.containsTwoPair(), "Expected hand to not be two pair")
|
|
||||||
assert.False(t, hand.containsPair(), "Expected hand to not be a pair")
|
|
||||||
assert.True(t, hand.HighestRank(AcesHigh) == KING, "Expected highest rank to be a king")
|
|
||||||
assert.True(t, hand.isUnmadeHand(), "Expected hand to be unmade")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTwoPair(t *testing.T) {
|
|
||||||
hand := Cards{
|
|
||||||
KingOfSpades(),
|
|
||||||
JackOfDiamonds(),
|
|
||||||
JackOfSpades(),
|
|
||||||
KingOfDiamonds(),
|
|
||||||
TenOfSpades(),
|
|
||||||
}
|
|
||||||
assert.False(t, hand.containsStraight(), "Expected hand to not be a straight")
|
|
||||||
assert.False(t, hand.containsFlush(), "Expected hand to not be a flush")
|
|
||||||
assert.False(t, hand.containsStraightFlush(), "Expected hand to not be a straight flush")
|
|
||||||
assert.False(t, hand.containsRoyalFlush(), "Expected hand to not be a royal flush")
|
|
||||||
assert.False(t, hand.containsFourOfAKind(), "Expected hand to not be four of a kind")
|
|
||||||
assert.False(t, hand.containsFullHouse(), "Expected hand to not be a full house")
|
|
||||||
assert.False(t, hand.containsThreeOfAKind(), "Expected hand to not be three of a kind")
|
|
||||||
assert.True(t, hand.containsTwoPair(), "Expected hand to be two pair")
|
|
||||||
assert.True(t, hand.containsPair(), "Expected hand to also be a pair")
|
|
||||||
assert.True(t, hand.HighestRank(AcesHigh) == KING, "Expected highest rank to be a king")
|
|
||||||
assert.False(t, hand.isUnmadeHand(), "Expected hand to not be unmade")
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package pokercore
|
|
||||||
|
|
||||||
func AceOfSpades() Card { return Card{ACE, SPADE} }
|
|
||||||
func DeuceOfSpades() Card { return Card{DEUCE, SPADE} }
|
|
||||||
func ThreeOfSpades() Card { return Card{THREE, SPADE} }
|
|
||||||
func FourOfSpades() Card { return Card{FOUR, SPADE} }
|
|
||||||
func FiveOfSpades() Card { return Card{FIVE, SPADE} }
|
|
||||||
func SixOfSpades() Card { return Card{SIX, SPADE} }
|
|
||||||
func SevenOfSpades() Card { return Card{SEVEN, SPADE} }
|
|
||||||
func EightOfSpades() Card { return Card{EIGHT, SPADE} }
|
|
||||||
func NineOfSpades() Card { return Card{NINE, SPADE} }
|
|
||||||
func TenOfSpades() Card { return Card{TEN, SPADE} }
|
|
||||||
func JackOfSpades() Card { return Card{JACK, SPADE} }
|
|
||||||
func QueenOfSpades() Card { return Card{QUEEN, SPADE} }
|
|
||||||
func KingOfSpades() Card { return Card{KING, SPADE} }
|
|
||||||
|
|
||||||
func AceOfHearts() Card { return Card{ACE, HEART} }
|
|
||||||
func DeuceOfHearts() Card { return Card{DEUCE, HEART} }
|
|
||||||
func ThreeOfHearts() Card { return Card{THREE, HEART} }
|
|
||||||
func FourOfHearts() Card { return Card{FOUR, HEART} }
|
|
||||||
func FiveOfHearts() Card { return Card{FIVE, HEART} }
|
|
||||||
func SixOfHearts() Card { return Card{SIX, HEART} }
|
|
||||||
func SevenOfHearts() Card { return Card{SEVEN, HEART} }
|
|
||||||
func EightOfHearts() Card { return Card{EIGHT, HEART} }
|
|
||||||
func NineOfHearts() Card { return Card{NINE, HEART} }
|
|
||||||
func TenOfHearts() Card { return Card{TEN, HEART} }
|
|
||||||
func JackOfHearts() Card { return Card{JACK, HEART} }
|
|
||||||
func QueenOfHearts() Card { return Card{QUEEN, HEART} }
|
|
||||||
func KingOfHearts() Card { return Card{KING, HEART} }
|
|
||||||
|
|
||||||
func AceOfDiamonds() Card { return Card{ACE, DIAMOND} }
|
|
||||||
func DeuceOfDiamonds() Card { return Card{DEUCE, DIAMOND} }
|
|
||||||
func ThreeOfDiamonds() Card { return Card{THREE, DIAMOND} }
|
|
||||||
func FourOfDiamonds() Card { return Card{FOUR, DIAMOND} }
|
|
||||||
func FiveOfDiamonds() Card { return Card{FIVE, DIAMOND} }
|
|
||||||
func SixOfDiamonds() Card { return Card{SIX, DIAMOND} }
|
|
||||||
func SevenOfDiamonds() Card { return Card{SEVEN, DIAMOND} }
|
|
||||||
func EightOfDiamonds() Card { return Card{EIGHT, DIAMOND} }
|
|
||||||
func NineOfDiamonds() Card { return Card{NINE, DIAMOND} }
|
|
||||||
func TenOfDiamonds() Card { return Card{TEN, DIAMOND} }
|
|
||||||
func JackOfDiamonds() Card { return Card{JACK, DIAMOND} }
|
|
||||||
func QueenOfDiamonds() Card { return Card{QUEEN, DIAMOND} }
|
|
||||||
func KingOfDiamonds() Card { return Card{KING, DIAMOND} }
|
|
||||||
|
|
||||||
func AceOfClubs() Card { return Card{ACE, CLUB} }
|
|
||||||
func DeuceOfClubs() Card { return Card{DEUCE, CLUB} }
|
|
||||||
func ThreeOfClubs() Card { return Card{THREE, CLUB} }
|
|
||||||
func FourOfClubs() Card { return Card{FOUR, CLUB} }
|
|
||||||
func FiveOfClubs() Card { return Card{FIVE, CLUB} }
|
|
||||||
func SixOfClubs() Card { return Card{SIX, CLUB} }
|
|
||||||
func SevenOfClubs() Card { return Card{SEVEN, CLUB} }
|
|
||||||
func EightOfClubs() Card { return Card{EIGHT, CLUB} }
|
|
||||||
func NineOfClubs() Card { return Card{NINE, CLUB} }
|
|
||||||
func TenOfClubs() Card { return Card{TEN, CLUB} }
|
|
||||||
func JackOfClubs() Card { return Card{JACK, CLUB} }
|
|
||||||
func QueenOfClubs() Card { return Card{QUEEN, CLUB} }
|
|
||||||
func KingOfClubs() Card { return Card{KING, CLUB} }
|
|
Loading…
Reference in New Issue