155 lines
4.2 KiB
Go
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
|
|
|
|
}
|
|
}
|