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 }