refactor: update api
This commit is contained in:
parent
129c58fb08
commit
e673f60657
|
|
@ -4,14 +4,13 @@ import type {
|
|||
CommandRegistry,
|
||||
PromptEvent,
|
||||
CommandRunnerContextExport,
|
||||
CommandResult
|
||||
} from '@/utils/command';
|
||||
import type { MutableSignal } from '@/utils/mutable-signal';
|
||||
import {createGameContext, IGameContext} from './game';
|
||||
import {createGameCommandRegistry, createGameContext, IGameContext} from './game';
|
||||
|
||||
export type GameHostStatus = 'created' | 'running' | 'disposed';
|
||||
|
||||
export class GameHost<TState extends Record<string, unknown>> {
|
||||
export class GameHost<TState extends Record<string, unknown>, TResult=unknown> {
|
||||
readonly context: IGameContext<TState>;
|
||||
readonly status: ReadonlySignal<GameHostStatus>;
|
||||
readonly activePromptSchema: ReadonlySignal<CommandSchema | null>;
|
||||
|
|
@ -19,6 +18,7 @@ export class GameHost<TState extends Record<string, unknown>> {
|
|||
|
||||
private _state: MutableSignal<TState>;
|
||||
private _commands: CommandRunnerContextExport<IGameContext<TState>>;
|
||||
private _start: (ctx: IGameContext<TState>) => Promise<TResult>;
|
||||
private _status: Signal<GameHostStatus>;
|
||||
private _activePromptSchema: Signal<CommandSchema | null>;
|
||||
private _activePromptPlayer: Signal<string | null>;
|
||||
|
|
@ -29,12 +29,14 @@ export class GameHost<TState extends Record<string, unknown>> {
|
|||
constructor(
|
||||
registry: CommandRegistry<IGameContext<TState>>,
|
||||
createInitialState: () => TState,
|
||||
start: (ctx: IGameContext<TState>) => Promise<TResult>
|
||||
) {
|
||||
this._createInitialState = createInitialState;
|
||||
this._eventListeners = new Map();
|
||||
|
||||
const initialState = createInitialState();
|
||||
this.context = createGameContext(registry, initialState);
|
||||
this._start = start;
|
||||
|
||||
this._status = new Signal<GameHostStatus>('created');
|
||||
this.status = this._status;
|
||||
|
|
@ -94,7 +96,7 @@ export class GameHost<TState extends Record<string, unknown>> {
|
|||
this._state.clearInterruptions();
|
||||
}
|
||||
|
||||
start(startCommand: string): Promise<CommandResult<unknown>> {
|
||||
start(): Promise<TResult> {
|
||||
if (this._isDisposed) {
|
||||
throw new Error('GameHost is disposed');
|
||||
}
|
||||
|
|
@ -104,7 +106,7 @@ export class GameHost<TState extends Record<string, unknown>> {
|
|||
const initialState = this._createInitialState();
|
||||
this._state.value = initialState as any;
|
||||
|
||||
const promise = this._commands.run(startCommand);
|
||||
const promise = this._start(this.context);
|
||||
|
||||
this._status.value = 'running';
|
||||
this._emitEvent('start');
|
||||
|
|
@ -147,16 +149,18 @@ export class GameHost<TState extends Record<string, unknown>> {
|
|||
}
|
||||
}
|
||||
|
||||
export type GameModule<TState extends Record<string, unknown>> = {
|
||||
registry: CommandRegistry<IGameContext<TState>>;
|
||||
export type GameModule<TState extends Record<string, unknown>, TResult=unknown> = {
|
||||
registry?: CommandRegistry<IGameContext<TState>>;
|
||||
createInitialState: () => TState;
|
||||
start: (ctx: IGameContext<TState>) => Promise<TResult>;
|
||||
}
|
||||
|
||||
export function createGameHost<TState extends Record<string, unknown>>(
|
||||
gameModule: GameModule<TState>
|
||||
): GameHost<TState> {
|
||||
return new GameHost(
|
||||
gameModule.registry,
|
||||
gameModule.registry || createGameCommandRegistry(),
|
||||
gameModule.createInitialState,
|
||||
gameModule.start
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ const checkGraduates = registry.register({
|
|||
run: handleCheckGraduates
|
||||
});
|
||||
|
||||
async function handleStart(game: BoopGame) {
|
||||
export async function start(game: BoopGame) {
|
||||
while (true) {
|
||||
const currentPlayer = game.value.currentPlayer;
|
||||
const turnOutput = await turn(game, currentPlayer);
|
||||
|
|
@ -175,10 +175,6 @@ async function handleStart(game: BoopGame) {
|
|||
|
||||
return game.value;
|
||||
}
|
||||
const start = registry.register({
|
||||
schema: 'start',
|
||||
run: handleStart
|
||||
});
|
||||
|
||||
async function handleCheckFullBoard(game: BoopGame, turnPlayer: PlayerType){
|
||||
// 检查8-piece规则: 如果玩家所有8个棋子都在棋盘上且没有获胜,强制升级一个小猫
|
||||
|
|
|
|||
|
|
@ -36,9 +36,7 @@ export type TicTacToeState = ReturnType<typeof createInitialState>;
|
|||
export type TicTacToeGame = IGameContext<TicTacToeState>;
|
||||
export const registry = createGameCommandRegistry<TicTacToeState>();
|
||||
|
||||
registry.register({
|
||||
schema: 'start',
|
||||
async run(game: TicTacToeGame) {
|
||||
export async function start(game: TicTacToeGame) {
|
||||
while (true) {
|
||||
const currentPlayer = game.value.currentPlayer;
|
||||
const turnNumber = game.value.turn + 1;
|
||||
|
|
@ -55,8 +53,7 @@ registry.register({
|
|||
}
|
||||
|
||||
return game.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const turn = registry.register({
|
||||
schema: 'turn <player> <turnNumber:number>',
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest';
|
|||
import {
|
||||
registry,
|
||||
createInitialState,
|
||||
start,
|
||||
TicTacToeState,
|
||||
WinnerType,
|
||||
PlayerType
|
||||
|
|
@ -12,7 +13,7 @@ import { MutableSignal } from '@/utils/mutable-signal';
|
|||
|
||||
function createTestHost() {
|
||||
const host = createGameHost(
|
||||
{ registry, createInitialState }
|
||||
{ registry, createInitialState, start }
|
||||
);
|
||||
return { host };
|
||||
}
|
||||
|
|
@ -59,7 +60,7 @@ describe('GameHost', () => {
|
|||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
expect(promptEvent.schema.name).toBe('play');
|
||||
|
|
@ -81,7 +82,7 @@ describe('GameHost', () => {
|
|||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
|
||||
|
|
@ -106,7 +107,7 @@ describe('GameHost', () => {
|
|||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
const schema = host.activePromptSchema.value;
|
||||
|
|
@ -131,7 +132,7 @@ describe('GameHost', () => {
|
|||
|
||||
// First setup - make one move
|
||||
let promptPromise = waitForPromptEvent(host);
|
||||
let runPromise = host.context._commands.run('start');
|
||||
let runPromise = host.start();
|
||||
let promptEvent = await promptPromise;
|
||||
|
||||
// Make a move
|
||||
|
|
@ -150,7 +151,7 @@ describe('GameHost', () => {
|
|||
const newPromptPromise = waitForPromptEvent(host);
|
||||
|
||||
// Reset - should reset state and start new game
|
||||
host.start('start');
|
||||
host.start();
|
||||
|
||||
// State should be back to initial
|
||||
expect(host.context._state.value.currentPlayer).toBe('X');
|
||||
|
|
@ -168,12 +169,12 @@ describe('GameHost', () => {
|
|||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
await promptPromise;
|
||||
|
||||
// Setup should cancel the active prompt and reset state
|
||||
host.start('start');
|
||||
host.start();
|
||||
|
||||
// The original runPromise should be rejected due to cancellation
|
||||
try {
|
||||
|
|
@ -192,7 +193,7 @@ describe('GameHost', () => {
|
|||
const { host } = createTestHost();
|
||||
host.dispose();
|
||||
|
||||
expect(() => host.start('start')).toThrow('GameHost is disposed');
|
||||
expect(() => host.start()).toThrow('GameHost is disposed');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -208,7 +209,7 @@ describe('GameHost', () => {
|
|||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
await promptPromise;
|
||||
|
||||
|
|
@ -245,7 +246,7 @@ describe('GameHost', () => {
|
|||
const promptPromise = waitForPromptEvent(host);
|
||||
|
||||
// Initial setup via reset
|
||||
host.start('start');
|
||||
host.start();
|
||||
expect(setupCount).toBe(1);
|
||||
|
||||
// State should be running
|
||||
|
|
@ -294,7 +295,7 @@ describe('GameHost', () => {
|
|||
|
||||
// Make a move
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
promptEvent.tryCommit({ name: 'play', params: ['X', 1, 1], options: {}, flags: {} });
|
||||
|
|
@ -320,7 +321,7 @@ describe('GameHost', () => {
|
|||
|
||||
// Start a command that triggers prompt
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
await promptPromise;
|
||||
|
||||
|
|
@ -369,7 +370,7 @@ describe('GameHost', () => {
|
|||
});
|
||||
|
||||
// Start setup command (runs game loop until completion)
|
||||
const setupPromise = host.context._commands.run('start');
|
||||
const setupPromise = host.start();
|
||||
|
||||
for (let i = 0; i < moves.length; i++) {
|
||||
// Wait until the next prompt event arrives
|
||||
|
|
@ -415,7 +416,7 @@ describe('GameHost', () => {
|
|||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
expect(promptEvent.currentPlayer).toBe('X');
|
||||
|
|
|
|||
Loading…
Reference in New Issue