Files
fool-cardgame/tests/core/rules/serialization.spec.ts
T
ftcandClaude Opus 5 fddab60527 M1: ядро правил дурака (чистый TypeScript, без движка)
Детерминированный конечный автомат: apply(state, action) -> {state, events}.
Весь набор правил из GAMEPLAY.md проверяется headless-тестами.

- карты: Suit/Rank/Card (флайвейт, 36 штук), колода
- GameState + клонирование и сериализация в JSON
- действия (attack/defend/take/pass) и события для слоя отображения
- раздача, выбор первого атакующего, лимиты подкидывания,
  взятие карт, добор, выбывание игроков, определение дурака
- 64 теста, strict typecheck

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 10:38:49 +03:00

45 lines
2.1 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { deserializeState, serializeState } from '@core/rules/GameState';
import { apply, createGame, currentActor, legalActions } from '@core/rules/DurakRules';
describe('state serialization', () => {
it('round-trips a fresh game through JSON', () => {
const { state } = createGame({ players: ['you', 'bot'], seed: 4242 });
const restored = deserializeState(JSON.parse(JSON.stringify(serializeState(state))));
expect(serializeState(restored)).toEqual(serializeState(state));
});
it('round-trips a game in the middle of a turn', () => {
let { state } = createGame({ players: ['you', 'bot'], seed: 99 });
for (let i = 0; i < 5; i++) {
const actor = currentActor(state);
if (!actor) break;
state = apply(state, legalActions(state, actor)[0]).state;
}
const restored = deserializeState(JSON.parse(JSON.stringify(serializeState(state))));
expect(serializeState(restored)).toEqual(serializeState(state));
expect(currentActor(restored)).toBe(currentActor(state));
});
it('restores cards as the same flyweight instances', () => {
const { state } = createGame({ players: ['you', 'bot'], seed: 7 });
const restored = deserializeState(serializeState(state));
expect(restored.players[0].hand[0]).toBe(state.players[0].hand[0]);
expect(restored.trumpCard).toBe(state.trumpCard);
});
it('replays identically from a restored state', () => {
const { state } = createGame({ players: ['you', 'bot'], seed: 2001 });
const restored = deserializeState(serializeState(state));
// The same action object works on both, because cards are flyweights and
// an action carries no reference to the state it was generated from.
const actor = currentActor(state) as string;
const action = legalActions(state, actor)[0];
expect(serializeState(apply(restored, action).state))
.toEqual(serializeState(apply(state, action).state));
});
});