Compare commits

...

4 Commits
master ... next

  1. 11
      Makefile
  2. 106
      card.go
  3. 89
      deck.go
  4. 10
      deck_test.go
  5. 9
      go.mod
  6. 21
      go.sum
  7. 83
      hand.go
  8. 17
      hand_test.go
  9. 101
      main.go
  10. 10
      misc/generateTestSuite.go
  11. 10
      pokercore/Makefile
  12. 232
      pokercore/pokercore.go

@ -1,4 +1,11 @@
default: test
test:
cd pokercore && make test
test: *.go
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)
}

@ -1,4 +1,4 @@
package pokercore
package poker
import "github.com/stretchr/testify/assert"
import "testing"
@ -9,19 +9,17 @@ type ShuffleTestResults []struct {
}
func TestPokerDeck(t *testing.T) {
d := NewDeck()
d.ShuffleDeterministically(437)
d := NewDeckFromSeed(437)
cards := d.Deal(7)
//expected := "7C,5S,QS,2D,6D,QC,3H"
expected := "7♣,5♠,Q♠,2♦,6♦,Q♣,3♥"
assert.Equal(t, cards.String(), expected)
x := d.Remaining()
assert.Equal(t, 45, x)
d.ShuffleDeterministically(123456789)
d = NewDeckFromSeed(123456789)
cards = d.Deal(10)
expected = "2♣,T♠,4♥,Q♣,9♦,7♥,7♠,6♥,5♥,5♠"
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)

@ -0,0 +1,9 @@
module github.com/sneak/poker
go 1.13
require (
github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23
github.com/sirupsen/logrus v1.4.2
github.com/stretchr/testify v1.4.0
)

@ -0,0 +1,21 @@
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/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23 h1:Wp7NjqGKGN9te9N/rvXYRhlVcrulGdxnz8zadXWs7fc=
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
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.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

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

@ -1,101 +0,0 @@
package main
import (
"errors"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/sneak/gopoker/pokercore"
"io"
"net/http"
"net/rpc"
"net/rpc/jsonrpc"
"os"
"time"
)
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,10 +0,0 @@
package main
import "fmt"
import "github.com/sneak/gopoker/pokercore"
func main() {
myDeck := pokercore.NewDeck()
myDeck.ShuffleRandomly()
fmt.Println("%s", myDeck.Cards)
}

@ -1,10 +0,0 @@
default: test
.PHONY: pkgs test
fetch:
go get -t
test: *.go
go test -v

@ -1,232 +0,0 @@
package pokercore
import "encoding/binary"
import "fmt"
import crand "crypto/rand"
import . "github.com/logrusorgru/aurora"
import log "github.com/sirupsen/logrus"
import rand "math/rand"
import "strings"
type Suit rune
type Rank rune
const (
CLUB Suit = '\u2663'
SPADE Suit = '\u2660'
DIAMOND Suit = '\u2666'
HEART Suit = '\u2665'
)
/*
// emoji are cooler anyway
const CLUB = "C"
const SPADE = "S"
const DIAMOND = "D"
const HEART = "H"
*/
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 TestGenerationIteration struct {
Deck *Deck
Seed int64
}
type Card struct {
Rank Rank
Suit Suit
}
type Cards []*Card
type Deck struct {
Cards Cards
DealIndex int
ShuffleSeedVal int64
}
func formatCardsForTerminal(c Cards) (output string) {
var cardstrings []string
for i := 0; i < len(c); i++ {
cardstrings = append(cardstrings, formatCardForTerminal(*c[i]))
}
output = strings.Join(cardstrings, ",")
return output
}
func formatCardForTerminal(c Card) (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(Bold(color(c.Rank))))
suit = fmt.Sprintf("%s", BgGray(Bold(color(c.Suit))))
output = fmt.Sprintf("%s%s", rank, suit)
return output
}
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 (self *Card) String() string {
return fmt.Sprintf("%s%s", string(self.Rank), string(self.Suit))
}
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) ShuffleRandomly() {
rnd := rand.New(rand.NewSource(int64(cryptoUint64())))
//FIXME(sneak) not sure if this is constant time or not
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) ShuffleDeterministically(seed int64) {
r := rand.New(rand.NewSource(seed))
//FIXME(sneak) not sure if this is constant time or not
r.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)
}
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 scorePokerHand(input Cards) (score int) {
/*
scoring system:
high card:
high card * 14^4
+ first kicker * 14^3
+ second kicker * 14^2
+ third kicker * 14
+ fourth kicker
max(AKQJ9): 576,011
single pair:
pair value * 1,000,000
+ first kicker * 14^2
+ second kicker * 14
+ third kicker
max(AAKQJ): 14,002,727
two pair:
higher of the two pair value * 100,000,000
+ lower of the two pair value * 14
+ kicker value
max(AAKKQ): 1,300,000,179
trips:
trips value * 1,000,000,000 (min 2,000,000,000)
+ first kicker * 14
+ second kicker
max (AAAKQ): 14,000,000,194
straight:
highest card * 10,000,000,000
straight to the ace: 140,000,000,000
flush:
highest card * 100,000,000,000
min(23457): 700,000,000,000
max(AXXXX): 1,400,000,000,000
boat:
*/
return 1
}
Loading…
Cancel
Save