Compare commits
	
		
			12 Commits
		
	
	
		
			eb85e1d36e
			...
			f135f480be
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f135f480be | |||
| 0757f12dfa | |||
| 79cf2fd9aa | |||
| f527c6eb07 | |||
| f217a95e19 | |||
| de1eeb214e | |||
| 14ffbe4eb4 | |||
| 5f7dba942c | |||
| 11c5886951 | |||
| 39e2f5a268 | |||
| 5a2560051d | |||
| 336c0d953b | 
							
								
								
									
										42
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -5,9 +5,45 @@
 | 
				
			|||||||
*.so
 | 
					*.so
 | 
				
			||||||
*.dylib
 | 
					*.dylib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Test binary, build with `go test -c`
 | 
					 | 
				
			||||||
*.test
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Output of the go coverage tool, specifically when used with LiteIDE
 | 
					# Output of the go coverage tool, specifically when used with LiteIDE
 | 
				
			||||||
*.out
 | 
					*.out
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Go workspace file
 | 
				
			||||||
 | 
					go.work
 | 
				
			||||||
 | 
					go.work.sum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Dependency directories
 | 
				
			||||||
 | 
					vendor/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Go build artifacts
 | 
				
			||||||
 | 
					bin/
 | 
				
			||||||
 | 
					build/
 | 
				
			||||||
 | 
					dist/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Configuration files for editors and tools
 | 
				
			||||||
 | 
					.vscode/
 | 
				
			||||||
 | 
					.idea/
 | 
				
			||||||
 | 
					*.swp
 | 
				
			||||||
 | 
					*.swo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Logs
 | 
				
			||||||
 | 
					*.log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Temporary files
 | 
				
			||||||
 | 
					*.tmp
 | 
				
			||||||
 | 
					*.temp
 | 
				
			||||||
 | 
					*.bak
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Test binary, coverage, and results
 | 
				
			||||||
 | 
					*.test
 | 
				
			||||||
 | 
					*.cover
 | 
				
			||||||
 | 
					*.cov
 | 
				
			||||||
 | 
					*.profile
 | 
				
			||||||
 | 
					*.prof
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# OS-specific files
 | 
				
			||||||
.DS_Store
 | 
					.DS_Store
 | 
				
			||||||
 | 
					Thumbs.db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# i don't know what keeps creating this file
 | 
				
			||||||
 | 
					examples/simgame/simgame
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										18
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								Makefile
									
									
									
									
									
								
							@ -1,4 +1,18 @@
 | 
				
			|||||||
