refactor: improve rule handling

This commit is contained in:
2026-04-02 00:44:29 +08:00
parent ff9d9bd9a1
commit e06dc8ecba
4 changed files with 184 additions and 226 deletions
+24 -35
View File
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { createRule, type RuleContext, type GameContextLike } from '../../src/core/rule';
import { createRule, type RuleContext, type RuleEngineHost } from '../../src/core/rule';
import { createGameContext } from '../../src/core/context';
import type { Command } from '../../src/utils/command';
@@ -7,6 +7,10 @@ function isCommand(value: Command | RuleContext<unknown>): value is Command {
return 'name' in value;
}
function schema(value: string | { name: string; params: any[]; options: any[]; flags: any[] }) {
return { type: 'schema' as const, value };
}
describe('Rule System', () => {
function createTestGame() {
const game = createGameContext();
@@ -34,7 +38,7 @@ describe('Rule System', () => {
return cmd.params[0];
});
const gen = rule.create.call(game as unknown as GameContextLike, { name: 'test', params: ['card1'], flags: {}, options: {} });
const gen = rule.create.call(game as unknown as RuleEngineHost, { name: 'test', params: ['card1'], flags: {}, options: {} });
const result = gen.next();
expect(result.done).toBe(true);
expect(result.value).toBe('card1');
@@ -46,7 +50,7 @@ describe('Rule System', () => {
const game = createTestGame();
game.registerRule('move', createRule('<from> <to>', function*(cmd) {
yield { name: '', params: [], options: [], flags: [] };
yield schema({ name: '', params: [], options: [], flags: [] });
return { moved: cmd.params[0] };
}));
@@ -62,7 +66,7 @@ describe('Rule System', () => {
const game = createTestGame();
game.registerRule('move', createRule('<from> <to>', function*(cmd) {
const confirm = yield { name: '', params: [], options: [], flags: [] };
const confirm = yield schema({ name: '', params: [], options: [], flags: [] });
const confirmCmd = isCommand(confirm) ? confirm : undefined;
return { moved: cmd.params[0], confirmed: confirmCmd?.name === 'confirm' };
}));
@@ -115,7 +119,7 @@ describe('Rule System', () => {
const game = createTestGame();
game.registerRule('move', createRule('<from> <to>', function*(cmd) {
yield { name: '', params: [], options: [], flags: [] };
yield schema({ name: '', params: [], options: [], flags: [] });
return { moved: cmd.params[0] };
}));
@@ -138,7 +142,7 @@ describe('Rule System', () => {
const game = createTestGame();
game.registerRule('move', createRule('<from> <to>', function*(cmd) {
const response = yield { name: '', params: [], options: [], flags: [] };
const response = yield schema({ name: '', params: [], options: [], flags: [] });
const rcmd = isCommand(response) ? response : undefined;
return { moved: cmd.params[0], response: rcmd?.name };
}));
@@ -154,7 +158,7 @@ describe('Rule System', () => {
const game = createTestGame();
game.registerRule('move', createRule('<from> <to>', function*(cmd) {
const response = yield '<item>';
const response = yield schema('<item>');
const rcmd = isCommand(response) ? response : undefined;
return { response: rcmd?.params[0] };
}));
@@ -170,7 +174,7 @@ describe('Rule System', () => {
const game = createTestGame();
game.registerRule('trade', createRule('<from> <to>', function*(cmd) {
const response = yield '<item> [amount: number]';
const response = yield schema('<item> [amount: number]');
const rcmd = isCommand(response) ? response : undefined;
return { traded: rcmd?.params[0] };
}));
@@ -188,12 +192,12 @@ describe('Rule System', () => {
const game = createTestGame();
game.registerRule('parent', createRule('<action>', function*() {
yield { name: '', params: [], options: [], flags: [] };
yield schema({ name: '', params: [], options: [], flags: [] });
return 'parent done';
}));
game.registerRule('child', createRule('<target>', function*() {
yield { name: '', params: [], options: [], flags: [] };
yield schema({ name: '', params: [], options: [], flags: [] });
return 'child done';
}));
@@ -212,7 +216,7 @@ describe('Rule System', () => {
const game = createTestGame();
game.registerRule('parent', createRule('<action>', function*() {
yield 'child_cmd';
yield schema('child_cmd');
return 'parent done';
}));
@@ -236,7 +240,7 @@ describe('Rule System', () => {
const game = createTestGame();
game.registerRule('parent', createRule('<action>', function*() {
yield 'child_a | child_b';
yield schema('child_a | child_b');
return 'parent done';
}));
@@ -270,7 +274,7 @@ describe('Rule System', () => {
const game = createTestGame();
game.registerRule('test', createRule('<arg>', function*() {
yield { name: '', params: [], options: [], flags: [] };
yield schema({ name: '', params: [], options: [], flags: [] });
return 'done';
}));
@@ -281,21 +285,6 @@ describe('Rule System', () => {
expect(game.ruleContexts.value.length).toBe(1);
expect(game.ruleContexts.value[0].state).toBe('yielded');
});
it('should add context to the context stack', () => {
const game = createTestGame();
game.registerRule('test', createRule('<arg>', function*() {
yield { name: '', params: [], options: [], flags: [] };
return 'done';
}));
const initialStackLength = game.contexts.value.length;
game.dispatchCommand('test arg1');
expect(game.contexts.value.length).toBe(initialStackLength + 1);
});
});
describe('error handling', () => {
@@ -315,7 +304,7 @@ describe('Rule System', () => {
const game = createTestGame();
game.registerRule('parent', createRule('<action>', function*() {
yield 'child';
yield schema('child');
return 'parent done';
}));
@@ -342,7 +331,7 @@ describe('Rule System', () => {
};
game.registerRule('test', createRule('<arg>', function*() {
const cmd = yield customSchema;
const cmd = yield schema(customSchema);
const rcmd = isCommand(cmd) ? cmd : undefined;
return { received: rcmd?.params[0] };
}));
@@ -358,8 +347,8 @@ describe('Rule System', () => {
const game = createTestGame();
game.registerRule('multi', createRule('<start>', function*() {
const a = yield '<value>';
const b = yield '<value>';
const a = yield schema('<value>');
const b = yield schema('<value>');
const acmd = isCommand(a) ? a : undefined;
const bcmd = isCommand(b) ? b : undefined;
return { a: acmd?.params[0], b: bcmd?.params[0] };
@@ -380,13 +369,13 @@ describe('Rule System', () => {
game.registerRule('start', createRule('<player>', function*(cmd) {
const player = cmd.params[0];
const action = yield { name: '', params: [], options: [], flags: [] };
const action = yield schema({ name: '', params: [], options: [], flags: [] });
if (isCommand(action)) {
if (action.name === 'move') {
yield '<target>';
yield schema('<target>');
} else if (action.name === 'attack') {
yield '<target> [--power: number]';
yield schema('<target> [--power: number]');
}
}