import { describe, expect, it } from 'vitest'; import { SCENES, sceneCells } from './lessons'; import { frozen, hasLegalMove, inCheck, kindOf, legalMovesFrom, movesFrom, playOn, square } from './board'; import { CALLOUTS, TOLD_MOVES, toldStates } from './told'; describe('the lessons', () => { it('every scene’s move is legal for the side to move, and only the asked-for moves count', () => { for (const scene of SCENES) { const cells = sceneCells(scene); for (const [from, to] of scene.moves) { const legal = legalMovesFrom(cells, square(from), 'printed'); expect(legal, `${scene.id}: ${from} to ${to}`).toContain(square(to)); } // The piece asked for is the player's. for (const [from] of scene.moves) expect(cells[square(from)][0], scene.id).toBe(scene.side === 'gold' ? 'g' : 's'); // The scene draws every board its move touches. for (const [from, to] of scene.moves) { expect(scene.levels, scene.id).toContain(Number(from[0])); expect(scene.levels, scene.id).toContain(Number(to[0])); } } }); it('the scenes do what their words say', () => { const at = (id: string) => SCENES.find((s) => s.id === id)!; const play = (id: string) => { const s = at(id); const cells = sceneCells(s); const [from, to] = s.moves[0]; return { cells, after: playOn(cells, square(from), square(to)), s }; }; // The Warrior's step is blocked and its capture is not. expect(movesFrom(sceneCells(at('warrior')), square('2c2'))).toEqual([square('2d3')]); // The Hero leaps the Warrior and takes the Mage. expect(play('hero').after.captured).toBe('M'); // The Mage comes down two boards onto the Basilisk. expect(play('mage').after.captured).toBe('B'); // The Paladin vaults over the Dwarf at 1c5. expect(play('paladin').after.captured).toBe('D'); // The Sylph's dive and the Dragon's strike take what they say. expect(play('sylph').after.captured).toBe('O'); const dragon = play('dragon'); expect(dragon.after.afar).toBe(true); expect(dragon.after.captured).toBe('M'); expect(dragon.after.cells[square('3c4')]).toBe('gR'); // The Basilisk's arrival freezes the Mage above. const basilisk = play('basilisk'); expect(frozen(basilisk.after.cells, square('2e4'))).toBe(true); expect(movesFrom(basilisk.after.cells, square('2e4'))).toEqual([]); // The Elemental climbs onto the Thief. expect(play('elemental').after.captured).toBe('T'); // The King is in check before it moves, and out of it after either move; up or down leaves it off the land. const king = sceneCells(at('king')); expect(inCheck(king, 'gold')).toBe(true); expect(legalMovesFrom(king, square('2g1'), 'printed').sort()).toEqual([square('1g1'), square('2f1'), square('2h1'), square('3g1')].sort()); // The last scene is mate in one by either listed move, and by no other. const mate = play('mate'); for (const [from, to] of mate.s.moves) { const after = playOn(mate.cells, square(from), square(to)).cells; expect(inCheck(after, 'scarlet'), `${from} to ${to}`).toBe(true); expect(hasLegalMove(after, 'scarlet', 'printed'), `${from} to ${to}`).toBe(false); } const listed = new Set(mate.s.moves.map(([a, b]) => `${square(a)}-${square(b)}`)); for (const to of legalMovesFrom(mate.cells, square('2a1'), 'printed')) { if (listed.has(`${square('2a1')}-${to}`)) continue; const after = playOn(mate.cells, square('2a1'), to).cells; expect(inCheck(after, 'scarlet') && !hasLegalMove(after, 'scarlet', 'printed'), `unlisted mate by the Oliphant to ${to}`).toBe(false); } expect(kindOf(mate.cells[square('2d4')])).toBe('K'); expect(frozen(mate.cells, square('2d4'))).toBe(true); }); }); describe('the game told', () => { it('plays through the engine from the opening to a checkmate, and every callout sits on a move', () => { const states = toldStates(); expect(states).toHaveLength(TOLD_MOVES.length + 1); const last = states[states.length - 1]; expect(last.end).toBe('mate'); expect(last.over?.winner).toBe('A'); for (const n of Object.keys(CALLOUTS).map(Number)) expect(n).toBeLessThanOrEqual(TOLD_MOVES.length); // The things the callouts say happened. expect(last.moves[8].captured).toBe('R'); expect(last.moves[16].kind).toBe('B'); expect(last.moves[20].captured).toBe('H'); expect(last.moves[23].captured).toBe('H'); expect(last.moves[26].captured).toBe('S'); expect(last.moves[26].check).toBe(true); }); });