crowns/game.go
2024-01-07 13:09:47 +13:00

155 lines
4.2 KiB
Go

package main
import (
"fmt"
)
func PlayGame(numPlayers int) {
currentPlayer := 0
for round := 3; round < 14; round++ {
d := Deck()
Shuffle(d)
discard := []Card{}
fmt.Printf("# Round %d\n\n", round)
// Deal starting cards
hands := make([][]Card, numPlayers)
for p := 0; p < numPlayers; p++ {
hands[p] = d[0:round] // Deal from the bottom (0)
d = d[round:]
fmt.Printf("P%d starting hand: %v\n", p, hands[p])
}
// Deal one into the discard pile
discard = append(discard, d[0])
d = d[1:]
fmt.Printf("Discard stack: %v\n", discard)
fmt.Printf("Deck has %d cards remaining\n", len(d))
roundEndsWhenPlayerIs := -1
for {
if roundEndsWhenPlayerIs == currentPlayer {
break
}
// Score our hand as if we pick up the discard and discard it immediately
_, score := FindBestGrouping(hands[currentPlayer], round)
baseScore := score
bestDiscardBasedIdx := -1
bestDiscardBasedScore := score
// Score our hand as if we pick up the discard card and discard each other card
for cidx := 0; cidx < round; cidx++ {
// Pick up the last discard card, and discard card@cidx
theoretically := forkHand(hands[currentPlayer])
theoretically[cidx] = discard[len(discard)-1]
_, score := FindBestGrouping(theoretically, round)
if score < bestDiscardBasedScore {
// TODO if the points are equal or near-equal, prefer not to discard a wild for the next player
bestDiscardBasedIdx = cidx
bestDiscardBasedScore = score
}
}
// If taking the discard would let us win, or reduce our score by >5, take it
if bestDiscardBasedScore == 0 || (baseScore-bestDiscardBasedScore) > 5 {
fmt.Printf("P%d taking %v from the discard pile\n", currentPlayer, discard[len(discard)-1])
// We're going to win
if bestDiscardBasedIdx == -1 {
// Take the discard card + put it back again i.e. no change
} else {
// Take the discard card and discard cidx
newCard := discard[len(discard)-1]
discard = discard[0 : len(discard)-1]
unwantedCard := hands[currentPlayer][bestDiscardBasedIdx]
hands[currentPlayer][bestDiscardBasedIdx] = newCard
discard = append(discard, unwantedCard) // On top = available for next player
fmt.Printf("P%d discards %v\n", currentPlayer, unwantedCard)
}
} else {
// The discard card is not going to make us win or significantly lower our
// score
// Take from the deck
newCard := d[0]
d = d[1:]
hands[currentPlayer] = append(hands[currentPlayer], newCard)
fmt.Printf("P%d taking %v from the deck\n", currentPlayer, newCard)
bestRandomBasedIdx := -1
bestRandomBasedScore := (round * 50) + 51 // Worse than the worst possible score
// Look at what we can now drop (hand size + 1 new card). We have to drop something
for cidx := 0; cidx < round+1; cidx++ {
// Assume we discard that card,
theoretically := forkHand(hands[currentPlayer])
theoretically[cidx] = Card(-1) // Never matches + worth 0 points
_, score := FindBestGrouping(theoretically, round)
if score < bestRandomBasedScore {
// TODO if the points are equal or near-equal, prefer not to discard a wild for the next player
bestRandomBasedIdx = cidx
bestRandomBasedScore = score
}
}
// Discard our chosen card
// Slice tricks: Copy end card into this slot and reslice bounds
unwantedCard := hands[currentPlayer][bestRandomBasedIdx]
fmt.Printf("P%d discards %v\n", currentPlayer, unwantedCard)
hands[currentPlayer][bestRandomBasedIdx] = hands[currentPlayer][len(hands[currentPlayer])-1]
hands[currentPlayer] = hands[currentPlayer][0 : len(hands[currentPlayer])-1]
discard = append(discard, unwantedCard) // On top = available for next player
}
// Check, one more time, if we have a winning hand
currentBestGrouping, currentScore := FindBestGrouping(hands[currentPlayer], round)
if currentScore == 0 {
// Declare victory
fmt.Printf("P%d declares victory\n- Hand: %v\n- Groupings: %v\n", currentPlayer, hands[currentPlayer], currentBestGrouping)
roundEndsWhenPlayerIs = currentPlayer
}
// Move to the next player
currentPlayer = (currentPlayer + 1) % numPlayers
}
// The round has ended
// Figure out what each player scored
return
}
}