refactor: transition to non child entity format

This commit is contained in:
2026-04-03 12:46:50 +08:00
parent cf7cfcd86b
commit eb0ebf5411
8 changed files with 425 additions and 478 deletions
+44 -42
View File
@@ -36,7 +36,7 @@ function waitForPrompt(ctx: ReturnType<typeof createTestContext>['ctx']): Promis
}
function getParts(state: Entity<BoopState>) {
return state.value.board.value.children;
return state.value.pieces;
}
describe('Boop - helper functions', () => {
@@ -81,8 +81,8 @@ describe('Boop - helper functions', () => {
const part = getPartAt(state, 2, 2);
expect(part).not.toBeNull();
if (part) {
expect(part.value.player).toBe('black');
expect(part.value.pieceType).toBe('kitten');
expect(part.player).toBe('black');
expect(part.pieceType).toBe('kitten');
}
});
});
@@ -95,9 +95,9 @@ describe('Boop - helper functions', () => {
const parts = getParts(state);
expect(parts.length).toBe(1);
expect(parts[0].value.position).toEqual([2, 3]);
expect(parts[0].value.player).toBe('white');
expect(parts[0].value.pieceType).toBe('kitten');
expect(parts[0].position).toEqual([2, 3]);
expect(parts[0].player).toBe('white');
expect(parts[0].pieceType).toBe('kitten');
});
it('should name piece white-kitten-1', () => {
@@ -130,23 +130,23 @@ describe('Boop - helper functions', () => {
const state = getState(ctx);
placePiece(state, 0, 0, 'white', 'kitten');
expect(state.value.players.white.value.kitten.supply).toBe(7);
expect(state.value.players.black.value.kitten.supply).toBe(8);
expect(state.value.players.white.kitten.supply).toBe(7);
expect(state.value.players.black.kitten.supply).toBe(8);
placePiece(state, 0, 1, 'black', 'kitten');
expect(state.value.players.white.value.kitten.supply).toBe(7);
expect(state.value.players.black.value.kitten.supply).toBe(7);
expect(state.value.players.white.kitten.supply).toBe(7);
expect(state.value.players.black.kitten.supply).toBe(7);
});
it('should decrement the correct player cat supply', () => {
const { ctx } = createTestContext();
const state = getState(ctx);
state.produce(s => {
s.players.white.value.cat.supply = 3;
s.players.white.cat.supply = 3;
});
placePiece(state, 0, 0, 'white', 'cat');
expect(state.value.players.white.value.cat.supply).toBe(2);
expect(state.value.players.white.cat.supply).toBe(2);
});
it('should add piece to board region children', () => {
@@ -155,7 +155,7 @@ describe('Boop - helper functions', () => {
placePiece(state, 1, 1, 'white', 'kitten');
const board = getBoardRegion(state);
expect(board.value.children.length).toBe(1);
expect(board.childIds.length).toBe(1);
});
it('should generate unique IDs for pieces', () => {
@@ -178,11 +178,11 @@ describe('Boop - helper functions', () => {
placePiece(state, 2, 2, 'white', 'kitten');
const whitePart = getParts(state)[1];
expect(whitePart.value.position).toEqual([2, 2]);
expect(whitePart.position).toEqual([2, 2]);
applyBoops(state, 3, 3, 'kitten');
expect(whitePart.value.position).toEqual([1, 1]);
expect(whitePart.position).toEqual([1, 1]);
});
it('should not boop a cat when a kitten is placed', () => {
@@ -191,13 +191,11 @@ describe('Boop - helper functions', () => {
placePiece(state, 3, 3, 'black', 'kitten');
const whitePart = getParts(state)[0];
whitePart.produce(p => {
p.pieceType = 'cat';
});
whitePart.pieceType = 'cat';
applyBoops(state, 3, 3, 'kitten');
expect(whitePart.value.position).toEqual([3, 3]);
expect(whitePart.position).toEqual([3, 3]);
});
it('should remove piece that is booped off the board', () => {
@@ -210,8 +208,8 @@ describe('Boop - helper functions', () => {
applyBoops(state, 1, 1, 'kitten');
expect(getParts(state).length).toBe(1);
expect(getParts(state)[0].value.player).toBe('black');
expect(state.value.players.white.value.kitten.supply).toBe(8);
expect(getParts(state)[0].player).toBe('black');
expect(state.value.players.white.kitten.supply).toBe(8);
});
it('should not boop piece if target cell is occupied', () => {
@@ -224,10 +222,10 @@ describe('Boop - helper functions', () => {
applyBoops(state, 0, 1, 'kitten');
const whitePart = getParts(state).find(p => p.value.player === 'white');
const whitePart = getParts(state).find(p => p.player === 'white');
expect(whitePart).toBeDefined();
if (whitePart) {
expect(whitePart.value.position).toEqual([1, 1]);
expect(whitePart.position).toEqual([1, 1]);
}
});
@@ -241,8 +239,8 @@ describe('Boop - helper functions', () => {
applyBoops(state, 3, 3, 'kitten');
expect(getParts(state)[1].value.position).toEqual([1, 1]);
expect(getParts(state)[2].value.position).toEqual([1, 3]);
expect(getParts(state)[1].position).toEqual([1, 1]);
expect(getParts(state)[2].position).toEqual([1, 3]);
});
it('should not boop the placed piece itself', () => {
@@ -253,7 +251,7 @@ describe('Boop - helper functions', () => {
applyBoops(state, 3, 3, 'kitten');
expect(getParts(state)[0].value.position).toEqual([3, 3]);
expect(getParts(state)[0].position).toEqual([3, 3]);
});
});
@@ -267,7 +265,7 @@ describe('Boop - helper functions', () => {
removePieceFromBoard(state, part);
const board = getBoardRegion(state);
expect(board.value.children.length).toBe(0);
expect(board.childIds.length).toBe(0);
});
});
@@ -343,9 +341,7 @@ describe('Boop - helper functions', () => {
placePiece(state, 0, 1, 'white', 'kitten');
placePiece(state, 0, 2, 'white', 'kitten');
getParts(state)[1].produce(p => {
p.pieceType = 'cat';
});
getParts(state)[1].pieceType = 'cat';
const lines = checkGraduation(state, 'white');
expect(lines.length).toBe(0);
@@ -367,7 +363,7 @@ describe('Boop - helper functions', () => {
processGraduation(state, 'white', lines);
expect(getParts(state).length).toBe(0);
expect(state.value.players.white.value.cat.supply).toBe(3);
expect(state.value.players.white.cat.supply).toBe(3);
});
it('should only graduate pieces on the winning lines', () => {
@@ -383,8 +379,8 @@ describe('Boop - helper functions', () => {
processGraduation(state, 'white', lines);
expect(getParts(state).length).toBe(1);
expect(getParts(state)[0].value.position).toEqual([3, 3]);
expect(state.value.players.white.value.cat.supply).toBe(3);
expect(getParts(state)[0].position).toEqual([3, 3]);
expect(state.value.players.white.cat.supply).toBe(3);
});
});
@@ -487,7 +483,7 @@ describe('Boop - game flow', () => {
expect(result.success).toBe(true);
if (result.success) expect(result.result.winner).toBeNull();
expect(getParts(ctx.state).length).toBe(1);
expect(getParts(ctx.state)[0].value.position).toEqual([2, 2]);
expect(getParts(ctx.state)[0].position).toEqual([2, 2]);
expect(getParts(ctx.state)[0].id).toBe('white-kitten-1');
});
@@ -537,7 +533,9 @@ describe('Boop - game flow', () => {
const { ctx } = createTestContext();
const state = getState(ctx);
state.value.players.white.value.kitten.supply = 0;
state.produce(s => {
s.players.white.kitten.supply = 0;
});
const promptPromise = waitForPrompt(ctx);
const runPromise = ctx.commands.run<{winner: WinnerType}>('turn white');
@@ -575,10 +573,10 @@ describe('Boop - game flow', () => {
expect(result.success).toBe(true);
expect(getParts(state).length).toBe(2);
const whitePart = getParts(state).find(p => p.value.player === 'white');
const whitePart = getParts(state).find(p => p.player === 'white');
expect(whitePart).toBeDefined();
if (whitePart) {
expect(whitePart.value.position).not.toEqual([3, 3]);
expect(whitePart.position).not.toEqual([3, 3]);
}
});
@@ -596,14 +594,16 @@ describe('Boop - game flow', () => {
processGraduation(state, 'white', lines);
expect(getParts(state).length).toBe(0);
expect(state.value.players.white.value.cat.supply).toBe(3);
expect(state.value.players.white.cat.supply).toBe(3);
});
it('should accept placing a cat via play command', async () => {
const { ctx } = createTestContext();
const state = getState(ctx);
state.value.players.white.value.cat.supply = 3;
state.produce(s => {
s.players.white.cat.supply = 3;
});
const promptPromise = waitForPrompt(ctx);
const runPromise = ctx.commands.run<{winner: WinnerType}>('turn white');
@@ -616,15 +616,17 @@ describe('Boop - game flow', () => {
expect(result.success).toBe(true);
expect(getParts(state).length).toBe(1);
expect(getParts(state)[0].id).toBe('white-cat-1');
expect(getParts(state)[0].value.pieceType).toBe('cat');
expect(state.value.players.white.value.cat.supply).toBe(2);
expect(getParts(state)[0].pieceType).toBe('cat');
expect(state.value.players.white.cat.supply).toBe(2);
});
it('should reject placing a cat when supply is empty', async () => {
const { ctx } = createTestContext();
const state = getState(ctx);
state.value.players.white.value.cat.supply = 0;
state.produce(s => {
s.players.white.cat.supply = 0;
});
const promptPromise = waitForPrompt(ctx);
const runPromise = ctx.commands.run<{winner: WinnerType}>('turn white');
+4 -4
View File
@@ -164,8 +164,8 @@ describe('TicTacToe - helper functions', () => {
placePiece(state, 1, 1, 'X');
expect(state.value.parts.length).toBe(1);
expect(state.value.parts[0].value.position).toEqual([1, 1]);
expect(state.value.parts[0].value.player).toBe('X');
expect(state.value.parts[0].position).toEqual([1, 1]);
expect(state.value.parts[0].player).toBe('X');
});
it('should add piece to board region children', () => {
@@ -174,7 +174,7 @@ describe('TicTacToe - helper functions', () => {
placePiece(state, 0, 0, 'O');
const board = state.value.board;
expect(board.value.children.length).toBe(1);
expect(board.childIds.length).toBe(1);
});
it('should generate unique IDs for pieces', () => {
@@ -230,7 +230,7 @@ describe('TicTacToe - game flow', () => {
expect(result.success).toBe(true);
if (result.success) expect(result.result.winner).toBeNull();
expect(ctx.state.value.parts.length).toBe(1);
expect(ctx.state.value.parts[0].value.position).toEqual([1, 1]);
expect(ctx.state.value.parts[0].position).toEqual([1, 1]);
});
it('should reject move for wrong player and re-prompt', async () => {