go_games_collection/durak_bot_test.go
2026-07-04 13:51:04 +03:00

99 lines
4.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import "testing"
func TestStalemateProtectionTerminatesGame(t *testing.T) {
g := NewDurakGame([]string{"A", "B"}, -1)
g.Talon = nil
g.TrumpSuit = Spades
g.AttackerIdx = 0
g.DefenderIdx = 1
// у обоих всегда есть по карте, а защищающийся никогда не может
// отбиться (у него всегда некозырная карта младше атакующей) —
// имитируем принудительное "взятие" много раз подряд напрямую
for i := 0; i < maxTakesWithoutBito+5 && g.Phase != DurakPhaseGameOver; i++ {
g.Players[0].Hand = []Card{c(Eight, Clubs), c(Nine, Diamonds)} // запасная карта, чтобы не выбыть
g.Players[1].Hand = []Card{c(Six, Hearts)}
g.attackCap = g.currentAttackCap()
g.Phase = DurakPhaseAttack
if err := g.ThrowIn(c(Eight, Clubs)); err != nil {
t.Fatalf("шаг %d: неожиданная ошибка подкидывания: %v", i, err)
}
if err := g.Take(); err != nil {
t.Fatalf("шаг %d: неожиданная ошибка взятия: %v", i, err)
}
if g.Phase != DurakPhaseGameOver {
// перед следующей итерацией восстанавливаем роли и стол
// вручную — в реальной игре это происходит по правилам
g.AttackerIdx, g.DefenderIdx = 0, 1
}
}
if g.Phase != DurakPhaseGameOver {
t.Fatalf("партия должна была принудительно завершиться из-за защиты от зацикливания")
}
if g.Result == nil || !g.Result.Stalemate {
t.Fatalf("ожидался результат Stalemate, получено: %+v", g.Result)
}
}
// TestDurakBotFullGames прогоняет много полных партий силами
// DurakBot для разного числа игроков (2-6) и проверяет, что игра
// всегда корректно завершается: без паники, без зависания, и с
// валидным результатом (ровно один проигравший либо ничья).
func TestDurakBotFullGames(t *testing.T) {
bot := DurakBot{}
for numPlayers := 2; numPlayers <= 6; numPlayers++ {
for game := 0; game < 30; game++ {
names := make([]string, numPlayers)
for i := range names {
names[i] = "Bot"
}
g := NewDurakGame(names, -1)
const maxSteps = 10000
steps := 0
for ; steps < maxSteps && g.Phase != DurakPhaseGameOver; steps++ {
if err := bot.PlayFullTurn(g); err != nil {
t.Fatalf("игроков %d, игра %d, шаг %d: ошибка бота: %v",
numPlayers, game, steps, err)
}
}
if steps >= maxSteps {
t.Fatalf("игроков %d, игра %d: партия не завершилась за %d шагов (возможное зависание)",
numPlayers, game, maxSteps)
}
if g.Result == nil {
t.Fatalf("игроков %d, игра %d: партия завершилась без результата", numPlayers, game)
}
if !g.Result.NoLoser && !g.Result.Stalemate {
if g.Result.Loser < 0 || g.Result.Loser >= numPlayers {
t.Fatalf("игроков %d, игра %d: некорректный индекс проигравшего %d",
numPlayers, game, g.Result.Loser)
}
}
// суммарно карты не должны теряться и не должны
// дублироваться: в талоне + на руках должно остаться
// ровно то, что не ушло в "бито" (отбой). Проверяем
// более простой инвариант — отсутствие дубликатов среди
// всех карт, которые сейчас на руках и в талоне.
seen := map[Card]bool{}
for _, p := range g.Players {
for _, c := range p.Hand {
if seen[c] {
t.Fatalf("игроков %d, игра %d: карта %v встретилась дважды", numPlayers, game, c)
}
seen[c] = true
}
}
for _, c := range g.Talon {
if seen[c] {
t.Fatalf("игроков %d, игра %d: карта %v встретилась дважды (в талоне и на руке)", numPlayers, game, c)
}
seen[c] = true
}
}
}
}