---
type: tutorial
title: 引擎套件 engine-kit
summary: 一个可选的撰写套件，它从一个普通的 reducer 产出一个合规的回放 run，替你处理确定性、固定步长循环、轨迹编码，以及一次本地自检。
---

`@caputchin/engine-kit` 是一个可选的撰写套件，它把一个普通的游戏 reducer 变成一个合规的 [`run`](/docs/marketplace-game-development/replay-contract)，替你处理确定性、固定步长循环和轨迹编码。它是产出那个产物的一种便利方式，而不是一项要求：你可以完全忽略这个套件，交付一个你手写的光秃秃的 `run`。当你宁愿写普通的游戏逻辑、并让确定性、回放循环和轨迹编码替你处理好时，再去用它。

这个套件重新导出整个 [回放契约](/docs/marketplace-game-development/replay-contract) 面，所以套件用户有单一一个导入点。

```bash
npm install @caputchin/engine-kit
```

## 这个点子：写一个 reducer，得一个 run

这个套件的核心动作，是让你把你的游戏表达为一个 **纯 reducer**：一个接受当前状态和一个 tick 的输入、返回下一个状态的函数。从那个 reducer 加上确定性原语，套件的 `toRun` 适配器产出一个按构造就合规的 `run(seed, config, trace)`。

```js
import { defineEngine, toRun, cap } from "@caputchin/engine-kit";

const engine = defineEngine({
  setup(seed) {
    const rng = cap.rng(seed);          // deterministic, seeded RNG
    return { score: 0, targets: spawn(rng), rng };
  },
  tick(state, input) {
    // pure: same (state, input) always yields the same next state
    return applyInput(state, input);
  },
  result(state) {
    return { passed: state.score >= 3, score: state.score, durationMs: state.elapsed };
  },
});

export const run = toRun(engine);       // a conforming RunFn
```

## 确定性原语

这个套件给你那两样最常打破跨运行时确定性的东西，两者都可播种且可重现：

- `cap.rng(seed)`（以及 `cap.rngFromState`）：一个可播种的 PRNG。用它来做每一个随机选择，而不是 `Math.random()`。
- `cap.math`：跨运行时一致的确定性超越函数（`sin`、`cos`、……），而不是可能不一致的平台 `Math` 那些。

## 可选垫片

- `applyShim()`：中和那些非确定性的全局量（墙上时钟、`Math.random`），好让一次意外的调用在开发中大声失败，而不是在回放时默不作声地分叉。
- `applyDomShim()`：给框架路径用的一个极简 headless DOM，好让一个碰一小片 DOM 面的 reducer 在无 DOM 的隔离体里仍能回放。

## 在发布之前在本地跑它

这个套件的 `replay` 测试床以服务器将要采用的同样方式把一条轨迹跑过你的引擎，而 `selfCheck` 跑一批用例并在你发布 **之前** 报告任何非确定性：

```js
import { selfCheck } from "@caputchin/engine-kit";

const report = selfCheck(engine, { cases: myCases });
if (!report.ok) console.error(report.violations);
```

一次干净的本地 `selfCheck` 是应用市场发布自检会通过的最好预测（它是同一个确定性标准）。这个套件还交付一个 CLI，用于从 `package.json` 脚本或 CI 里跑这个。

## 轨迹编码

`encodeTrace` / `decodeTrace` 给你一个紧凑的、带版本的轨迹格式，好让你实时游戏发出的那条输入流恰好就是你的 `run` 在回放时解码的那条。在两侧都用它们，让两者保持锁步。

## 何时跳过这个套件

当你已经有确定性逻辑（一个定点模拟、一个为规范浮点编译的 WASM 模块）、或者当你的游戏简单到手写 `run` 不费吹灰之力时，就跳过它。平台从不知道也不在乎这个套件有没有产出你的 `run`；它只加载并回放这个契约。

## 另见

- [回放契约](/docs/marketplace-game-development/replay-contract)：这个套件产出的那个 `run` / seed / verdict。
- [构建一个应用市场游戏](/docs/marketplace-game-development/build-a-marketplace-game)：这个套件能支撑其逻辑的那个游戏。
- [caputchin.json 清单](/docs/marketplace-game-development/the-caputchin-json-manifest#the-run-artifact)：把产出的 `run` 作为工件交付。
