crowns/game.go

169 lines
3.7 KiB
Go

package main
import (
"fmt"
"math/rand"
)
func PlayGame(numPlayers int, entropy *rand.Rand) {
currentPlayer := 0
runningScores := make([]int, numPlayers)
for round := 3; round < 14; round++ {
r := NewRound(round, numPlayers, entropy)
nextPlayer, roundScores := r.Play(currentPlayer)
currentPlayer = nextPlayer
for p := 0; p < numPlayers; p++ {
runningScores[p] += roundScores[p]
}
fmt.Printf("The round has ended\n")
fmt.Printf("Running total scores: %v\n", runningScores)
}
fmt.Printf("The game has ended\n")
}
type Round struct {
d []Card
discard []Card
round int
numPlayers int
hands [][]Card
}
func NewRound(round, numPlayers int, entropy *rand.Rand) *Round {
r := Round{
round: round,
numPlayers: numPlayers,
}
r.d = Deck()
Shuffle(r.d, entropy)
r.discard = []Card{}
fmt.Printf("# Round %d\n\n", r.round)
// Deal starting cards
r.hands = make([][]Card, numPlayers)
for p := 0; p < numPlayers; p++ {
r.hands[p] = make([]Card, round, round+1)
copy(r.hands[p], r.d[0:round]) // Deal from the bottom (0)
r.d = r.d[round:]
fmt.Printf("P%d starting hand: %v\n", p, r.hands[p])
}
// Deal one into the discard pile
r.discard = append(r.discard, r.d[0])
r.d = r.d[1:]
// Done
return &r
}
func (r *Round) DrawFromDeck() Card {
newCard := r.d[0]
r.d = r.d[1:]
return newCard
}
func (r *Round) PeekFromDiscard() Card {
return r.discard[len(r.discard)-1]
}
func (r *Round) DrawFromDiscard() Card {
newCard := r.discard[len(r.discard)-1]
r.discard = r.discard[0 : len(r.discard)-1]
return newCard
}
func (r *Round) AddToHand(player int, c Card) {
r.hands[player] = append(r.hands[player], c)
}
func (r *Round) Discard(player, idx int) {
unwantedCard := r.hands[player][idx]
// Slice tricks: Copy end card into this slot and reslice bounds
r.hands[player][idx] = r.hands[player][len(r.hands[player])-1]
r.hands[player] = r.hands[player][0 : len(r.hands[player])-1]
r.discard = append(r.discard, unwantedCard) // On top = available for next player
}
func (r *Round) Play(startingPlayer int) (nextPlayer int, roundScores []int) {
currentPlayer := startingPlayer
fmt.Printf("Discard stack: %v\n", r.discard)
fmt.Printf("Deck has %d cards remaining\n", len(r.d))
roundEndsWhenPlayerIs := -1
for {
if roundEndsWhenPlayerIs == currentPlayer {
break
}
// Play the strategy for the current player
r.PlayDefaultStrategy(currentPlayer)
// Check, one more time, if we have a winning hand
currentBestGrouping, currentScore := FindBestGrouping(r.hands[currentPlayer], r.round)
if currentScore == 0 {
// Declare victory
fmt.Printf("P%d declares victory\n- Hand: %v\n- Groupings: %v\n- Summary: %v\n", currentPlayer, r.hands[currentPlayer], currentBestGrouping, FormatHandGroupings(r.hands[currentPlayer], currentBestGrouping))
if roundEndsWhenPlayerIs == -1 {
roundEndsWhenPlayerIs = currentPlayer
} // Otherwise, the round is already ending anyway
}
// Move to the next player
currentPlayer = (currentPlayer + 1) % r.numPlayers
}
// The round has ended
// Figure out what each player scored
roundScores = make([]int, r.numPlayers)
for p := 0; p < r.numPlayers; p++ {
gr, score := FindBestGrouping(r.hands[p], r.round)
fmt.Printf("P%d has %v (score: %d)\n", p, FormatHandGroupings(r.hands[p], gr) /*r.hands[p], gr,*/, score)
if roundEndsWhenPlayerIs == p && score != 0 {
fmt.Printf("Expected player %d to have won, but they didn't?\n", p)
panic("???")
}
roundScores[p] = score
}
// Check stack alignment
if len(r.discard)+len(r.d)+(r.numPlayers*len(r.hands[0])) != DeckSize {
panic("Cards on the floor")
}
// Done
return currentPlayer, roundScores
}