import { describe, it, expect } from 'vitest'; import { createGameContext } from '../../src/core/context'; import { registerTicTacToeCommands, startTicTacToe, type TicTacToeState } from '../../src/samples/tic-tac-toe'; describe('Tic-Tac-Toe', () => { function createGame() { const game = createGameContext(); registerTicTacToeCommands(game); return game; } function getBoardState(game: ReturnType) { return game.latestContext('tic-tac-toe')!.value; } it('should initialize the board and start the game', async () => { const game = createGame(); startTicTacToe(game); await new Promise(resolve => setTimeout(resolve, 100)); 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', async () => { const game = createGame(); startTicTacToe(game); await new Promise(resolve => setTimeout(resolve, 100)); game.enqueueAll([ 'play X 0 0', 'play O 0 1', 'play X 1 0', 'play O 1 1', 'play X 2 0', ]); await new Promise(resolve => setTimeout(resolve, 200)); const state = getBoardState(game); expect(state.winner).toBe('X'); expect(state.moveCount).toBe(5); }); it('should reject out-of-bounds moves', async () => { const game = createGame(); startTicTacToe(game); await new Promise(resolve => setTimeout(resolve, 100)); const beforeCount = getBoardState(game).moveCount; game.enqueueAll([ 'play X 5 5', 'play X -1 0', 'play X 3 3', ]); await new Promise(resolve => setTimeout(resolve, 200)); expect(getBoardState(game).moveCount).toBe(beforeCount); }); it('should reject moves on occupied cells', async () => { const game = createGame(); startTicTacToe(game); await new Promise(resolve => setTimeout(resolve, 100)); game.enqueue('play X 1 1'); await new Promise(resolve => setTimeout(resolve, 100)); expect(getBoardState(game).moveCount).toBe(1); game.enqueue('play O 1 1'); await new Promise(resolve => setTimeout(resolve, 100)); expect(getBoardState(game).moveCount).toBe(1); }); it('should ignore moves after game is over', async () => { const game = createGame(); startTicTacToe(game); await new Promise(resolve => setTimeout(resolve, 100)); game.enqueueAll([ 'play X 0 0', 'play O 0 1', 'play X 1 0', 'play O 1 1', 'play X 2 0', ]); await new Promise(resolve => setTimeout(resolve, 200)); expect(getBoardState(game).winner).toBe('X'); const moveCountAfterWin = getBoardState(game).moveCount; game.enqueueAll([ 'play X 2 1', 'play O 2 2', ]); await new Promise(resolve => setTimeout(resolve, 200)); expect(getBoardState(game).moveCount).toBe(moveCountAfterWin); }); it('should detect a draw', async () => { const game = createGame(); startTicTacToe(game); await new Promise(resolve => setTimeout(resolve, 100)); game.enqueueAll([ 'play X 1 1', 'play O 0 0', 'play X 0 2', 'play O 2 0', 'play X 2 2', 'play O 0 1', 'play X 1 0', 'play O 1 2', 'play X 2 1', ]); await new Promise(resolve => setTimeout(resolve, 300)); 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', async () => { const game = createGame(); startTicTacToe(game); await new Promise(resolve => setTimeout(resolve, 100)); game.enqueue('play X 1 2'); await new Promise(resolve => setTimeout(resolve, 100)); 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]); }); });