import { describe, it, expect } from 'vitest'; import { createGameContext } from '../../src/core/context'; import { registerTicTacToeRules, startTicTacToe, type TicTacToeState } from '../../src/samples/tic-tac-toe'; describe('Tic-Tac-Toe', () => { function createGame() { const game = createGameContext(); registerTicTacToeRules(game); return game; } function getBoardState(game: ReturnType) { return game.latestContext('tic-tac-toe')!.value; } it('should initialize the board and start the game', () => { const game = createGame(); startTicTacToe(game); const state = getBoardState(game); expect(state.currentPlayer).toBe('X'); expect(state.winner).toBeNull(); expect(state.moveCount).toBe(0); const board = game.regions.get('board'); expect(board.value.axes).toHaveLength(2); expect(board.value.axes[0].name).toBe('x'); expect(board.value.axes[1].name).toBe('y'); }); it('should play moves and determine a winner', () => { const game = createGame(); startTicTacToe(game); // X wins with column 0 game.dispatchCommand('play X 0 0'); game.dispatchCommand('play O 0 1'); game.dispatchCommand('play X 1 0'); game.dispatchCommand('play O 1 1'); game.dispatchCommand('play X 2 0'); const state = getBoardState(game); expect(state.winner).toBe('X'); expect(state.moveCount).toBe(5); }); it('should reject out-of-bounds moves', () => { const game = createGame(); startTicTacToe(game); const beforeCount = getBoardState(game).moveCount; game.dispatchCommand('play X 5 5'); game.dispatchCommand('play X -1 0'); game.dispatchCommand('play X 3 3'); expect(getBoardState(game).moveCount).toBe(beforeCount); }); it('should reject moves on occupied cells', () => { const game = createGame(); startTicTacToe(game); game.dispatchCommand('play X 1 1'); expect(getBoardState(game).moveCount).toBe(1); // Try to play on the same cell game.dispatchCommand('play O 1 1'); expect(getBoardState(game).moveCount).toBe(1); }); it('should ignore moves after game is over', () => { const game = createGame(); startTicTacToe(game); // X wins game.dispatchCommand('play X 0 0'); game.dispatchCommand('play O 0 1'); game.dispatchCommand('play X 1 0'); game.dispatchCommand('play O 1 1'); game.dispatchCommand('play X 2 0'); expect(getBoardState(game).winner).toBe('X'); const moveCountAfterWin = getBoardState(game).moveCount; // Try to play more game.dispatchCommand('play X 2 1'); game.dispatchCommand('play O 2 2'); expect(getBoardState(game).moveCount).toBe(moveCountAfterWin); }); it('should detect a draw', () => { const game = createGame(); startTicTacToe(game); // Fill board with no winner (cat's game) // X: (1,1), (0,2), (2,2), (1,0), (2,1) // O: (0,0), (2,0), (0,1), (1,2) game.dispatchCommand('play X 1 1'); // X game.dispatchCommand('play O 0 0'); // O game.dispatchCommand('play X 0 2'); // X game.dispatchCommand('play O 2 0'); // O game.dispatchCommand('play X 2 2'); // X game.dispatchCommand('play O 0 1'); // O game.dispatchCommand('play X 1 0'); // X game.dispatchCommand('play O 1 2'); // O game.dispatchCommand('play X 2 1'); // X (last move, draw) const state = getBoardState(game); expect(state.winner).toBe('draw'); expect(state.moveCount).toBe(9); }); it('should place parts on the board region at correct positions', () => { const game = createGame(); startTicTacToe(game); game.dispatchCommand('play X 1 2'); const board = game.regions.get('board'); expect(board.value.children).toHaveLength(1); const piece = board.value.children[0].value; expect(piece.position).toEqual([1, 2]); }); });