refactor formatting, reuse helper functions

This commit is contained in:
mappu 2024-01-07 16:00:20 +13:00
parent 7896dc9f6b
commit 670e557966
6 changed files with 118 additions and 41 deletions

29
format.go Normal file
View File

@ -0,0 +1,29 @@
package main
import (
"fmt"
)
func FormatHandGroupings(hand []Card, groupings [][]int) string {
tmp := forkHand(hand)
cgroups := [][]Card{}
for _, group := range groupings {
cgroup := []Card{}
for _, cidx := range group {
cgroup = append(cgroup, hand[cidx])
tmp[cidx] = NewMasked()
}
cgroups = append(cgroups, cgroup)
}
leftover := []Card{}
for _, cv := range tmp {
if !cv.Masked() {
leftover = append(leftover, cv)
}
}
return fmt.Sprintf("[ %v leftover %v ]", cgroups, leftover)
}

12
format_test.go Normal file
View File

@ -0,0 +1,12 @@
package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFormatHandGrouping(t *testing.T) {
str := FormatHandGroupings([]Card{5, 10, 15, 20}, [][]int{[]int{0, 1}, []int{2}})
assert.EqualValues(t, "[ [[ 1♠ 2♠] [ 3♠]] leftover [ 4♠] ]", str)
}

51
game.go
View File