default: test
 | 
					.PHONY: examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					default: run
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					run: simgame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					simgame: examples
 | 
				
			||||||
 | 
						RANDOM_SHUFFLE=1 ./bin/simgame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					clean:
 | 
				
			||||||
 | 
						rm -rf bin *.test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test:
 | 
					test:
 | 
				
			||||||
	go test -v ./...
 | 
						go test -count=1 .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					examples:
 | 
				
			||||||
 | 
						test -d bin || mkdir bin
 | 
				
			||||||
 | 
						go build -o bin/ ./examples/...
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										97
									
								
								card.go
									
									
									
									
									
								
							
							
						
						
									
										97
									
								
								card.go
									
									
									
									
									
								
							@ -5,6 +5,7 @@ import (
 | 
				
			|||||||
	"slices"
 | 
						"slices"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"unicode/utf8"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/logrusorgru/aurora/v4"
 | 
						"github.com/logrusorgru/aurora/v4"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -14,59 +15,56 @@ type Card struct {
 | 
				
			|||||||
	Suit Suit
 | 
						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) {
 | 
					func NewCardFromString(card string) (Card, error) {
 | 
				
			||||||
	// FIXME extend this later to common format strings like "9s"
 | 
						// FIXME extend this later to common format strings like "9s"
 | 
				
			||||||
	if len(card) != 2 {
 | 
						length := utf8.RuneCountInString(card)
 | 
				
			||||||
 | 
						if length != 2 {
 | 
				
			||||||
		return Card{}, fmt.Errorf("Invalid card string %s", card)
 | 
							return Card{}, fmt.Errorf("Invalid card string %s", card)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	rank := NewRankFromString(card[0:1])
 | 
						var rank Rank
 | 
				
			||||||
	suit := NewSuitFromString(card[1:2])
 | 
						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) {
 | 
						if rank == Rank(0) || suit == Suit(0) {
 | 
				
			||||||
		return Card{}, fmt.Errorf("Invalid card string %s", card)
 | 
							return Card{}, fmt.Errorf("Invalid card string %s", card)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -78,6 +76,7 @@ func NewCardsFromString(cards string) (Cards, error) {
 | 
				
			|||||||
	// FIXME extend this later to common format strings like "9c Qh Qd Kc"
 | 
						// FIXME extend this later to common format strings like "9c Qh Qd Kc"
 | 
				
			||||||
	// with or without commas
 | 
						// with or without commas
 | 
				
			||||||
	var newCards Cards
 | 
						var newCards Cards
 | 
				
			||||||
 | 
						newCards = make(Cards, 0)
 | 
				
			||||||
	cardStrings := strings.Split(cards, ",")
 | 
						cardStrings := strings.Split(cards, ",")
 | 
				
			||||||
	for _, cardString := range cardStrings {
 | 
						for _, cardString := range cardStrings {
 | 
				
			||||||
		card, err := NewCardFromString(cardString)
 | 
							card, err := NewCardFromString(cardString)
 | 
				
			||||||
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										170
									
								
								examples/simgame/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								examples/simgame/main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,170 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"git.eeqj.de/sneak/pokercore"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// just like on tv
 | 
				
			||||||
 | 
					var delayTime = 2 * time.Second
 | 
				
			||||||
 | 
					var playerCount = 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Player struct {
 | 
				
			||||||
 | 
						Hand       pokercore.Cards
 | 
				
			||||||
 | 
						ScoredHand *pokercore.PokerHand
 | 
				
			||||||
 | 
						Position   int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Game struct {
 | 
				
			||||||
 | 
						Deck      *pokercore.Deck
 | 
				
			||||||
 | 
						Players   []*Player
 | 
				
			||||||
 | 
						Community pokercore.Cards
 | 
				
			||||||
 | 
						Street    int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func suspense() {
 | 
				
			||||||
 | 
						fmt.Printf("########################################\n")
 | 
				
			||||||
 | 
						if os.Getenv("NO_LAG") == "" {
 | 
				
			||||||
 | 
							time.Sleep(delayTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewGame() *Game {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// this "randomly chosen" seed somehow deals pocket queens, pocket kings, and pocket aces to three players.
 | 
				
			||||||
 | 
						// what are the odds? lol
 | 
				
			||||||
 | 
						//g := NewGame(1337))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// nothing up my sleeve:
 | 
				
			||||||
 | 
						seed := 3141592653
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g := &Game{}
 | 
				
			||||||
 | 
						g.Street = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g.Deck = pokercore.NewDeck()
 | 
				
			||||||
 | 
						g.SpreadCards()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Printf("Shuffle up and deal!\n")
 | 
				
			||||||
 | 
						suspense()
 | 
				
			||||||
 | 
						fmt.Printf("Shuffling...\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if os.Getenv("RANDOM_SHUFFLE") != "" {
 | 
				
			||||||
 | 
							g.Deck.ShuffleRandomly()
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							fmt.Printf("Using deterministic shuffle seed: %d\n", seed)
 | 
				
			||||||
 | 
							g.Deck.ShuffleDeterministically(int64(seed))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return g
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (g *Game) StreetAsString() string {
 | 
				
			||||||
 | 
						switch g.Street {
 | 
				
			||||||
 | 
						case 0:
 | 
				
			||||||
 | 
							return "pre-flop"
 | 
				
			||||||
 | 
						case 1:
 | 
				
			||||||
 | 
							return "flop"
 | 
				
			||||||
 | 
						case 2:
 | 
				
			||||||
 | 
							return "turn"
 | 
				
			||||||
 | 
						case 3:
 | 
				
			||||||
 | 
							return "river"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "unknown"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (g *Game) SpreadCards() {
 | 
				
			||||||
 | 
						fmt.Printf("deck before shuffling: %s\n", g.Deck.FormatForTerminal())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (g *Game) DealPlayersIn() {
 | 
				
			||||||
 | 
						for i := 0; i < playerCount; i++ {
 | 
				
			||||||
 | 
							p := Player{Hand: g.Deck.Deal(2), Position: i + 1}
 | 
				
			||||||
 | 
							g.Players = append(g.Players, &p)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (g *Game) ShowGameStatus() {
 | 
				
			||||||
 | 
						fmt.Printf("Street: %s\n", g.StreetAsString())
 | 
				
			||||||
 | 
						fmt.Printf("Community cards: %s\n", g.Community.FormatForTerminal())
 | 
				
			||||||
 | 
						for _, p := range g.Players {
 | 
				
			||||||
 | 
							if p != nil {
 | 
				
			||||||
 | 
								fmt.Printf("Player %d: %s\n", p.Position, p.Hand.FormatForTerminal())
 | 
				
			||||||
 | 
								if g.Street > 0 {
 | 
				
			||||||
 | 
									ac := append(p.Hand, g.Community...)
 | 
				
			||||||
 | 
									ph, err := ac.PokerHand()
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										panic(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									fmt.Printf("Player %d has %s\n", p.Position, ph.Description())
 | 
				
			||||||
 | 
									fmt.Printf("Player %d Score: %d\n", p.Position, ph.Score)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (g *Game) DealFlop() {
 | 
				
			||||||
 | 
						g.Community = g.Deck.Deal(3)
 | 
				
			||||||
 | 
						g.Street = 1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (g *Game) DealTurn() {
 | 
				
			||||||
 | 
						g.Community = append(g.Community, g.Deck.Deal(1)...)
 | 
				
			||||||
 | 
						g.Street = 2
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (g *Game) DealRiver() {
 | 
				
			||||||
 | 
						g.Community = append(g.Community, g.Deck.Deal(1)...)
 | 
				
			||||||
 | 
						g.Street = 3
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (g *Game) ShowWinner() {
 | 
				
			||||||
 | 
						var winner *Player
 | 
				
			||||||
 | 
						var winningHand *pokercore.PokerHand
 | 
				
			||||||
 | 
						for _, p := range g.Players {
 | 
				
			||||||
 | 
							if p != nil {
 | 
				
			||||||
 | 
								ac := append(p.Hand, g.Community...)
 | 
				
			||||||
 | 
								ph, err := ac.PokerHand()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									panic(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if winner == nil {
 | 
				
			||||||
 | 
									winner = p
 | 
				
			||||||
 | 
									winningHand = ph
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if ph.Score > winningHand.Score {
 | 
				
			||||||
 | 
										winner = p
 | 
				
			||||||
 | 
										winningHand = ph
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fmt.Printf("Winner: Player %d with %s.\n", winner.Position, winningHand.Description())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						g := NewGame()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g.ShowGameStatus()
 | 
				
			||||||
 | 
						g.DealPlayersIn()
 | 
				
			||||||
 | 
						suspense()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g.ShowGameStatus()
 | 
				
			||||||
 | 
						g.DealFlop()
 | 
				
			||||||
 | 
						suspense()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g.ShowGameStatus()
 | 
				
			||||||
 | 
						g.DealTurn()
 | 
				
			||||||
 | 
						suspense()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g.ShowGameStatus()
 | 
				
			||||||
 | 
						g.DealRiver()
 | 
				
			||||||
 | 
						suspense()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g.ShowGameStatus()
 | 
				
			||||||
 | 
						g.ShowWinner()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Printf("What a strange game.  The only winning move is to bet really big.\n")
 | 
				
			||||||
 | 
						fmt.Printf("Insert coin to play again.\n")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,35 +0,0 @@
 | 
				
			|||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"git.eeqj.de/sneak/pokercore"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func main() {
 | 
					 | 
				
			||||||
	d := pokercore.NewDeck()
 | 
					 | 
				
			||||||
	fmt.Printf("deck before shuffling: %s\n", d.FormatForTerminal())
 | 
					 | 
				
			||||||
	d.ShuffleDeterministically(1337 + 1) // 1337 gives too weird a result
 | 
					 | 
				
			||||||
	fmt.Printf("deck after shuffling: %s\n", d.FormatForTerminal())
 | 
					 | 
				
			||||||
	var players []pokercore.Cards
 | 
					 | 
				
			||||||
	for i := 0; i < 6; i++ {
 | 
					 | 
				
			||||||
		players = append(players, d.Deal(2))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for i, p := range players {
 | 
					 | 
				
			||||||
		fmt.Printf("player %d: %s\n", i, p.FormatForTerminal())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// deal the flop
 | 
					 | 
				
			||||||
	var community pokercore.Cards
 | 
					 | 
				
			||||||
	community = d.Deal(3)
 | 
					 | 
				
			||||||
	fmt.Printf("flop: %s\n", community.FormatForTerminal())
 | 
					 | 
				
			||||||
	// evaluate the hands so far
 | 
					 | 
				
			||||||
	for i, p := range players {
 | 
					 | 
				
			||||||
		fmt.Printf("player %d: %s\n", i, p.FormatForTerminal())
 | 
					 | 
				
			||||||
		thisPlayersHand, err := p.Append(community).PokerHand()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			fmt.Printf("error evaluating hand: %s\n", err)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		fmt.Printf("player %d: %s\n", i, thisPlayersHand.String())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							@ -17,6 +17,14 @@ func (hand Cards) IdentifyBestFiveCardPokerHand() (Cards, error) {
 | 
				
			|||||||
		return nil, ErrDuplicateCard
 | 
							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))
 | 
						newHands := make([]Cards, len(hand))
 | 
				
			||||||
	for i := 0; i < len(hand); i++ {
 | 
						for i := 0; i < len(hand); i++ {
 | 
				
			||||||
		newHand := make(Cards, len(hand)-1)
 | 
							newHand := make(Cards, len(hand)-1)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										23
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								go.mod
									
									
									
									
									
								
							@ -4,14 +4,33 @@ go 1.22.2
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/logrusorgru/aurora v2.0.3+incompatible
 | 
						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/sirupsen/logrus v1.9.3
 | 
				
			||||||
	github.com/stretchr/testify v1.8.0
 | 
						github.com/stretchr/testify v1.8.0
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
 | 
						git.eeqj.de/sneak/timingbench v0.0.0-20240519025145-fb13c5c56a02 // indirect
 | 
				
			||||||
	github.com/davecgh/go-spew v1.1.1 // indirect
 | 
						github.com/davecgh/go-spew v1.1.1 // indirect
 | 
				
			||||||
	github.com/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/k0kubun/pp/v3 v3.2.0 // indirect
 | 
				
			||||||
 | 
						github.com/mattn/go-colorable v0.1.13 // indirect
 | 
				
			||||||
 | 
						github.com/mattn/go-isatty v0.0.20 // indirect
 | 
				
			||||||
 | 
						github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
 | 
				
			||||||
 | 
						github.com/nxadm/tail v1.4.8 // indirect
 | 
				
			||||||
 | 
						github.com/onsi/ginkgo v1.16.5 // indirect
 | 
				
			||||||
 | 
						github.com/onsi/gomega v1.33.1 // indirect
 | 
				
			||||||
	github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
						github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
				
			||||||
	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
 | 
						gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										108
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								go.sum
									
									
									
									
									
								
							@ -1,24 +1,132 @@
 | 
				
			|||||||
 | 
					git.eeqj.de/sneak/timingbench v0.0.0-20240519025145-fb13c5c56a02 h1:b/v1EDAlsfvINIeV4znI/vH7SY7mUJOO1KWeBD+IW90=
 | 
				
			||||||
 | 
					git.eeqj.de/sneak/timingbench v0.0.0-20240519025145-fb13c5c56a02/go.mod h1:iKAlgt/liDtXifmn7fPJK+KYMr0c4lXYFJ+j5d3gfEQ=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
					github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
 | 
					github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
				
			||||||
 | 
					github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 | 
				
			||||||
 | 
					github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 | 
				
			||||||
 | 
					github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
 | 
				
			||||||
 | 
					github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 | 
				
			||||||
 | 
					github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
				
			||||||
 | 
					github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
 | 
				
			||||||
 | 
					github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
 | 
				
			||||||
 | 
					github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
 | 
				
			||||||
 | 
					github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 | 
				
			||||||
 | 
					github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 | 
				
			||||||
 | 
					github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 | 
				
			||||||
 | 
					github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
				
			||||||
 | 
					github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
				
			||||||
 | 
					github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
				
			||||||
 | 
					github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 | 
				
			||||||
 | 
					github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
				
			||||||
 | 
					github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 | 
				
			||||||
 | 
					github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
 | 
				
			||||||
 | 
					github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs=
 | 
				
			||||||
 | 
					github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA=
 | 
				
			||||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
 | 
					github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
 | 
				
			||||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
 | 
					github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
 | 
				
			||||||
github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA=
 | 
					github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA=
 | 
				
			||||||
github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ=
 | 
					github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ=
 | 
				
			||||||
 | 
					github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 | 
				
			||||||
 | 
					github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 | 
				
			||||||
 | 
					github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
				
			||||||
 | 
					github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 | 
				
			||||||
 | 
					github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
				
			||||||
 | 
					github.com/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 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/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 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 | 
				
			||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 | 
					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.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 | 
					github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 | 
				
			||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 | 
					github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 | 
				
			||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
					github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
					github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
 | 
					github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
 | 
				
			||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 | 
					github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 | 
				
			||||||
 | 
					github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
				
			||||||
 | 
					golang.org/x/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 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.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/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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
					gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										125
									
								
								handhelpers.go
									
									
									
									
									
								
							
							
						
						
									
										125
									
								
								handhelpers.go
									
									
									
									
									
								
							@ -1,38 +1,12 @@
 | 
				
			|||||||
package pokercore
 | 
					package pokercore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c Cards) scoreStraightFlush() HandScore {
 | 
					// these helper functions are used in a Cards.PokerHand() constructor and in
 | 
				
			||||||
	if !c.containsStraightFlush() {
 | 
					// the calculateScore() function called from it.
 | 
				
			||||||
		panic("hand must be a straight flush to score it")
 | 
					// they only work with exactly five cards, no duplicates, and no wild cards.
 | 
				
			||||||
	}
 | 
					// they are not intended to be used in any other context, which is why
 | 
				
			||||||
	return ScoreStraightFlush + 1000*c.HighestRank().Score()
 | 
					// they are not exported
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c Cards) scoreFlush() HandScore {
 | 
					 | 
				
			||||||
	if !c.containsFlush() {
 | 
					 | 
				
			||||||
		panic("hand must be a flush to score it")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	var score HandScore
 | 
					 | 
				
			||||||
	for _, card := range c {
 | 
					 | 
				
			||||||
		score += card.Rank.Score()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return ScoreFlush + score
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c Cards) scoreHighCard() HandScore {
 | 
					 | 
				
			||||||
	if !c.isUnmadeHand() {
 | 
					 | 
				
			||||||
		panic("hand must be a high card to score it")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	var score HandScore
 | 
					 | 
				
			||||||
	for _, card := range c {
 | 
					 | 
				
			||||||
		score += card.Rank.Score()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return ScoreHighCard + score
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c Cards) pairRank() Rank {
 | 
					func (c Cards) pairRank() Rank {
 | 
				
			||||||
	if !c.containsPair() {
 | 
					 | 
				
			||||||
		panic("hand must have a pair to have a pair rank")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := c.SortByRankAscending()
 | 
						sorted := c.SortByRankAscending()
 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank {
 | 
						if sorted[0].Rank == sorted[1].Rank {
 | 
				
			||||||
		return sorted[0].Rank
 | 
							return sorted[0].Rank
 | 
				
			||||||
@ -50,9 +24,6 @@ func (c Cards) pairRank() Rank {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c Cards) pairFirstKicker() Card {
 | 
					func (c Cards) pairFirstKicker() Card {
 | 
				
			||||||
	if !c.containsPair() {
 | 
					 | 
				
			||||||
		panic("hand must have a pair to have a first kicker")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := c.SortByRankAscending()
 | 
						sorted := c.SortByRankAscending()
 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank {
 | 
						if sorted[0].Rank == sorted[1].Rank {
 | 
				
			||||||
		return sorted[4]
 | 
							return sorted[4]
 | 
				
			||||||
@ -70,9 +41,6 @@ func (c Cards) pairFirstKicker() Card {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c Cards) pairSecondKicker() Card {
 | 
					func (c Cards) pairSecondKicker() Card {
 | 
				
			||||||
	if !c.containsPair() {
 | 
					 | 
				
			||||||
		panic("hand must have a pair to have a second kicker")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := c.SortByRankAscending()
 | 
						sorted := c.SortByRankAscending()
 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank {
 | 
						if sorted[0].Rank == sorted[1].Rank {
 | 
				
			||||||
		// first kicker is [4]
 | 
							// first kicker is [4]
 | 
				
			||||||
@ -94,9 +62,6 @@ func (c Cards) pairSecondKicker() Card {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c Cards) pairThirdKicker() Card {
 | 
					func (c Cards) pairThirdKicker() Card {
 | 
				
			||||||
	if !c.containsPair() {
 | 
					 | 
				
			||||||
		panic("hand must have a pair to have a third kicker")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := c.SortByRankAscending()
 | 
						sorted := c.SortByRankAscending()
 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank {
 | 
						if sorted[0].Rank == sorted[1].Rank {
 | 
				
			||||||
		// first kicker is [4]
 | 
							// first kicker is [4]
 | 
				
			||||||
@ -122,14 +87,25 @@ func (c Cards) pairThirdKicker() Card {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c Cards) twoPairBiggestPair() Rank {
 | 
					func (c Cards) twoPairBiggestPair() Rank {
 | 
				
			||||||
	if !c.containsTwoPair() {
 | 
					 | 
				
			||||||
		panic("hand must have two pair to have a biggest pair")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := c.SortByRankAscending()
 | 
						sorted := c.SortByRankAscending()
 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
 | 
						if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
 | 
				
			||||||
		return sorted[2].Rank
 | 
							return sorted[2].Rank
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if sorted[0].Rank == sorted[1].Rank && sorted[3].Rank == sorted[4].Rank {
 | 
				
			||||||
 | 
							return sorted[3].Rank
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
 | 
				
			||||||
 | 
							return sorted[3].Rank
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						panic("nope")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c Cards) twoPairSmallestPair() Rank {
 | 
				
			||||||
 | 
						sorted := c.SortByRankAscending()
 | 
				
			||||||
 | 
						if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
 | 
				
			||||||
 | 
							return sorted[0].Rank
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank && sorted[3].Rank == sorted[4].Rank {
 | 
						if sorted[0].Rank == sorted[1].Rank && sorted[3].Rank == sorted[4].Rank {
 | 
				
			||||||
		return sorted[0].Rank
 | 
							return sorted[0].Rank
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -139,27 +115,7 @@ func (c Cards) twoPairBiggestPair() Rank {
 | 
				
			|||||||
	panic("nope")
 | 
						panic("nope")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c Cards) twoPairSmallestPair() Rank {
 | 
					 | 
				
			||||||
	if !c.containsTwoPair() {
 | 
					 | 
				
			||||||
		panic("hand must have two pair to have a smallest pair")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := c.SortByRankAscending()
 | 
					 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
 | 
					 | 
				
			||||||
		return sorted[2].Rank
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank && sorted[3].Rank == sorted[4].Rank {
 | 
					 | 
				
			||||||
		return sorted[3].Rank
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
 | 
					 | 
				
			||||||
		return sorted[3].Rank
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	panic("nope")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c Cards) twoPairKicker() Card {
 | 
					func (c Cards) twoPairKicker() Card {
 | 
				
			||||||
	if !c.containsTwoPair() {
 | 
					 | 
				
			||||||
		panic("hand must have two pair to have a twoPairKicker")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := c.SortByRankAscending()
 | 
						sorted := c.SortByRankAscending()
 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
 | 
						if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
 | 
				
			||||||
		return sorted[4]
 | 
							return sorted[4]
 | 
				
			||||||
@ -174,9 +130,6 @@ func (c Cards) twoPairKicker() Card {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c Cards) threeOfAKindTripsRank() Rank {
 | 
					func (c Cards) threeOfAKindTripsRank() Rank {
 | 
				
			||||||
	if !c.containsThreeOfAKind() {
 | 
					 | 
				
			||||||
		panic("hand must have three of a kind to have a trips rank")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := c.SortByRankAscending()
 | 
						sorted := c.SortByRankAscending()
 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank {
 | 
						if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank {
 | 
				
			||||||
		return sorted[0].Rank
 | 
							return sorted[0].Rank
 | 
				
			||||||
@ -191,9 +144,6 @@ func (c Cards) threeOfAKindTripsRank() Rank {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c Cards) threeOfAKindKickers() Cards {
 | 
					func (c Cards) threeOfAKindKickers() Cards {
 | 
				
			||||||
	if !c.containsThreeOfAKind() {
 | 
					 | 
				
			||||||
		panic("hand must have three of a kind to have kickers")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := c.SortByRankAscending()
 | 
						sorted := c.SortByRankAscending()
 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank {
 | 
						if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank {
 | 
				
			||||||
		return Cards{sorted[3], sorted[4]}
 | 
							return Cards{sorted[3], sorted[4]}
 | 
				
			||||||
@ -284,14 +234,7 @@ func (hand Cards) containsDuplicates() bool {
 | 
				
			|||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hand Cards) IsFiveCardPokerHand() bool {
 | 
					 | 
				
			||||||
	return len(hand) == 5 && !hand.containsDuplicates()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (hand Cards) containsFlush() bool {
 | 
					func (hand Cards) containsFlush() bool {
 | 
				
			||||||
	if !hand.IsFiveCardPokerHand() {
 | 
					 | 
				
			||||||
		panic("hand must have 5 cards to be scored")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	suit := hand[0].Suit
 | 
						suit := hand[0].Suit
 | 
				
			||||||
	for i := 1; i < len(hand); i++ {
 | 
						for i := 1; i < len(hand); i++ {
 | 
				
			||||||
		if hand[i].Suit != suit {
 | 
							if hand[i].Suit != suit {
 | 
				
			||||||
@ -302,9 +245,6 @@ func (hand Cards) containsFlush() bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hand Cards) containsStraight() bool {
 | 
					func (hand Cards) containsStraight() bool {
 | 
				
			||||||
	if !hand.IsFiveCardPokerHand() {
 | 
					 | 
				
			||||||
		panic("hand must have 5 cards to be scored")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := hand.SortByRankAscending()
 | 
						sorted := hand.SortByRankAscending()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if sorted[4].Rank == ACE && sorted[3].Rank == FIVE {
 | 
						if sorted[4].Rank == ACE && sorted[3].Rank == FIVE {
 | 
				
			||||||
@ -322,9 +262,8 @@ func (hand Cards) containsStraight() bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hand Cards) containsStraightFlush() bool {
 | 
					func (hand Cards) containsStraightFlush() bool {
 | 
				
			||||||
	if !hand.IsFiveCardPokerHand() {
 | 
						// this of course only works on five card hands
 | 
				
			||||||
		panic("hand must have 5 cards to be scored")
 | 
						// but these hand helpers are only ever called from a five card hand
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if hand.containsStraight() && hand.containsFlush() {
 | 
						if hand.containsStraight() && hand.containsFlush() {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -332,9 +271,6 @@ func (hand Cards) containsStraightFlush() bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hand Cards) containsRoyalFlush() bool {
 | 
					func (hand Cards) containsRoyalFlush() bool {
 | 
				
			||||||
	if !hand.IsFiveCardPokerHand() {
 | 
					 | 
				
			||||||
		panic("hand must have 5 cards to be scored")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// This seems like it works, but a five-high straight flush is not a royal flush
 | 
						// This seems like it works, but a five-high straight flush is not a royal flush
 | 
				
			||||||
	// and the highest ranked card in five-high straigh flush is an ace.
 | 
						// and the highest ranked card in five-high straigh flush is an ace.
 | 
				
			||||||
	//if hand.containsStraightFlush() && hand.HighestRank() == ACE {
 | 
						//if hand.containsStraightFlush() && hand.HighestRank() == ACE {
 | 
				
			||||||
@ -348,9 +284,6 @@ func (hand Cards) containsRoyalFlush() bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hand Cards) containsFourOfAKind() bool {
 | 
					func (hand Cards) containsFourOfAKind() bool {
 | 
				
			||||||
	if !hand.IsFiveCardPokerHand() {
 | 
					 | 
				
			||||||
		panic("hand must have 5 cards to be scored")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := hand.SortByRankAscending()
 | 
						sorted := hand.SortByRankAscending()
 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
 | 
						if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
 | 
				
			||||||
		// the quads precede the kicker
 | 
							// the quads precede the kicker
 | 
				
			||||||
@ -364,9 +297,6 @@ func (hand Cards) containsFourOfAKind() bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hand Cards) containsFullHouse() bool {
 | 
					func (hand Cards) containsFullHouse() bool {
 | 
				
			||||||
	if !hand.IsFiveCardPokerHand() {
 | 
					 | 
				
			||||||
		panic("hand must have 5 cards to be scored")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := hand.SortByRankAscending()
 | 
						sorted := hand.SortByRankAscending()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
 | 
						if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
 | 
				
			||||||
@ -381,9 +311,6 @@ func (hand Cards) containsFullHouse() bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hand Cards) containsPair() bool {
 | 
					func (hand Cards) containsPair() bool {
 | 
				
			||||||
	if !hand.IsFiveCardPokerHand() {
 | 
					 | 
				
			||||||
		panic("hand must have 5 cards to be scored")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := hand.SortByRankAscending()
 | 
						sorted := hand.SortByRankAscending()
 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank {
 | 
						if sorted[0].Rank == sorted[1].Rank {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
@ -401,9 +328,6 @@ func (hand Cards) containsPair() bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hand Cards) containsThreeOfAKind() bool {
 | 
					func (hand Cards) containsThreeOfAKind() bool {
 | 
				
			||||||
	if !hand.IsFiveCardPokerHand() {
 | 
					 | 
				
			||||||
		panic("hand must have 5 cards to be scored")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := hand.SortByRankAscending()
 | 
						sorted := hand.SortByRankAscending()
 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank {
 | 
						if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
@ -418,9 +342,6 @@ func (hand Cards) containsThreeOfAKind() bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hand Cards) containsTwoPair() bool {
 | 
					func (hand Cards) containsTwoPair() bool {
 | 
				
			||||||
	if !hand.IsFiveCardPokerHand() {
 | 
					 | 
				
			||||||
		panic("hand must have 5 cards to be scored")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sorted := hand.SortByRankAscending()
 | 
						sorted := hand.SortByRankAscending()
 | 
				
			||||||
	if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
 | 
						if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
@ -435,8 +356,6 @@ func (hand Cards) containsTwoPair() bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hand Cards) isUnmadeHand() bool {
 | 
					func (hand Cards) isUnmadeHand() bool {
 | 
				
			||||||
	if !hand.IsFiveCardPokerHand() {
 | 
						// i suspect this is expensive but we use it only in tests
 | 
				
			||||||
		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()
 | 
						return !hand.containsPair() && !hand.containsTwoPair() && !hand.containsThreeOfAKind() && !hand.containsStraight() && !hand.containsFlush() && !hand.containsFullHouse() && !hand.containsFourOfAKind() && !hand.containsStraightFlush() && !hand.containsRoyalFlush()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										79
									
								
								perf_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								perf_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					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)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						t.Logf("Searching %d random cards for a hand takes on average %s", cardCount, result.Mean)
 | 
				
			||||||
 | 
						t.Logf("Over %d iterations the min was %s and the max was %s", iterations, result.Min, result.Max)
 | 
				
			||||||
 | 
						t.Logf("The standard deviation was %s", result.StdDev)
 | 
				
			||||||
 | 
						t.Logf("The median was %s", result.Median)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 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")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
package pokercore
 | 
					package pokercore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
@ -14,13 +13,13 @@ type ShuffleTestResults []struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestPokerDeck(t *testing.T) {
 | 
					func TestPokerDeck(t *testing.T) {
 | 
				
			||||||
	d := NewDeck()
 | 
						d := NewDeck()
 | 
				
			||||||
	fmt.Printf("newdeck: %+v\n", d)
 | 
						//fmt.Printf("newdeck: %+v\n", d)
 | 
				
			||||||
	d.ShuffleDeterministically(437)
 | 
						d.ShuffleDeterministically(437)
 | 
				
			||||||
	fmt.Printf("deterministically shuffled deck: %+v\n", d)
 | 
						//fmt.Printf("deterministically shuffled deck: %+v\n", d)
 | 
				
			||||||
	cards := d.Deal(7)
 | 
						cards := d.Deal(7)
 | 
				
			||||||
	expected := "6♥,A♦,7♥,9♣,6♠,9♥,8♣"
 | 
						expected := "6♥,A♦,7♥,9♣,6♠,9♥,8♣"
 | 
				
			||||||
	fmt.Printf("deterministically shuffled deck after dealing: %+v\n", d)
 | 
						//fmt.Printf("deterministically shuffled deck after dealing: %+v\n", d)
 | 
				
			||||||
	fmt.Printf("cards: %+v\n", cards)
 | 
						//fmt.Printf("cards: %+v\n", cards)
 | 
				
			||||||
	assert.Equal(t, expected, cards.String())
 | 
						assert.Equal(t, expected, cards.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	x := d.Count()
 | 
						x := d.Count()
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										128
									
								
								pokerhand.go
									
									
									
									
									
								
							
							
						
						
									
										128
									
								
								pokerhand.go
									
									
									
									
									
								
							@ -1,6 +1,9 @@
 | 
				
			|||||||
package pokercore
 | 
					package pokercore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "fmt"
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type PokerHandType int
 | 
					type PokerHandType int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -28,99 +31,41 @@ func (c Cards) Append(other Cards) Cards {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c Cards) PokerHand() (*PokerHand, error) {
 | 
					func (c Cards) PokerHand() (*PokerHand, error) {
 | 
				
			||||||
	if len(c) != 5 {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("hand must have 5 cards to be scored as a poker hand")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if c.containsDuplicates() {
 | 
						if c.containsDuplicates() {
 | 
				
			||||||
		return nil, fmt.Errorf("hand must have no duplicates to be scored as a poker hand")
 | 
							return nil, errors.New("hand must have no duplicates to be scored as a poker hand")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ph := new(PokerHand)
 | 
						ph := new(PokerHand)
 | 
				
			||||||
	ph.Hand = c
 | 
					
 | 
				
			||||||
	if c.containsRoyalFlush() {
 | 
						// IdentifyBestFiveCardPokerHand() calls us to score hands
 | 
				
			||||||
		ph.Type = RoyalFlush
 | 
						// but only if the hand is 5 cards exactly which avoids recursion loop
 | 
				
			||||||
		ph.Score = ScoreRoyalFlush
 | 
						if len(c) > 5 {
 | 
				
			||||||
		return ph, nil
 | 
							phc, err := c.IdentifyBestFiveCardPokerHand()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								// this should in theory never happen
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							ph.Hand = phc.SortByRankAscending()
 | 
				
			||||||
	if c.containsStraightFlush() {
 | 
						} else if len(c) == 5 {
 | 
				
			||||||
		ph.Type = StraightFlush
 | 
							ph.Hand = c.SortByRankAscending()
 | 
				
			||||||
		ph.Score = ph.Hand.scoreStraightFlush()
 | 
					 | 
				
			||||||
		return ph, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if c.containsFourOfAKind() {
 | 
					 | 
				
			||||||
		ph.Type = FourOfAKind
 | 
					 | 
				
			||||||
		ph.Score = ScoreFourOfAKind + 1000*c.fourOfAKindRank().Score() + c.fourOfAKindKicker().Score()
 | 
					 | 
				
			||||||
		return ph, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if c.containsFullHouse() {
 | 
					 | 
				
			||||||
		ph.Type = FullHouse
 | 
					 | 
				
			||||||
		ph.Score = ScoreFullHouse + 1000*c.fullHouseTripsRank().Score() + c.fullHousePairRank().Score()
 | 
					 | 
				
			||||||
		return ph, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if c.containsFlush() {
 | 
					 | 
				
			||||||
		ph.Type = Flush
 | 
					 | 
				
			||||||
		ph.Score = c.scoreFlush()
 | 
					 | 
				
			||||||
		return ph, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if c.containsStraight() {
 | 
					 | 
				
			||||||
		ph.Type = Straight
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Straights are scored by the highest card in the straight
 | 
					 | 
				
			||||||
		// UNLESS the second highest card is a 5 and the highest card is an Ace
 | 
					 | 
				
			||||||
		// In that case, the straight is a 5-high straight, not an Ace-high straight
 | 
					 | 
				
			||||||
		sorted := c.SortByRankAscending()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if sorted[3].Rank == FIVE && sorted[4].Rank == ACE {
 | 
					 | 
				
			||||||
			// 5-high straight
 | 
					 | 
				
			||||||
			ph.Score = ScoreStraight + 1000*sorted[3].Score()
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
			// All other straights
 | 
							return nil, errors.New("hand must have at least 5 cards to be scored as a poker hand")
 | 
				
			||||||
			ph.Score = ScoreStraight + 1000*sorted[4].Score()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return ph, nil
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c.containsThreeOfAKind() {
 | 
						// this doesn't return an error because we've already checked for
 | 
				
			||||||
		ph.Type = ThreeOfAKind
 | 
						// duplicates and we have already identified the best 5-card hand
 | 
				
			||||||
		ph.Score = ScoreThreeOfAKind
 | 
					 | 
				
			||||||
		ph.Score += 1000 * c.threeOfAKindTripsRank().Score()
 | 
					 | 
				
			||||||
		ph.Score += 100 * c.threeOfAKindFirstKicker().Score()
 | 
					 | 
				
			||||||
		ph.Score += 10 * c.threeOfAKindSecondKicker().Score()
 | 
					 | 
				
			||||||
		return ph, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c.containsTwoPair() {
 | 
						// this code used to be here, but got factored out so it can go in
 | 
				
			||||||
		ph.Type = TwoPair
 | 
						// scoring.go
 | 
				
			||||||
		ph.Score = ScoreTwoPair
 | 
						ph.calculateScore()
 | 
				
			||||||
		ph.Score += 1000 * c.twoPairBiggestPair().Score()
 | 
					 | 
				
			||||||
		ph.Score += 100 * c.twoPairSmallestPair().Score()
 | 
					 | 
				
			||||||
		ph.Score += 10 * c.twoPairKicker().Score()
 | 
					 | 
				
			||||||
		return ph, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c.containsPair() {
 | 
					 | 
				
			||||||
		ph.Type = Pair
 | 
					 | 
				
			||||||
		ph.Score = ScorePair
 | 
					 | 
				
			||||||
		ph.Score += 1000 * c.pairRank().Score()
 | 
					 | 
				
			||||||
		ph.Score += 100 * c.pairFirstKicker().Score()
 | 
					 | 
				
			||||||
		ph.Score += 10 * c.pairSecondKicker().Score()
 | 
					 | 
				
			||||||
		ph.Score += c.pairThirdKicker().Score()
 | 
					 | 
				
			||||||
		return ph, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ph.Type = HighCard
 | 
					 | 
				
			||||||
	ph.Score = c.scoreHighCard()
 | 
					 | 
				
			||||||
	return ph, nil
 | 
						return ph, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ph PokerHand) ToSortedCards() Cards {
 | 
					func (ph PokerHand) ToSortedCards() Cards {
 | 
				
			||||||
	sorted := ph.Hand.SortByRankAscending()
 | 
						// I believe ph.Hand is already sorted, but in any case it's only 5
 | 
				
			||||||
	return sorted
 | 
						// cards and sorting it costs ~nothing
 | 
				
			||||||
 | 
						return ph.Hand.SortByRankAscending()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ph PokerHand) Compare(other PokerHand) int {
 | 
					func (ph PokerHand) Compare(other PokerHand) int {
 | 
				
			||||||
@ -142,16 +87,15 @@ func (ph PokerHand) String() string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c PokerHand) Description() string {
 | 
					func (c PokerHand) Description() string {
 | 
				
			||||||
	sortedHand := c.Hand.SortByRankAscending()
 | 
					 | 
				
			||||||
	if c.Type == RoyalFlush {
 | 
						if c.Type == RoyalFlush {
 | 
				
			||||||
		return fmt.Sprintf("a royal flush in %s", c.Hand[0].Suit)
 | 
							return fmt.Sprintf("a royal flush in %s", c.Hand[0].Suit)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if c.Hand.containsStraightFlush() {
 | 
						if c.Hand.containsStraightFlush() {
 | 
				
			||||||
		if sortedHand[3].Rank == FIVE && sortedHand[4].Rank == ACE {
 | 
							if c.Hand[3].Rank == FIVE && c.Hand[4].Rank == ACE {
 | 
				
			||||||
			// special case for steel wheel
 | 
								// special case for steel wheel
 | 
				
			||||||
			return fmt.Sprintf("%s high straight flush in %s", sortedHand[3].Rank.WithArticle(), sortedHand[4].Suit)
 | 
								return fmt.Sprintf("%s high straight flush in %s", c.Hand[3].Rank.WithArticle(), c.Hand[4].Suit)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return fmt.Sprintf("%s high straight flush in %s", c.HighestRank().WithArticle(), sortedHand[4].Suit)
 | 
							return fmt.Sprintf("%s high straight flush in %s", c.HighestRank().WithArticle(), c.Hand[4].Suit)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if c.Hand.containsFourOfAKind() {
 | 
						if c.Hand.containsFourOfAKind() {
 | 
				
			||||||
		return fmt.Sprintf("four %s with %s", c.Hand.fourOfAKindRank().Pluralize(), c.Hand.fourOfAKindKicker().Rank.WithArticle())
 | 
							return fmt.Sprintf("four %s with %s", c.Hand.fourOfAKindRank().Pluralize(), c.Hand.fourOfAKindKicker().Rank.WithArticle())
 | 
				
			||||||
@ -160,12 +104,12 @@ func (c PokerHand) Description() string {
 | 
				
			|||||||
		return fmt.Sprintf("a full house, %s full of %s", c.Hand.fullHouseTripsRank().Pluralize(), c.Hand.fullHousePairRank().Pluralize())
 | 
							return fmt.Sprintf("a full house, %s full of %s", c.Hand.fullHouseTripsRank().Pluralize(), c.Hand.fullHousePairRank().Pluralize())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if c.Hand.containsFlush() {
 | 
						if c.Hand.containsFlush() {
 | 
				
			||||||
		return fmt.Sprintf("%s high flush in %s", c.HighestRank().WithArticle(), sortedHand[4].Suit)
 | 
							return fmt.Sprintf("%s high flush in %s", c.HighestRank().WithArticle(), c.Hand[4].Suit)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if c.Hand.containsStraight() {
 | 
						if c.Hand.containsStraight() {
 | 
				
			||||||
		if sortedHand[3].Rank == FIVE && sortedHand[4].Rank == ACE {
 | 
							if c.Hand[3].Rank == FIVE && c.Hand[4].Rank == ACE {
 | 
				
			||||||
			// special case for wheel straight
 | 
								// special case for wheel straight
 | 
				
			||||||
			return fmt.Sprintf("%s high straight", sortedHand[3].Rank.WithArticle())
 | 
								return fmt.Sprintf("%s high straight", c.Hand[3].Rank.WithArticle())
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return fmt.Sprintf("%s high straight", c.HighestRank().WithArticle())
 | 
							return fmt.Sprintf("%s high straight", c.HighestRank().WithArticle())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -198,10 +142,10 @@ func (c PokerHand) Description() string {
 | 
				
			|||||||
	return fmt.Sprintf(
 | 
						return fmt.Sprintf(
 | 
				
			||||||
		// "ace high with an eight, a seven, a six, and a deuce"
 | 
							// "ace high with an eight, a seven, a six, and a deuce"
 | 
				
			||||||
		"%s high with %s, %s, %s, and %s",
 | 
							"%s high with %s, %s, %s, and %s",
 | 
				
			||||||
		sortedHand[4].Rank,
 | 
							c.Hand[4].Rank,
 | 
				
			||||||
		sortedHand[3].Rank.WithArticle(),
 | 
							c.Hand[3].Rank.WithArticle(),
 | 
				
			||||||
		sortedHand[2].Rank.WithArticle(),
 | 
							c.Hand[2].Rank.WithArticle(),
 | 
				
			||||||
		sortedHand[1].Rank.WithArticle(),
 | 
							c.Hand[1].Rank.WithArticle(),
 | 
				
			||||||
		sortedHand[0].Rank.WithArticle(),
 | 
							c.Hand[0].Rank.WithArticle(),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										121
									
								
								scoring.go
									
									
									
									
									
								
							
							
						
						
									
										121
									
								
								scoring.go
									
									
									
									
									
								
							@ -1,6 +1,8 @@
 | 
				
			|||||||
package pokercore
 | 
					package pokercore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "fmt"
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type HandScore int
 | 
					type HandScore int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -22,18 +24,123 @@ func (c Card) Score() HandScore {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c Cards) PokerHandScore() (HandScore, error) {
 | 
					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")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ph, err := c.PokerHand()
 | 
						ph, err := c.PokerHand()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, err
 | 
							return 0, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	//fmt.Println(ph)
 | 
					 | 
				
			||||||
	return ph.Score, nil
 | 
						return ph.Score, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (x HandScore) String() string {
 | 
					func (x HandScore) String() string {
 | 
				
			||||||
	return fmt.Sprintf("<HandScore %d>", x)
 | 
						return fmt.Sprintf("<HandScore %d>", x)
 | 
				
			||||||
	//return scoreToName(x)
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ph *PokerHand) calculateScore() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// sanity check, we should only be called in the PokerHand() method from
 | 
				
			||||||
 | 
						// a Cards, but just in case
 | 
				
			||||||
 | 
						if len(ph.Hand) != 5 {
 | 
				
			||||||
 | 
							// normally we don't panic in a library but this is a "should never
 | 
				
			||||||
 | 
							// happen"
 | 
				
			||||||
 | 
							panic("PokerHand.calculateScore() called on a PokerHand with != 5 cards")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ph.Hand.containsRoyalFlush() {
 | 
				
			||||||
 | 
							ph.Type = RoyalFlush
 | 
				
			||||||
 | 
							ph.Score = ScoreRoyalFlush
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ph.Hand.containsStraightFlush() {
 | 
				
			||||||
 | 
							ph.Type = StraightFlush
 | 
				
			||||||
 | 
							ph.Score = ScoreStraightFlush
 | 
				
			||||||
 | 
							ph.Score += ph.Hand.HighestRank().Score()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ph.Hand.containsFourOfAKind() {
 | 
				
			||||||
 | 
							ph.Type = FourOfAKind
 | 
				
			||||||
 | 
							ph.Score = ScoreFourOfAKind
 | 
				
			||||||
 | 
							ph.Score += 10000 * ph.Hand.fourOfAKindRank().Score()
 | 
				
			||||||
 | 
							ph.Score += 100 * ph.Hand.fourOfAKindKicker().Score()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ph.Hand.containsFullHouse() {
 | 
				
			||||||
 | 
							ph.Type = FullHouse
 | 
				
			||||||
 | 
							ph.Score = ScoreFullHouse
 | 
				
			||||||
 | 
							ph.Score += 10000 * ph.Hand.fullHouseTripsRank().Score()
 | 
				
			||||||
 | 
							ph.Score += 100 * ph.Hand.fullHousePairRank().Score()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ph.Hand.containsFlush() {
 | 
				
			||||||
 | 
							ph.Type = Flush
 | 
				
			||||||
 | 
							ph.Score = ScoreFlush
 | 
				
			||||||
 | 
							// flush base score plus sum of card ranks
 | 
				
			||||||
 | 
							ph.Score += ph.Hand[0].Score()
 | 
				
			||||||
 | 
							ph.Score += ph.Hand[1].Score()
 | 
				
			||||||
 | 
							ph.Score += ph.Hand[2].Score()
 | 
				
			||||||
 | 
							ph.Score += ph.Hand[3].Score()
 | 
				
			||||||
 | 
							ph.Score += ph.Hand[4].Score()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ph.Hand.containsStraight() {
 | 
				
			||||||
 | 
							ph.Type = Straight
 | 
				
			||||||
 | 
							ph.Score = ScoreStraight
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// note that ph.Hand is already sorted by rank ascending with ace
 | 
				
			||||||
 | 
							// high
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Straights are scored by the highest card in the straight
 | 
				
			||||||
 | 
							// UNLESS the second highest card is a 5 and the highest card is an Ace
 | 
				
			||||||
 | 
							// In that case, the straight is a 5-high straight, not an Ace-high straight
 | 
				
			||||||
 | 
							if ph.Hand[3].Rank == FIVE && ph.Hand[4].Rank == ACE {
 | 
				
			||||||
 | 
								// 5-high straight, scored by the five's rank
 | 
				
			||||||
 | 
								ph.Score += ph.Hand[3].Score()
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// All other straights are scored by the highest card in the straight
 | 
				
			||||||
 | 
								ph.Score += ph.Hand[4].Score()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ph.Hand.containsThreeOfAKind() {
 | 
				
			||||||
 | 
							ph.Type = ThreeOfAKind
 | 
				
			||||||
 | 
							ph.Score = ScoreThreeOfAKind
 | 
				
			||||||
 | 
							ph.Score += 10000 * ph.Hand.threeOfAKindTripsRank().Score()
 | 
				
			||||||
 | 
							ph.Score += 100 * ph.Hand.threeOfAKindFirstKicker().Score()
 | 
				
			||||||
 | 
							ph.Score += ph.Hand.threeOfAKindSecondKicker().Score()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ph.Hand.containsTwoPair() {
 | 
				
			||||||
 | 
							ph.Type = TwoPair
 | 
				
			||||||
 | 
							ph.Score = ScoreTwoPair
 | 
				
			||||||
 | 
							ph.Score += 10000 * ph.Hand.twoPairBiggestPair().Score()
 | 
				
			||||||
 | 
							ph.Score += 100 * ph.Hand.twoPairSmallestPair().Score()
 | 
				
			||||||
 | 
							ph.Score += ph.Hand.twoPairKicker().Score()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ph.Hand.containsPair() {
 | 
				
			||||||
 | 
							ph.Type = Pair
 | 
				
			||||||
 | 
							ph.Score = ScorePair
 | 
				
			||||||
 | 
							ph.Score += 10000 * ph.Hand.pairRank().Score()
 | 
				
			||||||
 | 
							ph.Score += 100 * ph.Hand.pairFirstKicker().Score()
 | 
				
			||||||
 | 
							ph.Score += ph.Hand.pairSecondKicker().Score()
 | 
				
			||||||
 | 
							ph.Score += ph.Hand.pairThirdKicker().Score()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ph.Type = HighCard
 | 
				
			||||||
 | 
						ph.Score = ScoreHighCard // base score
 | 
				
			||||||
 | 
						// unmade hands are scored like flushes, just add up the values
 | 
				
			||||||
 | 
						ph.Score += ph.Hand[0].Score()
 | 
				
			||||||
 | 
						ph.Score += ph.Hand[1].Score()
 | 
				
			||||||
 | 
						ph.Score += ph.Hand[2].Score()
 | 
				
			||||||
 | 
						ph.Score += ph.Hand[3].Score()
 | 
				
			||||||
 | 
						ph.Score += ph.Hand[4].Score()
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										217
									
								
								scoring_test.go
									
									
									
									
									
								
							
							
						
						
									
										217
									
								
								scoring_test.go
									
									
									
									
									
								
							@ -1,12 +1,72 @@
 | 
				
			|||||||
package pokercore
 | 
					package pokercore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestHandDescripionBug(t *testing.T) {
 | 
				
			||||||
 | 
						playerCount := 8
 | 
				
			||||||
 | 
						d := NewDeck()
 | 
				
			||||||
 | 
						d.ShuffleDeterministically(1337)
 | 
				
			||||||
 | 
						var players []*Cards
 | 
				
			||||||
 | 
						players = make([]*Cards, playerCount)
 | 
				
			||||||
 | 
						for i := 1; i-1 < playerCount; i++ {
 | 
				
			||||||
 | 
							c := d.Deal(2)
 | 
				
			||||||
 | 
							players[i-1] = &c
 | 
				
			||||||
 | 
							t.Logf("Player %d dealt: %+v\n", i, c)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						t.Logf("Players: %+v\n", players)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						community := d.Deal(5)
 | 
				
			||||||
 | 
						t.Logf("Community: %+v\n", community)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var playerResults []*PokerHand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := 1; i-1 < playerCount; i++ {
 | 
				
			||||||
 | 
							t.Logf("Player %d hole cards: %+v\n", i, *players[i-1])
 | 
				
			||||||
 | 
							pc := append(*players[i-1], community...)
 | 
				
			||||||
 | 
							t.Logf("Player %d cards available: %+v\n", i, pc)
 | 
				
			||||||
 | 
							hand, err := pc.IdentifyBestFiveCardPokerHand()
 | 
				
			||||||
 | 
							assert.Nil(t, err, "Expected no error")
 | 
				
			||||||
 | 
							ph, err := hand.PokerHand()
 | 
				
			||||||
 | 
							assert.Nil(t, err, "Expected no error")
 | 
				
			||||||
 | 
							t.Logf("Player %d five cards used: %+v\n", i, hand)
 | 
				
			||||||
 | 
							t.Logf("Player %d poker hand: %+v\n", i, ph)
 | 
				
			||||||
 | 
							t.Logf("Player %d best hand description: %s\n", i, ph.Description())
 | 
				
			||||||
 | 
							playerResults = append(playerResults, ph)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						weirdOne := playerResults[7]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Logf("Weird one: %v\n", weirdOne)
 | 
				
			||||||
 | 
						t.Logf("Weird one description: %s\n", weirdOne.Description())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// T♠,7♠,9♦,7♣,T♥
 | 
				
			||||||
 | 
						assert.Equal(t, weirdOne.Description(), "two pair, tens and sevens with a nine")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scoreShouldBe := ScoreTwoPair
 | 
				
			||||||
 | 
						scoreShouldBe += 10000 * TEN.Score()
 | 
				
			||||||
 | 
						scoreShouldBe += 100 * SEVEN.Score()
 | 
				
			||||||
 | 
						scoreShouldBe += NINE.Score()
 | 
				
			||||||
 | 
						assert.Equal(t, weirdOne.Score, scoreShouldBe)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cards := weirdOne.Hand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.True(t, cards.containsTwoPair(), "Expected hand to be two pair")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bp := cards.twoPairBiggestPair() // returns Rank, because describing a pair
 | 
				
			||||||
 | 
						assert.Equal(t, bp, TEN, "Expected biggest pair to be a ten")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sp := cards.twoPairSmallestPair() // returns Rank, because describing a pair
 | 
				
			||||||
 | 
						assert.Equal(t, sp, SEVEN, "Expected smallest pair to be a seven")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						k := cards.twoPairKicker() // returns Card, because describing a single card
 | 
				
			||||||
 | 
						assert.Equal(t, k.Rank, NINE, "Expected kicker to be a nine")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestAceLowStraight(t *testing.T) {
 | 
					func TestAceLowStraight(t *testing.T) {
 | 
				
			||||||
	hand := Cards{
 | 
						hand := Cards{
 | 
				
			||||||
		AceOfSpades(),
 | 
							AceOfSpades(),
 | 
				
			||||||
@ -18,17 +78,18 @@ func TestAceLowStraight(t *testing.T) {
 | 
				
			|||||||
	assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
 | 
						assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
 | 
				
			||||||
	ph, err := hand.PokerHand()
 | 
						ph, err := hand.PokerHand()
 | 
				
			||||||
	assert.Nil(t, err, "Expected no error")
 | 
						assert.Nil(t, err, "Expected no error")
 | 
				
			||||||
	assert.Greater(t, ph.Score, 0, "Expected score to be nonzero 0")
 | 
						assert.Greater(t, ph.Score, 0, "Expected score to be greater than 0")
 | 
				
			||||||
	assert.Less(t, ph.Score, 100000000000000000, "Expected score to be less than 100000000000000000")
 | 
						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+FIVE.Score())
 | 
				
			||||||
	assert.Equal(t, ph.Description(), "a five high straight")
 | 
						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.HighestRank() == ACE, "Expected highest rank to be an ace")
 | 
				
			||||||
	assert.True(t, hand.SortByRankAscending().First().Rank == DEUCE, "Expected first card to be a deuce")
 | 
						s := hand.SortByRankAscending()
 | 
				
			||||||
	assert.True(t, hand.SortByRankAscending().Last().Rank == ACE, "Expected last card in sorted to be a ace")
 | 
						assert.True(t, s.First().Rank == DEUCE, "Expected first card to be a deuce")
 | 
				
			||||||
	assert.True(t, hand.SortByRankAscending().Second().Rank == THREE, "Expected second card to be a three")
 | 
						assert.True(t, s.Last().Rank == ACE, "Expected last card in sorted to be a ace")
 | 
				
			||||||
	assert.True(t, hand.SortByRankAscending().Third().Rank == FOUR, "Expected third card to be a four")
 | 
						assert.True(t, s.Second().Rank == THREE, "Expected second card to be a three")
 | 
				
			||||||
	assert.True(t, hand.SortByRankAscending().Fourth().Rank == FIVE, "Expected fourth card to be a five")
 | 
						assert.True(t, s.Third().Rank == FOUR, "Expected third card to be a four")
 | 
				
			||||||
	assert.True(t, hand.SortByRankAscending().Fifth().Rank == ACE, "Expected fifth card to be an ace")
 | 
						assert.True(t, s.Fourth().Rank == FIVE, "Expected fourth card to be a five")
 | 
				
			||||||
 | 
						assert.True(t, s.Fifth().Rank == ACE, "Expected fifth card to be an ace")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestAceHighStraight(t *testing.T) {
 | 
					func TestAceHighStraight(t *testing.T) {
 | 
				
			||||||
@ -66,12 +127,14 @@ func TestOtherStraight(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	newDeck := NewDeckFromCards(hand)
 | 
						newDeck := NewDeckFromCards(hand)
 | 
				
			||||||
	newDeck.ShuffleDeterministically(123456789)
 | 
						newDeck.ShuffleDeterministically(123456789)
 | 
				
			||||||
	fmt.Printf("Shuffled deck: %s\n", newDeck.String())
 | 
						//fmt.Printf("Shuffled deck: %s\n", newDeck.String())
 | 
				
			||||||
	fmt.Printf("new deck has %d cards\n", newDeck.Count())
 | 
						//fmt.Printf("new deck has %d cards\n", newDeck.Count())
 | 
				
			||||||
	shuffledHand := newDeck.Deal(5)
 | 
						shuffledHand := newDeck.Deal(5)
 | 
				
			||||||
	assert.True(t, shuffledHand.containsStraight(), "Expected hand to still be a straight after shuffle")
 | 
						assert.True(t, shuffledHand.containsStraight(), "Expected hand to still be a straight after shuffle")
 | 
				
			||||||
	assert.False(t, shuffledHand.containsTwoPair(), "Expected hand to not be two pair")
 | 
						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) {
 | 
					func TestFlush(t *testing.T) {
 | 
				
			||||||
@ -85,21 +148,24 @@ func TestFlush(t *testing.T) {
 | 
				
			|||||||
	assert.True(t, hand.containsFlush(), "Expected hand to be a flush")
 | 
						assert.True(t, hand.containsFlush(), "Expected hand to be a flush")
 | 
				
			||||||
	newDeck := NewDeckFromCards(hand)
 | 
						newDeck := NewDeckFromCards(hand)
 | 
				
			||||||
	newDeck.ShuffleDeterministically(123456789)
 | 
						newDeck.ShuffleDeterministically(123456789)
 | 
				
			||||||
	fmt.Printf("Shuffled deck: %s\n", newDeck.String())
 | 
						//fmt.Printf("Shuffled deck: %s\n", newDeck.String())
 | 
				
			||||||
	fmt.Printf("new deck has %d cards\n", newDeck.Count())
 | 
						//fmt.Printf("new deck has %d cards\n", newDeck.Count())
 | 
				
			||||||
	shuffledHand := newDeck.Deal(5)
 | 
						shuffledHand := newDeck.Deal(5)
 | 
				
			||||||
	assert.True(t, shuffledHand.containsFlush(), "Expected hand to still be a flush after shuffle")
 | 
						assert.True(t, shuffledHand.containsFlush(), "Expected hand to still be a flush after shuffle")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// flush value is the sum of the ranks, just like high card
 | 
				
			||||||
	x := ScoreFlush
 | 
						x := ScoreFlush
 | 
				
			||||||
	var multiplier HandScore = 100
 | 
						x += ACE.Score()
 | 
				
			||||||
	x += multiplier * DEUCE.Score()
 | 
						x += DEUCE.Score()
 | 
				
			||||||
	multiplier *= 100
 | 
						x += THREE.Score()
 | 
				
			||||||
	x += multiplier * THREE.Score()
 | 
						x += FOUR.Score()
 | 
				
			||||||
	multiplier *= 100
 | 
						x += SIX.Score()
 | 
				
			||||||
	x += multiplier * FOUR.Score()
 | 
						//fmt.Printf("a-2-3-4-6 flush score should be: %d\n", x)
 | 
				
			||||||
	multiplier *= 100
 | 
					
 | 
				
			||||||
	x += multiplier * SIX.Score()
 | 
						ph, err := shuffledHand.PokerHand()
 | 
				
			||||||
	fmt.Printf("a-2-3-4-6 flush score should be: %d\n", x)
 | 
						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) {
 | 
					func TestStraightFlush(t *testing.T) {
 | 
				
			||||||
@ -124,14 +190,43 @@ func TestStraightFlush(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	nd := NewDeckFromCards(hand)
 | 
						nd := NewDeckFromCards(hand)
 | 
				
			||||||
	nd.ShuffleDeterministically(123456789)
 | 
						nd.ShuffleDeterministically(123456789)
 | 
				
			||||||
	fmt.Printf("Shuffled deck: %s\n", nd.String())
 | 
						//fmt.Printf("Shuffled deck: %s\n", nd.String())
 | 
				
			||||||
	fmt.Printf("new deck has %d cards\n", nd.Count())
 | 
						//fmt.Printf("new deck has %d cards\n", nd.Count())
 | 
				
			||||||
	shuffledHand := nd.Deal(5)
 | 
						shuffledHand := nd.Deal(5)
 | 
				
			||||||
	assert.True(t, shuffledHand.containsStraightFlush(), "Expected hand to still be a straight flush after shuffle")
 | 
						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")
 | 
						assert.True(t, shuffledHand.HighestRank() == SIX, "Expected highest rank to still be a six after shuffle")
 | 
				
			||||||
	assert.True(t, shuffledHand.HighestRank() == SIX, "Expected highest rank to be a six after shuffle even with aces low")
 | 
						assert.True(t, shuffledHand.HighestRank() == SIX, "Expected highest rank to be a six after shuffle even with aces low")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestFourOfAKind(t *testing.T) {
 | 
				
			||||||
 | 
						hand := Cards{
 | 
				
			||||||
 | 
							SixOfSpades(),
 | 
				
			||||||
 | 
							SixOfHearts(),
 | 
				
			||||||
 | 
							SixOfDiamonds(),
 | 
				
			||||||
 | 
							SixOfClubs(),
 | 
				
			||||||
 | 
							FiveOfSpades(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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.True(t, hand.containsFourOfAKind(), "Expected hand to be four of a kind")
 | 
				
			||||||
 | 
						assert.False(t, hand.containsFullHouse(), "Expected hand to not be a full house")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// note that these are *expected* to be true.  the contains* functions
 | 
				
			||||||
 | 
						// are used in the PokerHand.calculateScore method to determine the best hand
 | 
				
			||||||
 | 
						// and are checked in sequence of descending value, so if a hand is four of a kind
 | 
				
			||||||
 | 
						// it will not be checked for full house, three of a kind, etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// technically quads *is* two pair also, and a hand with quads does
 | 
				
			||||||
 | 
						// indeed contain three of a kind, and contains a pair.
 | 
				
			||||||
 | 
						assert.True(t, hand.containsThreeOfAKind(), "Expected hand to contain three of a kind")
 | 
				
			||||||
 | 
						assert.True(t, hand.containsTwoPair(), "Expected hand to contain two pair")
 | 
				
			||||||
 | 
						assert.True(t, hand.containsPair(), "Expected hand to contain a pair")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.True(t, hand.HighestRank() == SIX, "Expected highest rank to be a six")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestRoyalFlush(t *testing.T) {
 | 
					func TestRoyalFlush(t *testing.T) {
 | 
				
			||||||
	hand := Cards{
 | 
						hand := Cards{
 | 
				
			||||||
		TenOfSpades(),
 | 
							TenOfSpades(),
 | 
				
			||||||
@ -197,6 +292,17 @@ func TestTwoPair(t *testing.T) {
 | 
				
			|||||||
	assert.False(t, hand.isUnmadeHand(), "Expected hand to not be unmade")
 | 
						assert.False(t, hand.isUnmadeHand(), "Expected hand to not be unmade")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDetectDuplicates(t *testing.T) {
 | 
				
			||||||
 | 
						hand := Cards{
 | 
				
			||||||
 | 
							KingOfSpades(),
 | 
				
			||||||
 | 
							JackOfDiamonds(),
 | 
				
			||||||
 | 
							JackOfSpades(),
 | 
				
			||||||
 | 
							KingOfSpades(),
 | 
				
			||||||
 | 
							TenOfSpades(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						assert.True(t, hand.containsDuplicates(), "Expected hand to contain duplicates")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestHandScore(t *testing.T) {
 | 
					func TestHandScore(t *testing.T) {
 | 
				
			||||||
	hand := Cards{
 | 
						hand := Cards{
 | 
				
			||||||
		KingOfSpades(),
 | 
							KingOfSpades(),
 | 
				
			||||||
@ -211,8 +317,8 @@ func TestHandScore(t *testing.T) {
 | 
				
			|||||||
	assert.True(t, ph.Score > 0, "Expected score to be nonzero 0")
 | 
						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")
 | 
						assert.True(t, ph.Score < 100000000000000000, "Expected score to be less than 100000000000000000")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fmt.Printf("PokerHand: %v+\n", ph)
 | 
						//fmt.Printf("PokerHand: %v+\n", ph)
 | 
				
			||||||
	fmt.Printf("PH score: %d\n", ph.Score)
 | 
						//fmt.Printf("PH score: %d\n", ph.Score)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestTwoPairBug(t *testing.T) {
 | 
					func TestTwoPairBug(t *testing.T) {
 | 
				
			||||||
@ -220,12 +326,65 @@ func TestTwoPairBug(t *testing.T) {
 | 
				
			|||||||
	c, err := NewCardsFromString("9♠,9♣,Q♥,Q♦,K♣")
 | 
						c, err := NewCardsFromString("9♠,9♣,Q♥,Q♦,K♣")
 | 
				
			||||||
	assert.Nil(t, err, "Expected no error")
 | 
						assert.Nil(t, err, "Expected no error")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//fmt.Printf("Cards: %v+\n", c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ph, err := c.PokerHand()
 | 
						ph, err := c.PokerHand()
 | 
				
			||||||
	assert.Nil(t, err, "Expected no error")
 | 
						assert.Nil(t, err, "Expected no error")
 | 
				
			||||||
	assert.Greater(t, ph.Score, 0, "Expected score to be nonzero 0")
 | 
						assert.Greater(t, ph.Score, 0, "Expected score to be nonzero 0")
 | 
				
			||||||
	desc := ph.Description()
 | 
						desc := ph.Description()
 | 
				
			||||||
	assert.Equal(t, desc, "two pair, queens and nines with a king")
 | 
						assert.Equal(t, desc, "two pair, queens and nines with a king")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fmt.Printf("PokerHand: %v+\n", ph)
 | 
						//fmt.Printf("PokerHand: %v+\n", ph)
 | 
				
			||||||
	fmt.Printf("PH score: %d\n", ph.Score)
 | 
						//fmt.Printf("PH score: %d\n", ph.Score)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestScoringStructureQuads(t *testing.T) {
 | 
				
			||||||
 | 
						handA := Cards{
 | 
				
			||||||
 | 
							DeuceOfSpades(),
 | 
				
			||||||
 | 
							DeuceOfHearts(),
 | 
				
			||||||
 | 
							DeuceOfDiamonds(),
 | 
				
			||||||
 | 
							DeuceOfClubs(),
 | 
				
			||||||
 | 
							AceOfSpades(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						handB := Cards{
 | 
				
			||||||
 | 
							ThreeOfSpades(),
 | 
				
			||||||
 | 
							ThreeOfHearts(),
 | 
				
			||||||
 | 
							ThreeOfDiamonds(),
 | 
				
			||||||
 | 
							ThreeOfClubs(),
 | 
				
			||||||
 | 
							DeuceOfSpades(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						phA, err := handA.PokerHand()
 | 
				
			||||||
 | 
						assert.Nil(t, err, "Expected no error")
 | 
				
			||||||
 | 
						phB, err := handB.PokerHand()
 | 
				
			||||||
 | 
						assert.Nil(t, err, "Expected no error")
 | 
				
			||||||
 | 
						assert.Greater(t, phB.Score, phA.Score, "Expected hand B to be higher than hand A")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestScoringStructureFullHouse(t *testing.T) {
 | 
				
			||||||
 | 
						handA := Cards{
 | 
				
			||||||
 | 
							DeuceOfSpades(),
 | 
				
			||||||
 | 
							DeuceOfHearts(),
 | 
				
			||||||
 | 
							DeuceOfDiamonds(),
 | 
				
			||||||
 | 
							AceOfSpades(),
 | 
				
			||||||
 | 
							AceOfHearts(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						phA, err := handA.PokerHand()
 | 
				
			||||||
 | 
						assert.Nil(t, err, "Expected no error")
 | 
				
			||||||
 | 
						deucesFullOfAcesScore := phA.Score
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						handB := Cards{
 | 
				
			||||||
 | 
							ThreeOfSpades(),
 | 
				
			||||||
 | 
							ThreeOfHearts(),
 | 
				
			||||||
 | 
							ThreeOfDiamonds(),
 | 
				
			||||||
 | 
							DeuceOfSpades(),
 | 
				
			||||||
 | 
							DeuceOfHearts(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						phB, err := handB.PokerHand()
 | 
				
			||||||
 | 
						assert.Nil(t, err, "Expected no error")
 | 
				
			||||||
 | 
						threesFullOfDeucesScore := phB.Score
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.Greater(t, threesFullOfDeucesScore, deucesFullOfAcesScore, "Expected Threes full of deuces to beat deuces full of aces")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user