/** * Партии бот-бот: детерминизм (одинаковый seed → идентичная партия), * легальность каждого хода и завершение за ≤ 200 ходов (property-style * на запиненных seed’ах; проверка легальности — внутри playBotGame через * applyMove, нелегальный ход = ошибка теста). */ import { describe, expect, it } from 'vitest'; import type { AiLevel } from './index.js'; import { playBotGame } from './test-utils.js'; const MAX_MOVES_9x9 = 200; describe('детерминизм', () => { it('одинаковый seed → идентичные последовательности ходов (два прогона)', () => { for (const seed of [7, 42, 2026]) { const first = playBotGame(seed, 3, 9, MAX_MOVES_9x9); const second = playBotGame(seed, 3, 9, MAX_MOVES_9x9); expect(second.moves).toEqual(first.moves); expect(second.finalState.grid).toEqual(first.finalState.grid); } }); }); describe('легальность и завершение (property, запиненные seed’ы)', () => { const levels: readonly AiLevel[] = [1, 2, 3]; const cases = Array.from({ length: 12 }, (_, index) => ({ seed: index + 1, level: levels[index % levels.length] ?? 1, })); for (const { seed, level } of cases) { it(`партия seed=${seed} ур.${level}: все ходы легальны, over за ≤ ${MAX_MOVES_9x9}`, () => { const game = playBotGame(seed, level, 9, MAX_MOVES_9x9); expect(game.finalState.over).toBe(true); expect(game.moves.length).toBeLessThanOrEqual(MAX_MOVES_9x9); // Партия завершается двумя пасами подряд. expect(game.moves[game.moves.length - 1]).toBe('pass'); expect(game.moves[game.moves.length - 2]).toBe('pass'); }); } });