Files
duplicate_chess/src/engine/game.test.ts
T
claude (duplicate_chess) 88f1da9f70 feat(engine): DuplicateGame core — boards, history, undo, draw clocks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 00:46:38 -04:00

74 lines
2.6 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { DuplicateGame } from './game';
const START = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR';
function placement(fen: string) {
return fen.split(' ')[0];
}
describe('DuplicateGame', () => {
it('starts with four boards in the standard position, North to move', () => {
const g = new DuplicateGame();
expect(g.ply).toBe(0);
expect(g.currentPlayer).toBe('N');
for (const id of ['NW', 'NE', 'SW', 'SE'] as const) {
expect(placement(g.boards[id].fen())).toBe(START);
}
});
it("applies a synchronized move to the current player's two boards only", () => {
const g = new DuplicateGame();
g.applyMove({ from: 'e2', to: 'e4' }); // North
expect(g.ply).toBe(1);
expect(g.currentPlayer).toBe('S');
expect(placement(g.boards.NW.fen())).toContain('4P3'); // pawn advanced
expect(placement(g.boards.NE.fen())).toContain('4P3');
expect(placement(g.boards.SW.fen())).toBe(START); // untouched
expect(placement(g.boards.SE.fen())).toBe(START);
});
it('cycles the current player N -> S -> E -> W -> N', () => {
const g = new DuplicateGame();
g.applyMove({ from: 'e2', to: 'e4' }); // N
g.applyMove({ from: 'e2', to: 'e4' }); // S
g.applyMove({ from: 'e7', to: 'e5' }); // E
g.applyMove({ from: 'e7', to: 'e5' }); // W
expect(g.currentPlayer).toBe('N');
expect(g.ply).toBe(4);
});
it("throws on a move not legal on both of the player's boards", () => {
const g = new DuplicateGame();
expect(() => g.applyMove({ from: 'e2', to: 'e5' })).toThrow();
});
it('undo removes the last move and restores the position', () => {
const g = new DuplicateGame();
g.applyMove({ from: 'e2', to: 'e4' });
g.undo();
expect(g.ply).toBe(0);
expect(g.currentPlayer).toBe('N');
expect(placement(g.boards.NW.fen())).toBe(START);
});
it('rebuilds from a history array passed to the constructor', () => {
const g = new DuplicateGame([
{ player: 'N', from: 'e2', to: 'e4' },
{ player: 'S', from: 'e2', to: 'e4' },
]);
expect(g.ply).toBe(2);
expect(g.currentPlayer).toBe('E');
});
it('resets the progress clock on a pawn move or capture', () => {
const g = new DuplicateGame();
g.applyMove({ from: 'e2', to: 'e4' }); // pawn move -> clock stays 0
expect(g.pliesSinceProgress).toBe(0);
g.applyMove({ from: 'e2', to: 'e4' }); // pawn move
expect(g.pliesSinceProgress).toBe(0);
g.applyMove({ from: 'g8', to: 'f6' }); // knight move -> clock increments
expect(g.pliesSinceProgress).toBe(1);
});
});