2019-03-23 09:43:25 +00:00
|
|
|
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"
|
|
|
|
|
2019-03-24 04:12:40 +00:00
|
|
|
type Suit rune
|
|
|
|
type Rank rune
|
|
|
|
|
|
|
|
const (
|
|
|
|
CLUB Suit = '\u2663'
|
|
|
|
SPADE Suit = '\u2660'
|
|
|
|
DIAMOND Suit = '\u2666'
|
|
|
|
HEART Suit = '\u2665'
|
|
|
|
)
|
2019-03-23 09:43:25 +00:00
|
|
|
|
2019-03-23 11:21:03 +00:00
|
|
|
/*
|
2019-03-24 04:12:40 +00:00
|
|
|
// emoji are cooler anyway
|
2019-03-23 09:43:25 +00:00
|
|
|
const CLUB = "C"
|
|
|
|
const SPADE = "S"
|
|
|
|
const DIAMOND = "D"
|
|
|
|
const HEART = "H"
|
2019-03-23 11:21:03 +00:00
|
|
|
*/
|
2019-03-23 09:43:25 +00:00
|
|
|
|
2019-03-24 04:12:40 +00:00
|
|
|
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'
|
|
|
|
)
|
|
|
|
|
2019-03-23 09:43:25 +00:00
|
|
|
type TestGenerationIteration struct {
|
|
|
|
Deck *Deck
|
|
|
|
Seed int64
|
|
|
|
}
|
|
|
|
|
|
|
|
type Card struct {
|
2019-03-24 04:12:40 +00:00
|
|
|
Rank Rank
|
|
|
|
Suit Suit
|
2019-03-23 09:43:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Cards []*Card
|
|
|
|
|
|
|
|
type Deck struct {
|
|
|
|
Cards Cards
|
2019-03-23 11:21:03 +00:00
|
|
|
DealIndex int
|
2019-03-23 09:43:25 +00:00
|
|
|
ShuffleSeedVal int64
|
|
|
|
}
|
|
|
|
|
2019-03-23 11:21:03 +00:00
|
|
|
func formatCardsForTerminal(c Cards) (output string) {
|
|
|
|
var cardstrings []string
|
|
|
|
for i := 0; i < len(c); i++ {
|
|
|
|
cardstrings = append(cardstrings, formatCardForTerminal(*c[i]))
|
2019-03-23 09:43:25 +00:00
|
|
|
}
|
2019-03-23 11:21:03 +00:00
|
|
|
output = strings.Join(cardstrings, ",")
|
|
|
|
return output
|
2019-03-23 09:43:25 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-03-23 11:21:03 +00:00
|
|
|
func formatCardForTerminal(c Card) (output string) {
|
2019-03-23 09:43:25 +00:00
|
|
|
var rank string
|
|
|
|
var suit string
|
|
|
|
color := Red
|
2019-03-23 11:21:03 +00:00
|
|
|
switch c.Suit {
|
2019-03-24 04:12:40 +00:00
|
|
|
case Suit(DIAMOND):
|
2019-03-23 09:43:25 +00:00
|
|
|
color = Blue
|
2019-03-24 04:12:40 +00:00
|
|
|
case Suit(HEART):
|
2019-03-23 09:43:25 +00:00
|
|
|
color = Red
|
2019-03-24 04:12:40 +00:00
|
|
|
case Suit(CLUB):
|
2019-03-23 09:43:25 +00:00
|
|
|
color = Green
|
2019-03-24 04:12:40 +00:00
|
|
|
case Suit(SPADE):
|
2019-03-23 09:43:25 +00:00
|
|
|
color = Black
|
|
|
|
}
|
|
|
|
|
2019-03-23 11:21:03 +00:00
|
|
|
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 {
|
2019-03-24 04:12:40 +00:00
|
|
|
return fmt.Sprintf("%s%s", string(self.Rank), string(self.Suit))
|
2019-03-23 09:43:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewDeck() *Deck {
|
|
|
|
|
|
|
|
self := new(Deck)
|
|
|
|
|
2019-03-24 04:12:40 +00:00
|
|
|
ranks := []Rank{
|
|
|
|
ACE, DEUCE, THREE, FOUR, FIVE,
|
|
|
|
SIX, SEVEN, EIGHT, NINE, TEN, JACK,
|
|
|
|
QUEEN, KING}
|
2019-03-23 09:43:25 +00:00
|
|
|
|
2019-03-24 04:12:40 +00:00
|
|
|
suits := []Suit{HEART, DIAMOND, CLUB, SPADE}
|
2019-03-23 09:43:25 +00:00
|
|
|
|
|
|
|
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++
|
|
|
|
}
|
|
|
|
}
|
2019-03-23 11:21:03 +00:00
|
|
|
self.DealIndex = 0
|
2019-03-23 09:43:25 +00:00
|
|
|
return self
|
|
|
|
}
|
|
|
|
|
2019-03-23 09:57:58 +00:00
|
|
|
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] })
|
2019-03-23 11:21:03 +00:00
|
|
|
self.DealIndex = 0
|
2019-03-23 09:57:58 +00:00
|
|
|
}
|
|
|
|
|
2019-03-23 11:21:03 +00:00
|
|
|
func (self *Deck) ShuffleDeterministically(seed int64) {
|
2019-03-23 09:57:58 +00:00
|
|
|
r := rand.New(rand.NewSource(seed))
|
|
|
|
//FIXME(sneak) not sure if this is constant time or not
|
2019-03-23 09:43:25 +00:00
|
|
|
r.Shuffle(len(self.Cards), func(i, j int) { self.Cards[i], self.Cards[j] = self.Cards[j], self.Cards[i] })
|
2019-03-23 11:21:03 +00:00
|
|
|
self.DealIndex = 0
|
2019-03-23 09:43:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (self *Deck) Deal(n int) (output Cards) {
|
|
|
|
|
2019-03-23 11:21:03 +00:00
|
|
|
if (self.DealIndex + n) > len(self.Cards) {
|
2019-03-23 09:43:25 +00:00
|
|
|
return output
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < n; i++ {
|
2019-03-23 11:21:03 +00:00
|
|
|
output = append(output, self.Cards[self.DealIndex+1])
|
|
|
|
self.DealIndex++
|
2019-03-23 09:43:25 +00:00
|
|
|
}
|
|
|
|
return output
|
|
|
|
}
|
|
|
|
|
2019-03-23 11:21:03 +00:00
|
|
|
func (self *Deck) Dealt() int {
|
|
|
|
return self.DealIndex
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *Deck) Remaining() int {
|
|
|
|
return (len(self.Cards) - self.DealIndex)
|
|
|
|
}
|
|
|
|
|
2019-03-23 09:43:25 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-03-23 09:57:58 +00:00
|
|
|
func generate() {
|
2019-03-23 09:43:25 +00:00
|
|
|
log.SetLevel(log.DebugLevel)
|
2019-03-23 09:57:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func proto() {
|
2019-03-23 09:43:25 +00:00
|
|
|
myDeck := NewDeck()
|
2019-03-23 11:21:03 +00:00
|
|
|
myDeck.ShuffleDeterministically(42)
|
2019-03-23 09:43:25 +00:00
|
|
|
myHand := myDeck.Deal(2)
|
|
|
|
//spew.Dump(myHand)
|
|
|
|
fmt.Printf("my hand: %s\n", myHand)
|
|
|
|
cmty := myDeck.Deal(5)
|
|
|
|
fmt.Printf("community: %s\n", cmty)
|
|
|
|
}
|
2019-03-23 11:21:03 +00:00
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
2019-03-24 03:59:45 +00:00
|
|
|
return 1
|
2019-03-23 11:21:03 +00:00
|
|
|
}
|