Improve AI to stop selling everything.
This commit is contained in:
@@ -306,3 +306,62 @@ func TestDecideShopNeverSellsLastPet(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecideShopKeepsHealthyBoard guards against the slow-bleed bug: with its
|
||||
// coins spent, the bot used to find that turning its worst pet into an apple
|
||||
// scored marginally *better* than passing (the apple buffs a survivor for one
|
||||
// battle; the permanent loss of a body barely dented the squashed future-value
|
||||
// term). Repeated every shop turn, that shed the board down to a single pet and
|
||||
// an all-apple hand — an automatic loss, since apples don't carry between
|
||||
// rounds. A capable bot facing a beatable opponent must now overwhelmingly
|
||||
// prefer keeping its four pets over selling one for a throwaway apple.
|
||||
func TestDecideShopKeepsHealthyBoard(t *testing.T) {
|
||||
pet := func(id, name string, tier, power int, suit game.Suit) game.Card {
|
||||
return game.Card{ID: id, Kind: game.KindPet, Name: name, Tier: tier, Power: power, Suit: suit}
|
||||
}
|
||||
v := &game.View{
|
||||
Phase: game.PhaseShop,
|
||||
Round: 3, // mid-game: future value still carries real weight
|
||||
MaxRounds: game.MaxRounds,
|
||||
MaxPets: game.MaxPets,
|
||||
Pack: game.DefaultPack,
|
||||
YouSeat: 0,
|
||||
Turn: 0,
|
||||
PrioritySeat: 0,
|
||||
DeckCounts: make([]int, game.MaxRounds+1),
|
||||
Players: []game.PlayerView{
|
||||
{Seat: 0, Coins: 0, PetCount: 4, DeckSize: 4, // coins spent: pass vs sell
|
||||
Deck: []game.Card{
|
||||
pet("p1", "Dog", 3, 3, game.SuitRed),
|
||||
pet("p2", "Sheep", 3, 2, game.SuitBlue),
|
||||
pet("p3", "Ant", 1, 2, game.SuitYellow),
|
||||
pet("p4", "Cricket", 1, 1, game.SuitRed),
|
||||
}},
|
||||
{Seat: 1, PetCount: 4, DeckSize: 4},
|
||||
},
|
||||
}
|
||||
// A comparable opponent, so battles are genuinely competitive — selling is
|
||||
// a real temptation, not a hopeless-position tie-break.
|
||||
mem := &Memory{}
|
||||
mem.Opp.Seat = 1
|
||||
for i, n := range []string{"Dog", "Sheep", "Ant", "Cricket"} {
|
||||
mem.Opp.Known = append(mem.Opp.Known, pet(fmt.Sprintf("o%d", i), n, 3, 3, game.SuitRed))
|
||||
}
|
||||
|
||||
bot := New(1.0) // a capable bot should almost never make this trade
|
||||
passes, sells := 0, 0
|
||||
const iters = 300
|
||||
for range iters {
|
||||
switch bot.decideShop(v, mem).Type {
|
||||
case "pass":
|
||||
passes++
|
||||
case "sell":
|
||||
sells++
|
||||
}
|
||||
}
|
||||
// Keeping the board must dominate: pre-fix this scenario went the other way
|
||||
// (selling outnumbered passing). The generous margin absorbs rollout noise.
|
||||
if passes < 4*sells {
|
||||
t.Errorf("bot sheds a healthy board: pass=%d sell=%d (want pass >> sell)", passes, sells)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user