boardgame-core/docs/game-module.md

168 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 编写 GameModule
GameModule 是定义游戏逻辑的模块,包含状态定义和命令注册。
## GameModule 结构
一个 GameModule 必须导出两个东西:
```ts
import { createGameCommandRegistry, createRegion } from 'boardgame-core';
export function createInitialState() {
return {
board: createRegion('board', [
{ name: 'x', min: 0, max: 2 },
{ name: 'y', min: 0, max: 2 },
]),
parts: {} as Record<string, Part>,
currentPlayer: 'X' as PlayerType,
winner: null as WinnerType,
turn: 0,
};
}
export const registry = createGameCommandRegistry<ReturnType<typeof createInitialState>>();
registry.register('setup', async function (game) { /* ... */ });
registry.register('play <player> <row:number> <col:number>', async function (game, cmd) { /* ... */ });
```
也可用 `createGameModule` 辅助函数包装:
```ts
export const gameModule = createGameModule({
registry,
createInitialState,
});
```
## 定义游戏状态
建议用 `ReturnType` 推导状态类型:
```ts
export type GameState = ReturnType<typeof createInitialState>;
```
状态通常包含 Region、parts`Record<string, Part>`)以及游戏专属字段(当前玩家、分数等)。
## 注册命令
使用 `registry.register()` 注册命令。Schema 字符串定义了命令格式:
```ts
registry.register('play <player> <row:number> <col:number>', async function (game, player, row, col) {
// game 是 IGameContext<TState>,可访问和修改状态
game.produce(state => {
// state.parts[...].position = [row, col];
});
return { success: true };
});
```
### Schema 语法
| 语法 | 含义 |
|---|---|
| `name` | 命令名 |
| `<param>` | 必填参数(字符串) |
| `<param:number>` | 必填参数(自动转为数字) |
| `[--flag]` | 可选标志 |
| `[-x:number]` | 可选选项(带类型) |
### 命令处理器函数签名
命令处理器接收 `game``IGameContext<TState>`)作为第一个参数,后续参数来自命令解析:
```ts
registry.register('myCommand <arg>', async function (game, arg) {
const state = game.value; // 读取状态
game.produce(d => { d.currentPlayer = 'O'; }); // 同步修改状态
await game.produceAsync(d => { /* ... */ }); // 异步修改(等待动画)
const result = await game.prompt('confirm <action>', validator, currentPlayer);
const subResult = await subCommand(game, player); // 调用子命令
return { success: true };
});
```
`registry.register()` 返回一个可调用函数,可在其他命令中直接调用:
```ts
const subCommand = registry.register('sub <player>', async function (game, player) {
return { score: 10 };
});
// 在另一个命令中使用
registry.register('main', async function (game) {
const result = await subCommand(game, 'X');
// result = { success: true, result: { score: 10 } }
});
```
详见 [API 参考](./api-reference.md)。
## 使用 prompt 等待玩家输入
`game.prompt()` 暂停命令执行,等待外部通过 `host.onInput()` 提交输入:
```ts
const playCmd = await game.prompt(
'play <player> <row:number> <col:number>',
(command) => {
const [player, row, col] = command.params as [PlayerType, number, number];
if (player !== turnPlayer) throw `Invalid player: ${player}`;
if (row < 0 || row > 2 || col < 0 || col > 2) throw `Invalid position`;
if (isCellOccupied(game, row, col)) throw `Cell occupied`;
return { player, row, col }; // 验证通过,返回所需数据
},
game.value.currentPlayer
);
// playCmd = { player, row, col }
```
验证函数中 `throw` 字符串会触发重新提示,返回非 null 值表示验证通过并通过该值 resolve Promise。
## 使用 setup 驱动游戏循环
`setup` 作为入口点驱动游戏循环,通过调用其他命令函数实现:
```ts
// 注册 turn 命令并获取可调用函数
const turnCommand = registry.register('turn <player>', async function (game, player) {
// ... 执行回合逻辑
return { winner: null as WinnerType };
});
// 注册 setup 命令
registry.register('setup', async function (game) {
while (true) {
const currentPlayer = game.value.currentPlayer;
const turnOutput = await turnCommand(game, currentPlayer);
if (!turnOutput.success) throw new Error(turnOutput.error);
game.produce(state => {
state.winner = turnOutput.result.winner;
if (!state.winner) {
state.currentPlayer = state.currentPlayer === 'X' ? 'O' : 'X';
}
});
if (game.value.winner) break;
}
return game.value;
});
```
## Part、Region 和 RNG
详见 [棋子、区域与 RNG](./parts-regions-rng.md)。
## 完整示例
参考以下示例:
- [`src/samples/tic-tac-toe.ts`](../src/samples/tic-tac-toe.ts) - 井字棋2D 棋盘、玩家轮流输入、胜负判定
- [`src/samples/boop/`](../src/samples/boop/) - Boop 游戏:六边形棋盘、推动机制、小猫升级