From 9034e1303ae0b89f112efb04369c7a25623835c8 Mon Sep 17 00:00:00 2001 From: Mariano Gappa Date: Fri, 19 Jul 2024 21:46:01 +0100 Subject: [PATCH] Award envido points when envido is revealed. --- main_wasm.go | 5 +++-- truco/action_any_quiero.go | 30 ++++++++++++++++++++++-------- truco/action_reveal_card.go | 11 +++++++++-- truco/action_son_buenas.go | 2 +- truco/action_son_mejores.go | 2 +- truco/actions.go | 34 +++++++++++++++++----------------- truco/envido_sequence.go | 7 +++++++ truco/truco.go | 33 ++++++++++++++++++++++++++++++++- 8 files changed, 92 insertions(+), 32 deletions(-) diff --git a/main_wasm.go b/main_wasm.go index 39f41d6..43963de 100644 --- a/main_wasm.go +++ b/main_wasm.go @@ -51,14 +51,15 @@ func trucoRunAction(this js.Value, p []js.Value) interface{} { func trucoBotRunAction(this js.Value, p []js.Value) interface{} { action := bot.ChooseAction(state.ToClientGameState(1)) + fmt.Println("Action chosen by bot:", action) err := state.RunAction(action) if err != nil { - panic(err) + panic(fmt.Errorf("running action: %w", err)) } nbs, err := json.Marshal(state.ToClientGameState(0)) if err != nil { - panic(err) + panic(fmt.Errorf("marshalling game state: %w", err)) } buffer := js.Global().Get("Uint8Array").New(len(nbs)) diff --git a/truco/action_any_quiero.go b/truco/action_any_quiero.go index f69225f..7c6da36 100644 --- a/truco/action_any_quiero.go +++ b/truco/action_any_quiero.go @@ -53,6 +53,9 @@ func (a ActionRevealEnvidoScore) IsPossible(g GameState) bool { if !g.EnvidoSequence.WasAccepted() { return false } + if g.EnvidoSequence.EnvidoPointsAwarded { + return false + } roundLog := g.RoundsLog[g.RoundNumber] if roundLog.EnvidoWinnerPlayerID != a.PlayerID { return false @@ -67,10 +70,14 @@ func (a ActionSayTrucoQuiero) IsPossible(g GameState) bool { } // Edge case: Truco -> Envido -> ??? // In this case, until envido is resolved, truco cannot continue - actionEnvidoQuiero := ActionSayEnvidoQuiero{act: act{Name: SAY_ENVIDO_QUIERO}} - actionSonBuenas := ActionSaySonBuenas{act: act{Name: SAY_SON_BUENAS}} - actionSonMejores := ActionSaySonMejores{act: act{Name: SAY_SON_MEJORES}} - if actionEnvidoQuiero.IsPossible(g) || actionSonBuenas.IsPossible(g) || actionSonMejores.IsPossible(g) { + var ( + me = a.PlayerID + isEnvidoQuieroPossible = NewActionSayEnvidoQuiero(me).IsPossible(g) + isSonBuenasPossible = NewActionSaySonBuenas(me).IsPossible(g) + isSonMejoresPossible = NewActionSaySonMejores(0, me).IsPossible(g) + isSayEnvidoScorePossible = NewActionSayEnvidoScore(0, me).IsPossible(g) + ) + if isEnvidoQuieroPossible || isSonBuenasPossible || isSonMejoresPossible || isSayEnvidoScorePossible { return false } @@ -83,10 +90,14 @@ func (a ActionSayTrucoNoQuiero) IsPossible(g GameState) bool { } // Edge case: Truco -> Envido -> ??? // In this case, until envido is resolved, truco cannot continue - actionEnvidoQuiero := ActionSayEnvidoQuiero{act: act{Name: SAY_ENVIDO_QUIERO}} - actionSonBuenas := ActionSaySonBuenas{act: act{Name: SAY_SON_BUENAS}} - actionSonMejores := ActionSaySonMejores{act: act{Name: SAY_SON_MEJORES}} - if actionEnvidoQuiero.IsPossible(g) || actionSonBuenas.IsPossible(g) || actionSonMejores.IsPossible(g) { + var ( + me = a.PlayerID + isEnvidoQuieroPossible = NewActionSayEnvidoQuiero(me).IsPossible(g) + isSonBuenasPossible = NewActionSaySonBuenas(me).IsPossible(g) + isSonMejoresPossible = NewActionSaySonMejores(0, me).IsPossible(g) + isSayEnvidoScorePossible = NewActionSayEnvidoScore(0, me).IsPossible(g) + ) + if isEnvidoQuieroPossible || isSonBuenasPossible || isSonMejoresPossible || isSayEnvidoScorePossible { return false } @@ -165,6 +176,9 @@ func (a ActionRevealEnvidoScore) Run(g *GameState) error { } // replace hand with our satisfactory candidate hand g.Players[a.PlayerID].Hand = &candidateHand + if !g.tryAwardEnvidoPoints() { + return fmt.Errorf("couldn't award envido score after running reveal envido score action due to a bug, this code should be unreachable") + } return nil } } diff --git a/truco/action_reveal_card.go b/truco/action_reveal_card.go index 1ca5488..84bb2a6 100644 --- a/truco/action_reveal_card.go +++ b/truco/action_reveal_card.go @@ -2,7 +2,9 @@ package truco type ActionRevealCard struct { act - Card Card `json:"card"` + Card Card `json:"card"` + EnMesa bool `json:"en_mesa"` + Score int `json:"score"` } func (a ActionRevealCard) IsPossible(g GameState) bool { @@ -27,7 +29,7 @@ func (a ActionRevealCard) IsPossible(g GameState) bool { return g.CardRevealSequence.CanAddStep(step, g) } -func (a ActionRevealCard) Run(g *GameState) error { +func (a *ActionRevealCard) Run(g *GameState) error { if !a.IsPossible(*g) { return errActionNotPossible } @@ -64,6 +66,11 @@ func (a ActionRevealCard) Run(g *GameState) error { if !g.IsEnvidoFinished && len(g.Players[g.TurnPlayerID].Hand.Revealed) >= 1 && len(g.Players[g.TurnOpponentPlayerID].Hand.Revealed) >= 1 { g.IsEnvidoFinished = true } + // Revealing a card may cause the envido score to be revealed + if g.tryAwardEnvidoPoints() { + a.EnMesa = true + a.Score = g.Players[a.PlayerID].Hand.EnvidoScore() // it must be the action's player + } return nil } diff --git a/truco/action_son_buenas.go b/truco/action_son_buenas.go index d47e672..d94f7b1 100644 --- a/truco/action_son_buenas.go +++ b/truco/action_son_buenas.go @@ -45,7 +45,7 @@ func (a ActionSaySonBuenas) Run(g *GameState) error { g.RoundsLog[g.RoundNumber].EnvidoPoints = cost g.RoundsLog[g.RoundNumber].EnvidoWinnerPlayerID = g.TurnOpponentPlayerID g.IsEnvidoFinished = true - g.Players[g.TurnOpponentPlayerID].Score += cost + g.tryAwardEnvidoPoints() return nil } diff --git a/truco/action_son_mejores.go b/truco/action_son_mejores.go index 670bada..39799df 100644 --- a/truco/action_son_mejores.go +++ b/truco/action_son_mejores.go @@ -44,7 +44,7 @@ func (a ActionSaySonMejores) Run(g *GameState) error { g.RoundsLog[g.RoundNumber].EnvidoPoints = cost g.RoundsLog[g.RoundNumber].EnvidoWinnerPlayerID = g.TurnPlayerID g.IsEnvidoFinished = true - g.Players[g.TurnPlayerID].Score += cost + g.tryAwardEnvidoPoints() return nil } diff --git a/truco/actions.go b/truco/actions.go index 4756b7f..cb22104 100644 --- a/truco/actions.go +++ b/truco/actions.go @@ -26,69 +26,69 @@ func (a act) YieldsTurn(g GameState) bool { } func NewActionSayEnvido(playerID int) Action { - return ActionSayEnvido{act: act{Name: SAY_ENVIDO, PlayerID: playerID}} + return &ActionSayEnvido{act: act{Name: SAY_ENVIDO, PlayerID: playerID}} } func NewActionSayRealEnvido(playerID int) Action { - return ActionSayRealEnvido{act: act{Name: SAY_REAL_ENVIDO, PlayerID: playerID}} + return &ActionSayRealEnvido{act: act{Name: SAY_REAL_ENVIDO, PlayerID: playerID}} } func NewActionSayFaltaEnvido(playerID int) Action { - return ActionSayFaltaEnvido{act: act{Name: SAY_FALTA_ENVIDO, PlayerID: playerID}} + return &ActionSayFaltaEnvido{act: act{Name: SAY_FALTA_ENVIDO, PlayerID: playerID}} } func NewActionSayEnvidoNoQuiero(playerID int) Action { - return ActionSayEnvidoNoQuiero{act: act{Name: SAY_ENVIDO_NO_QUIERO, PlayerID: playerID}} + return &ActionSayEnvidoNoQuiero{act: act{Name: SAY_ENVIDO_NO_QUIERO, PlayerID: playerID}} } func NewActionSayEnvidoQuiero(playerID int) Action { - return ActionSayEnvidoQuiero{act: act{Name: SAY_ENVIDO_QUIERO, PlayerID: playerID}} + return &ActionSayEnvidoQuiero{act: act{Name: SAY_ENVIDO_QUIERO, PlayerID: playerID}} } func NewActionSayEnvidoScore(score int, playerID int) Action { - return ActionSayEnvidoScore{act: act{Name: SAY_ENVIDO_SCORE, PlayerID: playerID}, Score: score} + return &ActionSayEnvidoScore{act: act{Name: SAY_ENVIDO_SCORE, PlayerID: playerID}, Score: score} } func NewActionSayTrucoQuiero(playerID int) Action { - return ActionSayTrucoQuiero{act: act{Name: SAY_TRUCO_QUIERO, PlayerID: playerID}} + return &ActionSayTrucoQuiero{act: act{Name: SAY_TRUCO_QUIERO, PlayerID: playerID}} } func NewActionSayTrucoNoQuiero(playerID int) Action { - return ActionSayTrucoNoQuiero{act: act{Name: SAY_TRUCO_NO_QUIERO, PlayerID: playerID}} + return &ActionSayTrucoNoQuiero{act: act{Name: SAY_TRUCO_NO_QUIERO, PlayerID: playerID}} } func NewActionSayTruco(playerID int) Action { - return ActionSayTruco{act: act{Name: SAY_TRUCO, PlayerID: playerID}} + return &ActionSayTruco{act: act{Name: SAY_TRUCO, PlayerID: playerID}} } func NewActionSayQuieroRetruco(playerID int) Action { - return ActionSayQuieroRetruco{act: act{Name: SAY_QUIERO_RETRUCO, PlayerID: playerID}} + return &ActionSayQuieroRetruco{act: act{Name: SAY_QUIERO_RETRUCO, PlayerID: playerID}} } func NewActionSayQuieroValeCuatro(playerID int) Action { - return ActionSayQuieroValeCuatro{act: act{Name: SAY_QUIERO_VALE_CUATRO, PlayerID: playerID}} + return &ActionSayQuieroValeCuatro{act: act{Name: SAY_QUIERO_VALE_CUATRO, PlayerID: playerID}} } func NewActionSaySonBuenas(playerID int) Action { - return ActionSaySonBuenas{act: act{Name: SAY_SON_BUENAS, PlayerID: playerID}} + return &ActionSaySonBuenas{act: act{Name: SAY_SON_BUENAS, PlayerID: playerID}} } func NewActionSaySonMejores(score int, playerID int) Action { - return ActionSaySonMejores{act: act{Name: SAY_SON_MEJORES, PlayerID: playerID}, Score: score} + return &ActionSaySonMejores{act: act{Name: SAY_SON_MEJORES, PlayerID: playerID}, Score: score} } func NewActionRevealCard(card Card, playerID int) Action { - return ActionRevealCard{act: act{Name: REVEAL_CARD, PlayerID: playerID}, Card: card} + return &ActionRevealCard{act: act{Name: REVEAL_CARD, PlayerID: playerID}, Card: card} } func NewActionSayMeVoyAlMazo(playerID int) Action { - return ActionSayMeVoyAlMazo{act: act{Name: SAY_ME_VOY_AL_MAZO, PlayerID: playerID}} + return &ActionSayMeVoyAlMazo{act: act{Name: SAY_ME_VOY_AL_MAZO, PlayerID: playerID}} } func NewActionConfirmRoundFinished(playerID int) Action { - return ActionConfirmRoundFinished{act: act{Name: CONFIRM_ROUND_FINISHED, PlayerID: playerID}} + return &ActionConfirmRoundFinished{act: act{Name: CONFIRM_ROUND_FINISHED, PlayerID: playerID}} } func NewActionRevealEnvidoScore(playerID int, score int) Action { - return ActionRevealEnvidoScore{act: act{Name: REVEAL_ENVIDO_SCORE, PlayerID: playerID}, Score: score} + return &ActionRevealEnvidoScore{act: act{Name: REVEAL_ENVIDO_SCORE, PlayerID: playerID}, Score: score} } diff --git a/truco/envido_sequence.go b/truco/envido_sequence.go index 88a3997..42f6e30 100644 --- a/truco/envido_sequence.go +++ b/truco/envido_sequence.go @@ -95,6 +95,13 @@ type EnvidoSequence struct { // This is necessary because when son_buenas/son_mejores/no_quiero is said, // the turn goes to whoever started the envido sequence (i.e. affects YieldsTurn) StartingPlayerID int `json:"startingPlayerID"` + + // EnvidoPointsAwarded is used to determine if the points have already been awarded. + // + // When an envido is won, points are not automatically awarded. They are when the + // winning score is revealed. This could be at the time it is won, but normally + // it is revealed later. + EnvidoPointsAwarded bool `json:"envidoPointsAwarded"` } func (es EnvidoSequence) String() string { diff --git a/truco/truco.go b/truco/truco.go index a99aae0..eecb105 100644 --- a/truco/truco.go +++ b/truco/truco.go @@ -266,6 +266,31 @@ func (g *GameState) PrettyPrint() (string, error) { return string(prettyJSON), nil } +func (g *GameState) canAwardEnvidoPoints(revealedHand Hand) bool { + wonBy := g.RoundsLog[g.RoundNumber].EnvidoWinnerPlayerID + if wonBy == -1 { + return false + } + if g.EnvidoSequence.EnvidoPointsAwarded { + return false + } + if revealedHand.EnvidoScore() != g.Players[wonBy].Hand.EnvidoScore() { + return false + } + return true +} + +func (g *GameState) tryAwardEnvidoPoints() bool { + if !g.canAwardEnvidoPoints(Hand{Revealed: g.Players[g.TurnPlayerID].Hand.Revealed}) { + return false + } + wonBy := g.RoundsLog[g.RoundNumber].EnvidoWinnerPlayerID + score := g.RoundsLog[g.RoundNumber].EnvidoPoints + g.Players[wonBy].Score += score + g.EnvidoSequence.EnvidoPointsAwarded = true + return true +} + type Action interface { IsPossible(g GameState) bool Run(g *GameState) error @@ -290,7 +315,13 @@ func (g GameState) CalculatePossibleActions() []Action { allActions := []Action{} for _, card := range g.Players[g.TurnPlayerID].Hand.Unrevealed { - allActions = append(allActions, NewActionRevealCard(card, g.TurnPlayerID)) + revealCard := NewActionRevealCard(card, g.TurnPlayerID).(*ActionRevealCard) + // If revealing a card would award envido points, then set the score in the action + if g.canAwardEnvidoPoints(Hand{Revealed: append(g.Players[g.TurnPlayerID].Hand.Revealed, card)}) { + revealCard.EnMesa = true + revealCard.Score = g.Players[g.TurnPlayerID].Hand.EnvidoScore() + } + allActions = append(allActions, revealCard) } allActions = append(allActions,