refactor: update usage pattern for tic tac toe and boop
This commit is contained in:
parent
5f812a3478
commit
15122defcc
|
|
@ -17,6 +17,7 @@ type PlayerSupply = {
|
|||
cat: PieceSupply;
|
||||
};
|
||||
|
||||
// TODO refactor this into an Entity
|
||||
function createPlayerSupply(): PlayerSupply {
|
||||
return {
|
||||
kitten: { supply: MAX_PIECES_PER_PLAYER, placed: 0 },
|
||||
|
|
@ -67,21 +68,32 @@ registration.add('setup', async function() {
|
|||
|
||||
registration.add('turn <player>', async function(cmd) {
|
||||
const [turnPlayer] = cmd.params as [PlayerType];
|
||||
const maxRetries = 50;
|
||||
let retries = 0;
|
||||
|
||||
while (retries < maxRetries) {
|
||||
retries++;
|
||||
const playCmd = await this.prompt('play <player> <row:number> <col:number> [type:string]');
|
||||
const [player, row, col, type] = playCmd.params as [PlayerType, number, number, PieceType?];
|
||||
const playCmd = await this.prompt(
|
||||
'play <player> <row:number> <col:number> [type:string]',
|
||||
(command) => {
|
||||
const [player, row, col, type] = command.params as [PlayerType, number, number, PieceType?];
|
||||
const pieceType = type === 'cat' ? 'cat' : 'kitten';
|
||||
|
||||
if (player !== turnPlayer) continue;
|
||||
if (!isValidMove(row, col)) continue;
|
||||
if (isCellOccupied(this.context, row, col)) continue;
|
||||
if (player !== turnPlayer) {
|
||||
return `Invalid player: ${player}. Expected ${turnPlayer}.`;
|
||||
}
|
||||
if (!isValidMove(row, col)) {
|
||||
return `Invalid position: (${row}, ${col}). Must be between 0 and ${BOARD_SIZE - 1}.`;
|
||||
}
|
||||
if (isCellOccupied(this.context, row, col)) {
|
||||
return `Cell (${row}, ${col}) is already occupied.`;
|
||||
}
|
||||
|
||||
const supply = this.context.value.players[player][pieceType].supply;
|
||||
if (supply <= 0) continue;
|
||||
if (supply <= 0) {
|
||||
return `No ${pieceType}s left in ${player}'s supply.`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
const [player, row, col, type] = playCmd.params as [PlayerType, number, number, PieceType?];
|
||||
const pieceType = type === 'cat' ? 'cat' : 'kitten';
|
||||
|
||||
placePiece(this.context, row, col, turnPlayer, pieceType);
|
||||
applyBoops(this.context, row, col, pieceType);
|
||||
|
|
@ -95,9 +107,6 @@ registration.add('turn <player>', async function(cmd) {
|
|||
if (winner) return { winner };
|
||||
|
||||
return { winner: null };
|
||||
}
|
||||
|
||||
throw new Error('Too many invalid attempts');
|
||||
});
|
||||
|
||||
function isValidMove(row: number, col: number): boolean {
|
||||
|
|
|
|||
|
|
@ -61,18 +61,26 @@ registration.add('setup', async function() {
|
|||
|
||||
registration.add('turn <player> <turn:number>', async function(cmd) {
|
||||
const [turnPlayer, turnNumber] = cmd.params as [PlayerType, number];
|
||||
const maxRetries = MAX_TURNS * 2;
|
||||
let retries = 0;
|
||||
|
||||
while (retries < maxRetries) {
|
||||
retries++;
|
||||
const playCmd = await this.prompt('play <player> <row:number> <col:number>');
|
||||
const playCmd = await this.prompt(
|
||||
'play <player> <row:number> <col:number>',
|
||||
(command) => {
|
||||
const [player, row, col] = command.params as [PlayerType, number, number];
|
||||
|
||||
if (player !== turnPlayer) {
|
||||
return `Invalid player: ${player}. Expected ${turnPlayer}.`;
|
||||
}
|
||||
if (!isValidMove(row, col)) {
|
||||
return `Invalid position: (${row}, ${col}). Must be between 0 and ${BOARD_SIZE - 1}.`;
|
||||
}
|
||||
if (isCellOccupied(this.context, row, col)) {
|
||||
return `Cell (${row}, ${col}) is already occupied.`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
const [player, row, col] = playCmd.params as [PlayerType, number, number];
|
||||
|
||||
if (player !== turnPlayer) continue;
|
||||
if (!isValidMove(row, col)) continue;
|
||||
if (isCellOccupied(this.context, row, col)) continue;
|
||||
|
||||
placePiece(this.context, row, col, turnPlayer);
|
||||
|
||||
const winner = checkWinner(this.context);
|
||||
|
|
@ -80,9 +88,6 @@ registration.add('turn <player> <turn:number>', async function(cmd) {
|
|||
if (turnNumber >= MAX_TURNS) return { winner: 'draw' as WinnerType };
|
||||
|
||||
return { winner: null };
|
||||
}
|
||||
|
||||
throw new Error('Too many invalid attempts');
|
||||
});
|
||||
|
||||
function isValidMove(row: number, col: number): boolean {
|
||||
|
|
|
|||
|
|
@ -498,14 +498,12 @@ describe('Boop - game flow', () => {
|
|||
const runPromise = ctx.commands.run<{winner: WinnerType}>('turn white');
|
||||
|
||||
const promptEvent1 = await promptPromise;
|
||||
// 没有验证器,tryCommit 返回 null,但游戏逻辑会 continue 并重新 prompt
|
||||
// 验证器会拒绝错误的玩家
|
||||
const error1 = promptEvent1.tryCommit({ name: 'play', params: ['black', 2, 2], options: {}, flags: {} });
|
||||
expect(error1).toBeNull();
|
||||
expect(error1).toContain('Invalid player');
|
||||
|
||||
const promptEvent2 = await waitForPrompt(ctx);
|
||||
expect(promptEvent2).not.toBeNull();
|
||||
|
||||
const error2 = promptEvent2.tryCommit({ name: 'play', params: ['white', 2, 2], options: {}, flags: {} });
|
||||
// 验证失败后,再次尝试有效输入
|
||||
const error2 = promptEvent1.tryCommit({ name: 'play', params: ['white', 2, 2], options: {}, flags: {} });
|
||||
expect(error2).toBeNull();
|
||||
|
||||
const result = await runPromise;
|
||||
|
|
@ -524,12 +522,10 @@ describe('Boop - game flow', () => {
|
|||
|
||||
const promptEvent1 = await promptPromise;
|
||||
const error1 = promptEvent1.tryCommit({ name: 'play', params: ['white', 2, 2], options: {}, flags: {} });
|
||||
expect(error1).toBeNull();
|
||||
expect(error1).toContain('occupied');
|
||||
|
||||
const promptEvent2 = await waitForPrompt(ctx);
|
||||
expect(promptEvent2).not.toBeNull();
|
||||
|
||||
const error2 = promptEvent2.tryCommit({ name: 'play', params: ['white', 0, 0], options: {}, flags: {} });
|
||||
// 验证失败后,再次尝试有效输入
|
||||
const error2 = promptEvent1.tryCommit({ name: 'play', params: ['white', 0, 0], options: {}, flags: {} });
|
||||
expect(error2).toBeNull();
|
||||
|
||||
const result = await runPromise;
|
||||
|
|
@ -550,12 +546,10 @@ describe('Boop - game flow', () => {
|
|||
|
||||
const promptEvent1 = await promptPromise;
|
||||
const error1 = promptEvent1.tryCommit({ name: 'play', params: ['white', 0, 0], options: {}, flags: {} });
|
||||
expect(error1).toBeNull();
|
||||
expect(error1).toContain('No kittens');
|
||||
|
||||
const promptEvent2 = await waitForPrompt(ctx);
|
||||
expect(promptEvent2).not.toBeNull();
|
||||
|
||||
promptEvent2.cancel('test end');
|
||||
// 验证失败后,取消
|
||||
promptEvent1.cancel('test end');
|
||||
|
||||
const result = await runPromise;
|
||||
expect(result.success).toBe(false);
|
||||
|
|
@ -643,12 +637,10 @@ describe('Boop - game flow', () => {
|
|||
|
||||
const promptEvent1 = await promptPromise;
|
||||
const error1 = promptEvent1.tryCommit({ name: 'play', params: ['white', 0, 0, 'cat'], options: {}, flags: {} });
|
||||
expect(error1).toBeNull();
|
||||
expect(error1).toContain('No cats');
|
||||
|
||||
const promptEvent2 = await waitForPrompt(ctx);
|
||||
expect(promptEvent2).not.toBeNull();
|
||||
|
||||
promptEvent2.cancel('test end');
|
||||
// 验证失败后,取消
|
||||
promptEvent1.cancel('test end');
|
||||
|
||||
const result = await runPromise;
|
||||
expect(result.success).toBe(false);
|
||||
|
|
|
|||
|
|
@ -240,14 +240,12 @@ describe('TicTacToe - game flow', () => {
|
|||
const runPromise = ctx.commands.run<{winner: WinnerType}>('turn X 1');
|
||||
|
||||
const promptEvent1 = await promptPromise;
|
||||
// 没有验证器,tryCommit 返回 null,但游戏逻辑会 continue 并重新 prompt
|
||||
// 验证器会拒绝错误的玩家
|
||||
const error1 = promptEvent1.tryCommit({ name: 'play', params: ['O', 1, 1], options: {}, flags: {} });
|
||||
expect(error1).toBeNull();
|
||||
expect(error1).toContain('Invalid player');
|
||||
|
||||
const promptEvent2 = await waitForPrompt(ctx);
|
||||
expect(promptEvent2).not.toBeNull();
|
||||
|
||||
const error2 = promptEvent2.tryCommit({ name: 'play', params: ['X', 1, 1], options: {}, flags: {} });
|
||||
// 验证失败后,再次尝试有效输入
|
||||
const error2 = promptEvent1.tryCommit({ name: 'play', params: ['X', 1, 1], options: {}, flags: {} });
|
||||
expect(error2).toBeNull();
|
||||
|
||||
const result = await runPromise;
|
||||
|
|
@ -266,12 +264,10 @@ describe('TicTacToe - game flow', () => {
|
|||
|
||||
const promptEvent1 = await promptPromise;
|
||||
const error1 = promptEvent1.tryCommit({ name: 'play', params: ['X', 1, 1], options: {}, flags: {} });
|
||||
expect(error1).toBeNull();
|
||||
expect(error1).toContain('occupied');
|
||||
|
||||
const promptEvent2 = await waitForPrompt(ctx);
|
||||
expect(promptEvent2).not.toBeNull();
|
||||
|
||||
const error2 = promptEvent2.tryCommit({ name: 'play', params: ['X', 0, 0], options: {}, flags: {} });
|
||||
// 验证失败后,再次尝试有效输入
|
||||
const error2 = promptEvent1.tryCommit({ name: 'play', params: ['X', 0, 0], options: {}, flags: {} });
|
||||
expect(error2).toBeNull();
|
||||
|
||||
const result = await runPromise;
|
||||
|
|
|
|||
Loading…
Reference in New Issue