Compare commits
2 Commits
b929a126a1
...
912ef6e283
| Author | SHA1 | Date |
|---|---|---|
|
|
912ef6e283 | |
|
|
dd73deabb0 |
|
|
@ -93,7 +93,7 @@ export class GameHost<TState extends Record<string, unknown>> {
|
||||||
this._state.clearInterruptions();
|
this._state.clearInterruptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
async setup(setupCommand: string): Promise<void> {
|
setup(setupCommand: string): Promise<unknown> {
|
||||||
if (this._isDisposed) {
|
if (this._isDisposed) {
|
||||||
throw new Error('GameHost is disposed');
|
throw new Error('GameHost is disposed');
|
||||||
}
|
}
|
||||||
|
|
@ -103,14 +103,12 @@ export class GameHost<TState extends Record<string, unknown>> {
|
||||||
const initialState = this._createInitialState();
|
const initialState = this._createInitialState();
|
||||||
this._state.value = initialState as any;
|
this._state.value = initialState as any;
|
||||||
|
|
||||||
// Start the setup command but don't wait for it to complete
|
const promise = this._commands.run(setupCommand);
|
||||||
// The command will run in the background and prompt for input
|
|
||||||
this._commands.run(setupCommand).catch(() => {
|
|
||||||
// Command may be cancelled or fail, which is expected
|
|
||||||
});
|
|
||||||
|
|
||||||
this._status.value = 'running';
|
this._status.value = 'running';
|
||||||
this._emitEvent('setup');
|
this._emitEvent('setup');
|
||||||
|
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose(): void {
|
dispose(): void {
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ const boopCommand = registry.register('boop <row:number> <col:number> <type>', b
|
||||||
/**
|
/**
|
||||||
* 检查是否有玩家获胜(三个猫连线)
|
* 检查是否有玩家获胜(三个猫连线)
|
||||||
*/
|
*/
|
||||||
async function checkWin(game: BoopGame) {
|
async function checkWin(game: BoopGame): Promise<WinnerType | null> {
|
||||||
for(const line of getLineCandidates()){
|
for(const line of getLineCandidates()){
|
||||||
let whites = 0;
|
let whites = 0;
|
||||||
let blacks = 0;
|
let blacks = 0;
|
||||||
|
|
@ -151,10 +151,9 @@ async function setup(game: BoopGame) {
|
||||||
while (true) {
|
while (true) {
|
||||||
const currentPlayer = game.value.currentPlayer;
|
const currentPlayer = game.value.currentPlayer;
|
||||||
const turnOutput = await turnCommand(game, currentPlayer);
|
const turnOutput = await turnCommand(game, currentPlayer);
|
||||||
if (!turnOutput.success) throw new Error(turnOutput.error);
|
|
||||||
|
|
||||||
await game.produceAsync(state => {
|
await game.produceAsync(state => {
|
||||||
state.winner = turnOutput.result.winner;
|
state.winner = turnOutput.winner;
|
||||||
if (!state.winner) {
|
if (!state.winner) {
|
||||||
state.currentPlayer = state.currentPlayer === 'white' ? 'black' : 'white';
|
state.currentPlayer = state.currentPlayer === 'white' ? 'black' : 'white';
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +232,7 @@ async function turn(game: BoopGame, turnPlayer: PlayerType) {
|
||||||
await placeCommand(game, row, col, turnPlayer, pieceType);
|
await placeCommand(game, row, col, turnPlayer, pieceType);
|
||||||
await boopCommand(game, row, col, pieceType);
|
await boopCommand(game, row, col, pieceType);
|
||||||
const winner = await checkWinCommand(game);
|
const winner = await checkWinCommand(game);
|
||||||
if(winner.success) return { winner: winner.result as WinnerType };
|
if(winner) return { winner: winner };
|
||||||
|
|
||||||
await checkGraduatesCommand(game);
|
await checkGraduatesCommand(game);
|
||||||
await checkFullBoard(game, turnPlayer);
|
await checkFullBoard(game, turnPlayer);
|
||||||
|
|
|
||||||
|
|
@ -42,10 +42,9 @@ async function setup(game: TicTacToeGame) {
|
||||||
const currentPlayer = game.value.currentPlayer;
|
const currentPlayer = game.value.currentPlayer;
|
||||||
const turnNumber = game.value.turn + 1;
|
const turnNumber = game.value.turn + 1;
|
||||||
const turnOutput = await turnCommand(game, currentPlayer, turnNumber);
|
const turnOutput = await turnCommand(game, currentPlayer, turnNumber);
|
||||||
if (!turnOutput.success) throw new Error(turnOutput.error);
|
|
||||||
|
|
||||||
game.produce(state => {
|
game.produce(state => {
|
||||||
state.winner = turnOutput.result.winner;
|
state.winner = turnOutput.winner;
|
||||||
if (!state.winner) {
|
if (!state.winner) {
|
||||||
state.currentPlayer = state.currentPlayer === 'X' ? 'O' : 'X';
|
state.currentPlayer = state.currentPlayer === 'X' ? 'O' : 'X';
|
||||||
state.turn = turnNumber;
|
state.turn = turnNumber;
|
||||||
|
|
|
||||||
|
|
@ -30,13 +30,15 @@ export class CommandRegistry<TContext> extends Map<string, CommandRunner<TContex
|
||||||
|
|
||||||
type TParams = TFunc extends (ctx: TContext, ...args: infer X) => Promise<unknown> ? X : null;
|
type TParams = TFunc extends (ctx: TContext, ...args: infer X) => Promise<unknown> ? X : null;
|
||||||
type TResult = TFunc extends (ctx: TContext, ...args: any[]) => Promise<infer X> ? X : null;
|
type TResult = TFunc extends (ctx: TContext, ...args: any[]) => Promise<infer X> ? X : null;
|
||||||
return function(ctx: TContext & CanRunParsed, ...args: TParams){
|
return async function(ctx: TContext & CanRunParsed, ...args: TParams){
|
||||||
return ctx.runParsed({
|
const result: CommandResult<TResult> = await ctx.runParsed({
|
||||||
options: {},
|
options: {},
|
||||||
params: args,
|
params: args,
|
||||||
flags: {},
|
flags: {},
|
||||||
name: parsedSchema.name,
|
name: parsedSchema.name,
|
||||||
}) as Promise<CommandResult<TResult>>;
|
});
|
||||||
|
if(result.success) return result.result;
|
||||||
|
throw new Error(result.error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ describe('GameHost', () => {
|
||||||
const newPromptPromise = waitForPromptEvent(host);
|
const newPromptPromise = waitForPromptEvent(host);
|
||||||
|
|
||||||
// Reset - should reset state and start new game
|
// Reset - should reset state and start new game
|
||||||
await host.setup('setup');
|
host.setup('setup');
|
||||||
|
|
||||||
// State should be back to initial
|
// State should be back to initial
|
||||||
expect(host.context._state.value.currentPlayer).toBe('X');
|
expect(host.context._state.value.currentPlayer).toBe('X');
|
||||||
|
|
@ -173,7 +173,7 @@ describe('GameHost', () => {
|
||||||
await promptPromise;
|
await promptPromise;
|
||||||
|
|
||||||
// Setup should cancel the active prompt and reset state
|
// Setup should cancel the active prompt and reset state
|
||||||
await host.setup('setup');
|
host.setup('setup');
|
||||||
|
|
||||||
// The original runPromise should be rejected due to cancellation
|
// The original runPromise should be rejected due to cancellation
|
||||||
try {
|
try {
|
||||||
|
|
@ -188,11 +188,11 @@ describe('GameHost', () => {
|
||||||
expect(host.context._state.value.turn).toBe(0);
|
expect(host.context._state.value.turn).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw error when disposed', async () => {
|
it('should throw error when disposed', () => {
|
||||||
const { host } = createTestHost();
|
const { host } = createTestHost();
|
||||||
host.dispose();
|
host.dispose();
|
||||||
|
|
||||||
await expect(host.setup('setup')).rejects.toThrow('GameHost is disposed');
|
expect(() => host.setup('setup')).toThrow('GameHost is disposed');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -245,7 +245,7 @@ describe('GameHost', () => {
|
||||||
const promptPromise = waitForPromptEvent(host);
|
const promptPromise = waitForPromptEvent(host);
|
||||||
|
|
||||||
// Initial setup via reset
|
// Initial setup via reset
|
||||||
await host.setup('setup');
|
host.setup('setup');
|
||||||
expect(setupCount).toBe(1);
|
expect(setupCount).toBe(1);
|
||||||
|
|
||||||
// State should be running
|
// State should be running
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue