refactor: async boop
This commit is contained in:
parent
775bb00bed
commit
80f0796f3c
|
|
@ -16,7 +16,7 @@ export type PlayerType = 'white' | 'black';
|
||||||
export type PieceType = 'kitten' | 'cat';
|
export type PieceType = 'kitten' | 'cat';
|
||||||
export type WinnerType = PlayerType | 'draw' | null;
|
export type WinnerType = PlayerType | 'draw' | null;
|
||||||
|
|
||||||
type BoopPart = Part<{ player: PlayerType; pieceType: PieceType }>;
|
export type BoopPart = Part<{ player: PlayerType; pieceType: PieceType }>;
|
||||||
|
|
||||||
type PieceSupply = { supply: number; placed: number };
|
type PieceSupply = { supply: number; placed: number };
|
||||||
|
|
||||||
|
|
@ -120,7 +120,7 @@ registration.add('turn <player>', async function(cmd) {
|
||||||
const pieceType = type === 'cat' ? 'cat' : 'kitten';
|
const pieceType = type === 'cat' ? 'cat' : 'kitten';
|
||||||
|
|
||||||
placePiece(this.context, row, col, turnPlayer, pieceType);
|
placePiece(this.context, row, col, turnPlayer, pieceType);
|
||||||
applyBoops(this.context, row, col, pieceType);
|
await applyBoops(this.context, row, col, pieceType);
|
||||||
|
|
||||||
const graduatedLines = checkGraduation(this.context, turnPlayer);
|
const graduatedLines = checkGraduation(this.context, turnPlayer);
|
||||||
if (graduatedLines.length > 0) {
|
if (graduatedLines.length > 0) {
|
||||||
|
|
@ -192,8 +192,7 @@ export function placePiece(host: MutableSignal<BoopState>, row: number, col: num
|
||||||
decrementSupply(playerData, pieceType);
|
decrementSupply(playerData, pieceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyBoops(host: MutableSignal<BoopState>, placedRow: number, placedCol: number, placedType: PieceType) {
|
export async function applyBoops(host: MutableSignal<BoopState>, placedRow: number, placedCol: number, placedType: PieceType) {
|
||||||
const board = getBoardRegion(host);
|
|
||||||
const pieces = host.value.pieces;
|
const pieces = host.value.pieces;
|
||||||
const piecesArray = Object.values(pieces);
|
const piecesArray = Object.values(pieces);
|
||||||
|
|
||||||
|
|
@ -216,6 +215,10 @@ export function applyBoops(host: MutableSignal<BoopState>, placedRow: number, pl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await host.produceAsync(state => {
|
||||||
|
const board = state.board;
|
||||||
|
const currentPieces = state.pieces;
|
||||||
|
|
||||||
for (const { part, dr, dc } of piecesToBoop) {
|
for (const { part, dr, dc } of piecesToBoop) {
|
||||||
const [r, c] = part.position;
|
const [r, c] = part.position;
|
||||||
const newRow = r + dr;
|
const newRow = r + dr;
|
||||||
|
|
@ -224,31 +227,37 @@ export function applyBoops(host: MutableSignal<BoopState>, placedRow: number, pl
|
||||||
if (newRow < 0 || newRow >= BOARD_SIZE || newCol < 0 || newCol >= BOARD_SIZE) {
|
if (newRow < 0 || newRow >= BOARD_SIZE || newCol < 0 || newCol >= BOARD_SIZE) {
|
||||||
const pt = part.pieceType;
|
const pt = part.pieceType;
|
||||||
const pl = part.player;
|
const pl = part.player;
|
||||||
const playerData = getPlayer(host, pl);
|
const playerData = state.players[pl];
|
||||||
removePieceFromBoard(host, part);
|
// Remove piece from board
|
||||||
incrementSupply(playerData, pt);
|
board.childIds = board.childIds.filter(id => id !== part.id);
|
||||||
|
delete board.partMap[part.position.join(',')];
|
||||||
|
delete currentPieces[part.id];
|
||||||
|
playerData[pt].placed--;
|
||||||
|
playerData[pt].supply++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCellOccupied(host, newRow, newCol)) continue;
|
// Check if target cell is occupied
|
||||||
|
const targetPosKey = `${newRow},${newCol}`;
|
||||||
|
if (board.partMap[targetPosKey]) continue;
|
||||||
|
|
||||||
|
// Move piece to new position
|
||||||
|
delete board.partMap[part.position.join(',')];
|
||||||
part.position = [newRow, newCol];
|
part.position = [newRow, newCol];
|
||||||
board.partMap = Object.fromEntries(
|
board.partMap[targetPosKey] = part.id;
|
||||||
board.childIds.map(id => {
|
|
||||||
const p = pieces[id];
|
|
||||||
return [p.position.join(','), id];
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removePieceFromBoard(host: MutableSignal<BoopState>, part: BoopPart) {
|
export function removePieceFromBoard(host: MutableSignal<BoopState>, part: BoopPart) {
|
||||||
const board = getBoardRegion(host);
|
host.produce(state => {
|
||||||
const playerData = getPlayer(host, part.player);
|
const board = state.board;
|
||||||
|
const playerData = state.players[part.player];
|
||||||
board.childIds = board.childIds.filter(id => id !== part.id);
|
board.childIds = board.childIds.filter(id => id !== part.id);
|
||||||
delete board.partMap[part.position.join(',')];
|
delete board.partMap[part.position.join(',')];
|
||||||
delete host.value.pieces[part.id];
|
delete state.pieces[part.id];
|
||||||
playerData[part.pieceType].placed--;
|
playerData[part.pieceType].placed--;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const DIRECTIONS: [number, number][] = [
|
const DIRECTIONS: [number, number][] = [
|
||||||
|
|
@ -363,3 +372,18 @@ export function checkWinner(host: MutableSignal<BoopState>): WinnerType {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 命令构建器
|
||||||
|
export const commands = {
|
||||||
|
play: (player: PlayerType, row: number, col: number, type?: PieceType) =>
|
||||||
|
`play ${player} ${row} ${col}${type ? ` ${type}` : ''}`,
|
||||||
|
turn: (player: PlayerType) => `turn ${player}`,
|
||||||
|
graduate: (row: number, col: number) => `graduate ${row} ${col}`,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 导出游戏模块
|
||||||
|
export const gameModule = {
|
||||||
|
createInitialState,
|
||||||
|
registry,
|
||||||
|
commands,
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ describe('Boop - helper functions', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('applyBoops', () => {
|
describe('applyBoops', () => {
|
||||||
it('should boop adjacent kitten away from placed kitten', () => {
|
it('should boop adjacent kitten away from placed kitten', async () => {
|
||||||
const { ctx } = createTestContext();
|
const { ctx } = createTestContext();
|
||||||
const state = getState(ctx);
|
const state = getState(ctx);
|
||||||
|
|
||||||
|
|
@ -180,12 +180,12 @@ describe('Boop - helper functions', () => {
|
||||||
const whitePart = getParts(state)[1];
|
const whitePart = getParts(state)[1];
|
||||||
expect(whitePart.position).toEqual([2, 2]);
|
expect(whitePart.position).toEqual([2, 2]);
|
||||||
|
|
||||||
applyBoops(state, 3, 3, 'kitten');
|
await applyBoops(state, 3, 3, 'kitten');
|
||||||
|
|
||||||
expect(whitePart.position).toEqual([1, 1]);
|
expect(whitePart.position).toEqual([1, 1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not boop a cat when a kitten is placed', () => {
|
it('should not boop a cat when a kitten is placed', async () => {
|
||||||
const { ctx } = createTestContext();
|
const { ctx } = createTestContext();
|
||||||
const state = getState(ctx);
|
const state = getState(ctx);
|
||||||
|
|
||||||
|
|
@ -193,26 +193,26 @@ describe('Boop - helper functions', () => {
|
||||||
const whitePart = getParts(state)[0];
|
const whitePart = getParts(state)[0];
|
||||||
whitePart.pieceType = 'cat';
|
whitePart.pieceType = 'cat';
|
||||||
|
|
||||||
applyBoops(state, 3, 3, 'kitten');
|
await applyBoops(state, 3, 3, 'kitten');
|
||||||
|
|
||||||
expect(whitePart.position).toEqual([3, 3]);
|
expect(whitePart.position).toEqual([3, 3]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove piece that is booped off the board', () => {
|
it('should remove piece that is booped off the board', async () => {
|
||||||
const { ctx } = createTestContext();
|
const { ctx } = createTestContext();
|
||||||
const state = getState(ctx);
|
const state = getState(ctx);
|
||||||
|
|
||||||
placePiece(state, 0, 0, 'white', 'kitten');
|
placePiece(state, 0, 0, 'white', 'kitten');
|
||||||
placePiece(state, 1, 1, 'black', 'kitten');
|
placePiece(state, 1, 1, 'black', 'kitten');
|
||||||
|
|
||||||
applyBoops(state, 1, 1, 'kitten');
|
await applyBoops(state, 1, 1, 'kitten');
|
||||||
|
|
||||||
expect(getParts(state).length).toBe(1);
|
expect(getParts(state).length).toBe(1);
|
||||||
expect(getParts(state)[0].player).toBe('black');
|
expect(getParts(state)[0].player).toBe('black');
|
||||||
expect(state.value.players.white.kitten.supply).toBe(8);
|
expect(state.value.players.white.kitten.supply).toBe(8);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not boop piece if target cell is occupied', () => {
|
it('should not boop piece if target cell is occupied', async () => {
|
||||||
const { ctx } = createTestContext();
|
const { ctx } = createTestContext();
|
||||||
const state = getState(ctx);
|
const state = getState(ctx);
|
||||||
|
|
||||||
|
|
@ -220,7 +220,7 @@ describe('Boop - helper functions', () => {
|
||||||
placePiece(state, 2, 1, 'black', 'kitten');
|
placePiece(state, 2, 1, 'black', 'kitten');
|
||||||
placePiece(state, 0, 1, 'black', 'kitten');
|
placePiece(state, 0, 1, 'black', 'kitten');
|
||||||
|
|
||||||
applyBoops(state, 0, 1, 'kitten');
|
await applyBoops(state, 0, 1, 'kitten');
|
||||||
|
|
||||||
const whitePart = getParts(state).find(p => p.player === 'white');
|
const whitePart = getParts(state).find(p => p.player === 'white');
|
||||||
expect(whitePart).toBeDefined();
|
expect(whitePart).toBeDefined();
|
||||||
|
|
@ -229,7 +229,7 @@ describe('Boop - helper functions', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should boop multiple adjacent pieces', () => {
|
it('should boop multiple adjacent pieces', async () => {
|
||||||
const { ctx } = createTestContext();
|
const { ctx } = createTestContext();
|
||||||
const state = getState(ctx);
|
const state = getState(ctx);
|
||||||
|
|
||||||
|
|
@ -237,19 +237,19 @@ describe('Boop - helper functions', () => {
|
||||||
placePiece(state, 2, 2, 'black', 'kitten');
|
placePiece(state, 2, 2, 'black', 'kitten');
|
||||||
placePiece(state, 2, 3, 'black', 'kitten');
|
placePiece(state, 2, 3, 'black', 'kitten');
|
||||||
|
|
||||||
applyBoops(state, 3, 3, 'kitten');
|
await applyBoops(state, 3, 3, 'kitten');
|
||||||
|
|
||||||
expect(getParts(state)[1].position).toEqual([1, 1]);
|
expect(getParts(state)[1].position).toEqual([1, 1]);
|
||||||
expect(getParts(state)[2].position).toEqual([1, 3]);
|
expect(getParts(state)[2].position).toEqual([1, 3]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not boop the placed piece itself', () => {
|
it('should not boop the placed piece itself', async () => {
|
||||||
const { ctx } = createTestContext();
|
const { ctx } = createTestContext();
|
||||||
const state = getState(ctx);
|
const state = getState(ctx);
|
||||||
|
|
||||||
placePiece(state, 3, 3, 'white', 'kitten');
|
placePiece(state, 3, 3, 'white', 'kitten');
|
||||||
|
|
||||||
applyBoops(state, 3, 3, 'kitten');
|
await applyBoops(state, 3, 3, 'kitten');
|
||||||
|
|
||||||
expect(getParts(state)[0].position).toEqual([3, 3]);
|
expect(getParts(state)[0].position).toEqual([3, 3]);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue