88f1da9f70
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
74 lines
2.6 KiB
TypeScript
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);
|
|
});
|
|
});
|