boardgame-core/tests/samples/boop.test.ts

145 lines
6.1 KiB
TypeScript
Raw Normal View History

2026-04-04 21:57:25 +08:00
import { describe, it, expect } from 'vitest';
import { registry, createInitialState, BoopState } from '@/samples/boop';
import { createGameContext } from '@/index';
import type { PromptEvent } from '@/utils/command';
2026-04-02 16:53:17 +08:00
2026-04-04 21:57:25 +08:00
function createTestContext() {
const ctx = createGameContext(registry, createInitialState());
return { registry, ctx };
}
function waitForPrompt(ctx: ReturnType<typeof createTestContext>['ctx']): Promise<PromptEvent> {
return new Promise(resolve => {
ctx._commands.on('prompt', resolve);
});
2026-04-02 16:53:17 +08:00
}
2026-04-04 21:53:37 +08:00
describe('Boop Game', () => {
describe('Setup', () => {
it('should create initial state correctly', () => {
2026-04-04 21:57:25 +08:00
const state = createInitialState();
2026-04-04 21:53:37 +08:00
expect(state.currentPlayer).toBe('white');
expect(state.winner).toBeNull();
expect(state.regions.board).toBeDefined();
expect(state.regions.white).toBeDefined();
expect(state.regions.black).toBeDefined();
2026-04-04 21:57:25 +08:00
2026-04-04 21:53:37 +08:00
// 8 kittens per player
const whiteKittens = Object.values(state.pieces).filter(p => p.player === 'white' && p.type === 'kitten');
const blackKittens = Object.values(state.pieces).filter(p => p.player === 'black' && p.type === 'kitten');
expect(whiteKittens.length).toBe(8);
expect(blackKittens.length).toBe(8);
2026-04-04 21:57:25 +08:00
2026-04-04 21:53:37 +08:00
// 8 cats per player (initially in box)
const whiteCats = Object.values(state.pieces).filter(p => p.player === 'white' && p.type === 'cat');
const blackCats = Object.values(state.pieces).filter(p => p.player === 'black' && p.type === 'cat');
expect(whiteCats.length).toBe(8);
expect(blackCats.length).toBe(8);
2026-04-04 21:57:25 +08:00
2026-04-04 21:53:37 +08:00
// All cats should be in box (regionId = '')
whiteCats.forEach(cat => expect(cat.regionId).toBe(''));
blackCats.forEach(cat => expect(cat.regionId).toBe(''));
2026-04-04 21:57:25 +08:00
2026-04-04 21:53:37 +08:00
// Kittens should be in player supplies
whiteKittens.forEach(k => expect(k.regionId).toBe('white'));
blackKittens.forEach(k => expect(k.regionId).toBe('black'));
});
});
describe('Place and Boop Commands', () => {
it('should place a kitten via play command', async () => {
2026-04-04 21:57:25 +08:00
const { ctx } = createTestContext();
// Use turn command instead of setup which runs indefinitely
const promptPromise = waitForPrompt(ctx);
const runPromise = ctx.run('turn white');
const promptEvent = await promptPromise;
expect(promptEvent).not.toBeNull();
expect(promptEvent.schema.name).toBe('play');
// Place a kitten at position 2,2
const error = promptEvent.tryCommit({ name: 'play', params: ['white', 2, 2, 'kitten'], options: {}, flags: {} });
expect(error).toBeNull();
const result = await runPromise;
expect(result.success).toBe(true);
2026-04-04 21:53:37 +08:00
2026-04-04 21:57:25 +08:00
const state = ctx.value;
2026-04-04 21:53:37 +08:00
// Should have placed a piece on the board
const boardPieces = Object.keys(state.regions.board.partMap);
expect(boardPieces.length).toBeGreaterThan(0);
2026-04-04 21:57:25 +08:00
2026-04-04 21:53:37 +08:00
// Should have one less kitten in supply
const whiteSupply = state.regions.white.childIds.filter(id => state.pieces[id].type === 'kitten');
expect(whiteSupply.length).toBe(7);
});
});
describe('Boop Mechanics', () => {
it('should boop adjacent pieces away from placement', async () => {
2026-04-04 21:57:25 +08:00
const { ctx } = createTestContext();
2026-04-04 21:53:37 +08:00
// White places at 2,2
2026-04-04 21:57:25 +08:00
let promptPromise = waitForPrompt(ctx);
let runPromise = ctx.run('turn white');
let promptEvent = await promptPromise;
let error = promptEvent.tryCommit({ name: 'play', params: ['white', 2, 2, 'kitten'], options: {}, flags: {} });
expect(error).toBeNull();
let result = await runPromise;
expect(result.success).toBe(true);
2026-04-04 21:53:37 +08:00
// Black places at 2,3, which will boop white's piece
2026-04-04 21:57:25 +08:00
promptPromise = waitForPrompt(ctx);
runPromise = ctx.run('turn black');
promptEvent = await promptPromise;
error = promptEvent.tryCommit({ name: 'play', params: ['black', 2, 3, 'kitten'], options: {}, flags: {} });
expect(error).toBeNull();
result = await runPromise;
expect(result.success).toBe(true);
const state = ctx.value;
2026-04-04 21:53:37 +08:00
// Check that pieces were placed
const boardPieceCount = Object.keys(state.regions.board.partMap).length;
expect(boardPieceCount).toBeGreaterThanOrEqual(1);
});
it('should handle pieces being booped off the board', async () => {
2026-04-04 21:57:25 +08:00
const { ctx } = createTestContext();
2026-04-04 21:53:37 +08:00
// White places at corner
2026-04-04 21:57:25 +08:00
const promptPromise = waitForPrompt(ctx);
const runPromise = ctx.run('turn white');
const promptEvent = await promptPromise;
const error = promptEvent.tryCommit({ name: 'play', params: ['white', 0, 0, 'kitten'], options: {}, flags: {} });
expect(error).toBeNull();
const result = await runPromise;
expect(result.success).toBe(true);
const state = ctx.value;
2026-04-04 21:53:37 +08:00
// Verify placement
expect(state.regions.board.partMap['0,0']).toBeDefined();
});
});
describe('Full Game Flow', () => {
it('should play a turn and switch players', async () => {
2026-04-04 21:57:25 +08:00
const { ctx } = createTestContext();
2026-04-04 21:53:37 +08:00
// White's turn - place at 2,2
2026-04-04 21:57:25 +08:00
let promptPromise = waitForPrompt(ctx);
let runPromise = ctx.run('turn white');
let prompt = await promptPromise;
const error1 = prompt.tryCommit({ name: 'play', params: ['white', 2, 2, 'kitten'], options: {}, flags: {} });
expect(error1).toBeNull();
let result = await runPromise;
expect(result.success).toBe(true);
const stateAfterWhite = ctx.value;
2026-04-04 21:53:37 +08:00
// Should have placed a piece
expect(stateAfterWhite.regions.board.partMap['2,2']).toBeDefined();
});
2026-04-02 16:53:17 +08:00
});
});