@ -71,28 +71,36 @@ func NewRound(round, numPlayers int, entropy *rand.Rand) *Round {
return &r
}
func FormatHandGroupings(hand []Card, groupings [][]int) string {
tmp := forkHand(hand)
func (r *Round) DrawFromDeck() Card {
newCard := r.d[0]
r.d = r.d[1:]
return newCard
}
cgroups := [][]Card{}
for _, group := range groupings {
cgroup := []Card{}
for _, cidx := range group {
cgroup = append(cgroup, hand[cidx])
func (r *Round) PeekFromDiscard() Card {
return r.discard[len(r.discard)-1]
}
tmp[cidx] = Card(-1)
}
cgroups = append(cgroups, cgroup)
}
func (r *Round) DrawFromDiscard() Card {
newCard := r.discard[len(r.discard)-1]
r.discard = r.discard[0 : len(r.discard)-1]
leftover := []Card{}
for _, cv := range tmp {
if cv != Card(-1) {
leftover = append(leftover, cv)
}
}
return newCard
}
return fmt.Sprintf("[ %v leftover %v ]", cgroups, leftover)
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) {
@ -119,7 +127,7 @@ func (r *Round) Play(startingPlayer int) (nextPlayer int, roundScores []int) {
if currentScore == 0 {
// Declare victory
fmt.Printf("P%d declares victory\n- Hand: %v\n- Groupings: %v\n", currentPlayer, r.hands[currentPlayer], currentBestGrouping)
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
@ -141,6 +149,11 @@ func (r *Round) Play(startingPlayer int) (nextPlayer int, roundScores []int) {
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
}

View File

@ -17,3 +17,28 @@ func TestPlayRound(t *testing.T) {
assert.EqualValues(t, 0, nextPlayer)
assert.EqualValues(t, []int{0, 28, 6, 6}, scores)
}
func TestDrawDiscard(t *testing.T) {
entropy := rand.New(rand.NewSource(0xdeadbeef))
rr := NewRound(5, 4, entropy)
assert.EqualValues(t, []Card{50, 47, 24, 25, 22}, rr.hands[0])
c := rr.DrawFromDeck()
assert.EqualValues(t, Card(15), c)
rr.AddToHand(0, c)
assert.EqualValues(t, []Card{50, 47, 24, 25, 22, 15}, rr.hands[0])
rr.Discard(0, 2)
assert.EqualValues(t, []Card{50, 47, 15, 25, 22}, rr.hands[0])
assert.EqualValues(t, Card(24), rr.PeekFromDiscard())
c = rr.DrawFromDiscard()
rr.AddToHand(0, c)
assert.EqualValues(t, []Card{50, 47, 15, 25, 22, 24}, rr.hands[0])
}

View File

@ -119,7 +119,7 @@ func MakeMultiGroups(hand []Card, wildface int) [][][]int {
// If that uses up all cards, then early-exit
anyUnpaired := false
for cidx := 0; cidx < len(cloneHand); cidx++ {
if cloneHand[cidx] != Card(-1) {
if !cloneHand[cidx].Masked() {
anyUnpaired = true
break
}

View File

@ -19,7 +19,7 @@ func (r *Round) PlayDefaultStrategy(currentPlayer int) {
for cidx := 0; cidx < r.round; cidx++ {
// Pick up the last discard card, and discard card@cidx
theoretically := forkHand(r.hands[currentPlayer])
theoretically[cidx] = r.discard[len(r.discard)-1]
theoretically[cidx] = r.PeekFromDiscard()
_, score := FindBestGrouping(theoretically, r.round)
if score < bestDiscardBasedScore {
@ -27,6 +27,10 @@ func (r *Round) PlayDefaultStrategy(currentPlayer int) {
bestDiscardBasedIdx = cidx
bestDiscardBasedScore = score
}
if score == 0 {
break // Early exit
}
}
}
@ -42,16 +46,15 @@ func (r *Round) PlayDefaultStrategy(currentPlayer int) {
fmt.Printf("P%d discards %v immediately\n", currentPlayer, r.discard[len(r.discard)-1])
} else {
// Take the discard card and discard cidx
newCard := r.discard[len(r.discard)-1]
r.discard = r.discard[0 : len(r.discard)-1]
// Take the discard card
newCard := r.DrawFromDiscard()
unwantedCard := r.hands[currentPlayer][bestDiscardBasedIdx]
r.hands[currentPlayer][bestDiscardBasedIdx] = newCard
r.AddToHand(currentPlayer, newCard)
r.discard = append(r.discard, unwantedCard) // On top = available for next player
// Discard cidx
fmt.Printf("P%d discards %v (position %d)\n", currentPlayer, unwantedCard, bestDiscardBasedIdx)
fmt.Printf("P%d discards %v (position %d)\n", currentPlayer, r.hands[currentPlayer][bestDiscardBasedIdx], bestDiscardBasedIdx)
r.Discard(currentPlayer, bestDiscardBasedIdx)
}
@ -60,9 +63,8 @@ func (r *Round) PlayDefaultStrategy(currentPlayer int) {
// score
// Take from the deck
newCard := r.d[0]
r.d = r.d[1:]
r.hands[currentPlayer] = append(r.hands[currentPlayer], newCard)
newCard := r.DrawFromDeck()
r.AddToHand(currentPlayer, newCard)
fmt.Printf("P%d taking %v from the deck\n", currentPlayer, newCard)
@ -73,7 +75,7 @@ func (r *Round) PlayDefaultStrategy(currentPlayer int) {
for cidx := 0; cidx < r.round+1; cidx++ {
// Assume we discard that card,
theoretically := forkHand(r.hands[currentPlayer])
theoretically[cidx] = Card(-1) // Never matches + worth 0 points
theoretically[cidx] = NewMasked() // Never matches + worth 0 points
_, score := FindBestGrouping(theoretically, r.round)
if score < bestRandomBasedScore {
@ -82,19 +84,15 @@ func (r *Round) PlayDefaultStrategy(currentPlayer int) {
bestRandomBasedScore = score
}
if score == 0 {
break // Early exit
}
}
// Discard our chosen card
// Slice tricks: Copy end card into this slot and reslice bounds
unwantedCard := r.hands[currentPlayer][bestRandomBasedIdx]
fmt.Printf("P%d discards %v (position %d)\n", currentPlayer, unwantedCard, bestRandomBasedIdx)
r.hands[currentPlayer][bestRandomBasedIdx] = r.hands[currentPlayer][len(r.hands[currentPlayer])-1]
r.hands[currentPlayer] = r.hands[currentPlayer][0 : len(r.hands[currentPlayer])-1]
r.discard = append(r.discard, unwantedCard) // On top = available for next player
fmt.Printf("P%d discards %v (position %d)\n", currentPlayer, r.hands[currentPlayer][bestRandomBasedIdx], bestRandomBasedIdx)
r.Discard(currentPlayer, bestRandomBasedIdx)
}