Skip to content

Commit

Permalink
Fix tests. Use Action.Enrich() pattern.
Browse files Browse the repository at this point in the history
  • Loading branch information
marianogappa committed Jul 25, 2024
1 parent 7a85a56 commit ff83ef1
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 142 deletions.
28 changes: 7 additions & 21 deletions examplebot/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@ func calculateEnvidoScore(gs truco.ClientGameState) int {

func calculateCardStrength(gs truco.Card) int {
specialValues := map[truco.Card]int{
{Suit: truco.ESPADA, Number: 1}: 19,
{Suit: truco.BASTO, Number: 1}: 18,
{Suit: truco.ESPADA, Number: 7}: 17,
{Suit: truco.ORO, Number: 7}: 16,
{Suit: truco.ESPADA, Number: 1}: 15,
{Suit: truco.BASTO, Number: 1}: 14,
{Suit: truco.ESPADA, Number: 7}: 13,
{Suit: truco.ORO, Number: 7}: 12,
}
if _, ok := specialValues[gs]; ok {
return specialValues[gs] - 4
return specialValues[gs]
}
if gs.Number <= 3 {
return gs.Number + 12 - 4
Expand All @@ -84,14 +84,6 @@ func faceoffResults(gs truco.ClientGameState) []int {
return results
}

func calculateTrucoHandChance(cards []truco.Card) float64 {
sum := 0.0
for _, card := range cards {
sum += float64(calculateCardStrength(card))
}
return sum / (15 + 14 + 13)
}

func canAnyEnvido(actions map[string]truco.Action) bool {
return len(filter(actions,
truco.NewActionSayEnvido(1),
Expand Down Expand Up @@ -299,13 +291,7 @@ func lowestCardThatBeats(card truco.Card, cards []truco.Card) truco.Card {
}

func cardsChance(cards []truco.Card) float64 {
divisor := float64(19.0)
if len(cards) == 2 {
divisor = 15.0 + 14.0
}
if len(cards) == 3 {
divisor = 15.0 + 14.0 + 13.0
}
divisor := float64([]float64{1, 15.0, 15.0 + 14.0, 15.0 + 14.0 + 13.0}[len(cards)])
sum := 0.0
for _, card := range cards {
sum += float64(calculateCardStrength(card))
Expand Down Expand Up @@ -575,7 +561,7 @@ func sonBuenas(gs truco.ClientGameState) truco.Action {
return truco.NewActionSaySonBuenas(gs.YouPlayerID)
}
func sonMejores(gs truco.ClientGameState) truco.Action {
return truco.NewActionSaySonMejores(0, gs.YouPlayerID)
return truco.NewActionSaySonMejores(gs.YouPlayerID)
}
func envidoNoQuiero(gs truco.ClientGameState) truco.Action {
return truco.NewActionSayEnvidoNoQuiero(gs.YouPlayerID)
Expand Down
66 changes: 6 additions & 60 deletions examplebot/bot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ func TestCalculateCardStrength(t *testing.T) {
card truco.Card
expected int
}{
{card: truco.Card{Suit: truco.ESPADA, Number: 1}, expected: 19},
{card: truco.Card{Suit: truco.ESPADA, Number: 2}, expected: 14},
{card: truco.Card{Suit: truco.ESPADA, Number: 3}, expected: 15},
{card: truco.Card{Suit: truco.ESPADA, Number: 4}, expected: 4},
{card: truco.Card{Suit: truco.ESPADA, Number: 1}, expected: 15},
{card: truco.Card{Suit: truco.ESPADA, Number: 2}, expected: 10},
{card: truco.Card{Suit: truco.ESPADA, Number: 3}, expected: 11},
{card: truco.Card{Suit: truco.ESPADA, Number: 4}, expected: 0},
}

for _, tc := range testCases {
Expand All @@ -26,44 +26,6 @@ func TestCalculateCardStrength(t *testing.T) {
}
}

func TestCalculateTrucoHandChance(t *testing.T) {
testCases := []struct {
cards []truco.Card
expected float64
}{
{
cards: []truco.Card{
{Suit: truco.ESPADA, Number: 1},
{Suit: truco.ESPADA, Number: 7},
{Suit: truco.BASTO, Number: 1},
},
expected: 1,
},
{
cards: []truco.Card{
{Suit: truco.BASTO, Number: 4},
{Suit: truco.COPA, Number: 4},
{Suit: truco.ORO, Number: 4},
},
expected: 0.0,
},
// Add more test cases here...
}

for _, tc := range testCases {
actual := calculateTrucoHandChance(tc.cards)
if !floatEquals(actual, tc.expected, 0.01) {
t.Errorf("calculateTrucoHandChance(%v) = %f, expected %f", tc.cards, actual, tc.expected)
}

}
}

// Function to compare floating-point numbers within a tolerance.
func floatEquals(a, b, tolerance float64) bool {
return math.Abs(a-b) < tolerance
}

func TestCanBeatCard(t *testing.T) {
testCases := []struct {
card truco.Card
Expand Down Expand Up @@ -662,7 +624,7 @@ func TestChanceOfWinningTruco(t *testing.T) {
expected: 0.733,
},
{
name: "Can't beat the opponent's revealed card, highest remaining card 3: (10+7-8)/(19+18-8)*0.66 = 20.4%% chance of winning",
name: "Can't beat the opponent's revealed card, highest remaining card 3: ((10+7-8)/(15+14))^2 = 9.63%% chance of winning",
gs: truco.ClientGameState{
YourRevealedCards: []truco.Card{},
YourUnrevealedCards: []truco.Card{
Expand All @@ -674,7 +636,7 @@ func TestChanceOfWinningTruco(t *testing.T) {
{Suit: truco.ORO, Number: 7},
},
},
expected: 0.204,
expected: 0.096,
},
{
name: "Tie at first revealed card, highest possible remaining card = 100.0%% chance of winning",
Expand Down Expand Up @@ -876,22 +838,6 @@ func TestChanceOfWinningTruco(t *testing.T) {
},
expected: 0.4,
},
{
name: "test",
gs: truco.ClientGameState{
YourRevealedCards: []truco.Card{
{Suit: truco.ESPADA, Number: 10},
{Suit: truco.ORO, Number: 1},
},
YourUnrevealedCards: []truco.Card{
{Suit: truco.BASTO, Number: 1},
},
TheirRevealedCards: []truco.Card{
{Suit: truco.COPA, Number: 4},
},
},
expected: 0.4,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
Expand Down
40 changes: 21 additions & 19 deletions truco/action_any_quiero.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ func (a ActionSayTrucoQuiero) IsPossible(g GameState) bool {
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)
isSonMejoresPossible = NewActionSaySonMejores(me).IsPossible(g)
isSayEnvidoScorePossible = NewActionSayEnvidoScore(me).IsPossible(g)
)
if isEnvidoQuieroPossible || isSonBuenasPossible || isSonMejoresPossible || isSayEnvidoScorePossible {
return false
Expand All @@ -107,8 +107,8 @@ func (a ActionSayTrucoNoQuiero) IsPossible(g GameState) bool {
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)
isSonMejoresPossible = NewActionSaySonMejores(me).IsPossible(g)
isSayEnvidoScorePossible = NewActionSayEnvidoScore(me).IsPossible(g)
)
if isEnvidoQuieroPossible || isSonBuenasPossible || isSonMejoresPossible || isSayEnvidoScorePossible {
return false
Expand Down Expand Up @@ -251,26 +251,28 @@ func (a ActionRevealEnvidoScore) YieldsTurn(g GameState) bool {
return false
}

func (a *ActionSayTrucoQuiero) withRequiresReminder(g GameState) Action {
if len(g.RoundsLog[g.RoundNumber].ActionsLog) == 0 {
a.RequiresReminder = false
return a
}
lastAction := _deserializeCurrentRoundLastAction(g)
// If the last action wasn't a truco action, then an envido sequence
// got in the middle of the truco sequence. A reminder is needed.
a.RequiresReminder = !slices.Contains[[]string]([]string{SAY_TRUCO, SAY_QUIERO_RETRUCO, SAY_QUIERO_VALE_CUATRO}, lastAction.GetName())
return a
func (a *ActionSayTrucoQuiero) Enrich(g GameState) {
a.RequiresReminder = _doesTrucoActionRequireReminder(g)
}

func (a *ActionSayTrucoNoQuiero) Enrich(g GameState) {
a.RequiresReminder = _doesTrucoActionRequireReminder(g)
}

func (a *ActionSayTrucoNoQuiero) withRequiresReminder(g GameState) Action {
func _doesTrucoActionRequireReminder(g GameState) bool {
if len(g.RoundsLog[g.RoundNumber].ActionsLog) == 0 {
a.RequiresReminder = false
return a
return false
}
lastAction := _deserializeCurrentRoundLastAction(g)
// If the last action wasn't a truco action, then an envido sequence
// got in the middle of the truco sequence. A reminder is needed.
a.RequiresReminder = !slices.Contains[[]string]([]string{SAY_TRUCO, SAY_QUIERO_RETRUCO, SAY_QUIERO_VALE_CUATRO}, lastAction.GetName())
return a
return !slices.Contains[[]string]([]string{SAY_TRUCO, SAY_QUIERO_RETRUCO, SAY_QUIERO_VALE_CUATRO}, lastAction.GetName())
}

func (a *ActionSayEnvidoScore) Enrich(g GameState) {
a.Score = g.Players[a.PlayerID].Hand.EnvidoScore()
}

func (a *ActionRevealEnvidoScore) Enrich(g GameState) {
a.Score = g.Players[a.PlayerID].Hand.EnvidoScore()
}
2 changes: 1 addition & 1 deletion truco/action_confirm_round_ended.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type ActionConfirmRoundFinished struct {

func (a ActionConfirmRoundFinished) IsPossible(g GameState) bool {
return g.IsRoundFinished &&
!NewActionRevealEnvidoScore(a.PlayerID, 0).IsPossible(g) &&
!NewActionRevealEnvidoScore(a.PlayerID).IsPossible(g) &&
!g.RoundFinishedConfirmedPlayerIDs[a.PlayerID]
}

Expand Down
7 changes: 7 additions & 0 deletions truco/action_reveal_card.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,10 @@ func (a *ActionRevealCard) Run(g *GameState) error {
func (a ActionRevealCard) YieldsTurn(g GameState) bool {
return g.CardRevealSequence.YieldsTurn(g)
}

func (a *ActionRevealCard) Enrich(g GameState) {
if g.canAwardEnvidoPoints(Hand{Revealed: append(g.Players[g.TurnPlayerID].Hand.Revealed, a.Card)}) {
a.EnMesa = true
a.Score = g.Players[g.TurnPlayerID].Hand.EnvidoScore()
}
}
4 changes: 4 additions & 0 deletions truco/action_son_mejores.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,7 @@ func (a ActionSaySonMejores) YieldsTurn(g GameState) bool {
}
return g.TurnPlayerID != g.EnvidoSequence.StartingPlayerID
}

func (a *ActionSaySonMejores) Enrich(g GameState) {
a.Score = g.Players[a.PlayerID].Hand.EnvidoScore()
}
23 changes: 17 additions & 6 deletions truco/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ func (a act) GetPlayerID() int {
return a.PlayerID
}

// By default, actions don't need to be enriched.
func (a act) Enrich(g GameState) {}

func (a act) String() string {
name := strings.ReplaceAll(strings.TrimPrefix(a.Name, "say_"), "_", " ")
return fmt.Sprintf("Player %v says %v", a.PlayerID, name)
Expand Down Expand Up @@ -49,8 +52,8 @@ func NewActionSayEnvidoQuiero(playerID int) Action {
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}
func NewActionSayEnvidoScore(playerID int) Action {
return &ActionSayEnvidoScore{act: act{Name: SAY_ENVIDO_SCORE, PlayerID: playerID}}
}

func NewActionSayTrucoQuiero(playerID int) Action {
Expand All @@ -77,14 +80,22 @@ func NewActionSaySonBuenas(playerID int) Action {
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}
func NewActionSaySonMejores(playerID int) Action {
return &ActionSaySonMejores{act: act{Name: SAY_SON_MEJORES, PlayerID: playerID}}
}

func NewActionRevealCard(card Card, playerID int) Action {
return &ActionRevealCard{act: act{Name: REVEAL_CARD, PlayerID: playerID}, Card: card}
}

func NewActionsRevealCards(playerID int, gameState GameState) []Action {
actions := []Action{}
for _, card := range gameState.Players[playerID].Hand.Unrevealed {
actions = append(actions, NewActionRevealCard(card, playerID))
}
return actions
}

func NewActionSayMeVoyAlMazo(playerID int) Action {
return &ActionSayMeVoyAlMazo{act: act{Name: SAY_ME_VOY_AL_MAZO, PlayerID: playerID}}
}
Expand All @@ -93,8 +104,8 @@ func NewActionConfirmRoundFinished(playerID int) Action {
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}
func NewActionRevealEnvidoScore(playerID int) Action {
return &ActionRevealEnvidoScore{act: act{Name: REVEAL_ENVIDO_SCORE, PlayerID: playerID}}
}

func (a ActionSaySonMejores) String() string {
Expand Down
21 changes: 16 additions & 5 deletions truco/envido_sequence_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestEnvidoSequence(t *testing.T) {
name: "cannot start with son_mejores",
steps: []testStep{
{
action: NewActionSaySonMejores(10, 0),
action: NewActionSaySonMejores(0),
expectedIsValid: false,
},
},
Expand All @@ -74,7 +74,7 @@ func TestEnvidoSequence(t *testing.T) {
},
},
{
name: "basic envido finished sequence with son buenas",
name: "basic envido finished sequence with son mejores",
steps: []testStep{
{
action: NewActionSayEnvido(0),
Expand All @@ -87,7 +87,12 @@ func TestEnvidoSequence(t *testing.T) {
expectedPlayerTurnAfterRunning: 0,
},
{
action: NewActionSaySonBuenas(0),
action: NewActionSayEnvidoScore(0),
expectedIsValid: true,
expectedPlayerTurnAfterRunning: 1,
},
{
action: NewActionSaySonMejores(1),
expectedIsValid: true,
expectedPlayerTurnAfterRunning: 0, // doesn't yield turn because envido is over, so player who started gets to play
expectedIsFinishedAfterRunning: true,
Expand All @@ -110,10 +115,15 @@ func TestEnvidoSequence(t *testing.T) {
{
action: NewActionSayEnvidoQuiero(0),
expectedIsValid: true,
expectedPlayerTurnAfterRunning: 0,
},
{
action: NewActionSayEnvidoScore(0),
expectedIsValid: true,
expectedPlayerTurnAfterRunning: 1,
},
{
action: NewActionSaySonMejores(31, 1),
action: NewActionSaySonMejores(1),
expectedIsValid: true,
expectedPlayerTurnAfterRunning: 1, // doesn't yield turn because envido is over, so player who started gets to play
expectedIsFinishedAfterRunning: true,
Expand Down Expand Up @@ -145,8 +155,9 @@ func TestEnvidoSequence(t *testing.T) {
}
}

step.action.Enrich(*gameState)
err := gameState.RunAction(step.action)
require.NoError(t, err)
require.NoError(t, err, "at step %v", i)

if step.ignoreAction {
continue
Expand Down
Loading

0 comments on commit ff83ef1

Please sign in to comment.