replay确定性重放
时间、调度工作和随机抽样都按固定顺序执行。根据重放日志可以重建一次模拟运行,检查点哈希用来确认结果完全一致。
开源 · 0.13.0 · 尚未到 1.0
Canwu Engine
参伍是用 Rust 编写的无界面模拟引擎。你的代码向它提交命令,它负责推进时间、记录发生了什么以及原因,并跟踪每个角色知道什么。渲染、界面和历史内容仍由你的游戏、研究工具或智能体系统负责。
starter.rsuse canwu_api::{Canwu, CommandRequest, CommandRequestId, EntityRef, Issuer, SimDuration};
use canwu_reference_world::{
MovementCommand, ReferenceWorldPlugin, demo_scenario, order_movement,
snapshot as reference_snapshot,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let (scenario, ids) = demo_scenario()?;
let plugin = ReferenceWorldPlugin;
let mut canwu = Canwu::new_with_plugins(35, scenario, &[&plugin])?;
let envelope = order_movement(
Issuer::Actor(ids.commander),
&MovementCommand {
subject: EntityRef::Army(ids.army),
destination: ids.eastern_territory,
cargo: Vec::new(),
},
)?
.at_time(canwu.time());
canwu.enqueue_command(
canwu.time(),
0,
CommandRequest::new(CommandRequestId::new(1), canwu.revision(), envelope),
)?;
canwu.advance_canonical(SimDuration::hours(19))?;
let saved = canwu.snapshot_json()?;
let loaded = Canwu::from_snapshot_json_with_plugins(&saved, &[&plugin])?;
let fork = loaded.fork();
let journal = canwu.replay_journal();
let replayed = Canwu::replay_from_journal(&[&plugin], &journal)?;
assert_eq!(loaded.checkpoint_hash(), canwu.checkpoint_hash());
assert_eq!(fork.checkpoint_hash(), canwu.checkpoint_hash());
assert_eq!(replayed.checkpoint_hash(), canwu.checkpoint_hash());
println!(
"army_location={} checkpoint={}",
reference_snapshot(&replayed)?
.army(ids.army)
.expect("demo army exists")
.location,
replayed.checkpoint_hash()
);
Ok(())
}架构
对外 API 边界 · 这条线以上的代码只使用 canwu-api
引擎内部 · 只能经由 canwu-api 访问
负责渲染、输入、现实时间、账号与历史内容。它依赖 canwu-api,注册需要的插件,提交命令并读取结果。
canwu-debug基于对外 API 与参考整合包的桌面调试客户端(egui)模拟领域扩展以模拟插件的形式提供可复用规则,它们都建立在 canwu-api 之上。按需注册,也可以自己编写。参考内容包提供数据,参考整合包把这些部件组合成可以直接运行、也可以复制改造的小型世界。
canwu-resource资源账户、预留、转移与履约canwu-production生产工艺、设施、工单与产出canwu-movement移动命令、路段与运力池canwu-military军队、行动、战斗与占领canwu-society人群之间的社会传播canwu-culture文化编写、编译与生命周期canwu-law法律程序、版本化法律与适用(实验性)canwu-technology从证据到采用与传播的技术模拟canwu-fiscal财政制度、程序与核算canwu-information文书、副本、访问与解读canwu-correspondence信件、地址、承运人与截取canwu-history-research技术模拟的可选历史研究评估canwu-ming-fiscal带出处的版本化明代财政数据canwu-economy-reference-content经济模型卡与带出处的数据canwu-military-reference-content供参考整合包使用的合成(非史实)军事规则集canwu-reference-world包含军队与地区的小型示例世界,starter 示例使用它canwu-economy-reference包含生产、运输与军事补给的粮食循环canwu-force-supply-reference作为第二个资源消费者的军事补给canwu-military-reference在参考世界中运行的军事扩展canwu-ming-fiscal-reference洪武、万历、弘光三个财政场景canwu-api应用依赖的引擎 crate,扩展 crate 也建立在它之上。其中的 Canwu 类型负责创建和推进模拟运行、接收命令、返回独立快照和角色相对视图,并支持存档、派生分支与重放。下层 crate 中需要的类型都由它重新导出。
Canwu::new_with_plugins用场景和插件列表创建模拟运行enqueue_command把命令排入指定模拟时间的输入队列advance_canonical推进时间并结算到期的全部工作viewer_for_actor按某个角色所知读取世界events · explain列出发生的事件并查询原因snapshot_json · fork · replay_from_journal存档、派生分支和重放canwu-sim持有权威状态。它把收到的命令放入输入队列,运行调度工作,并以边界为单位结算到期工作:边界是某个模拟时间点上的一次整体结算,按 14 个固定阶段运行;任何一步失败,整个边界回滚。它还记录存档和重放所需的证据与哈希。应用只通过 canwu-api 访问它。
canwu-sim运行时、调度、结算、插件、持久化、哈希与重放插件和应用经由 canwu-api 使用的数据类型:发生了什么以及原因、每个角色知道什么、待定的决策,以及路线与运输记录。运行时本身使用其中的事件、知识和决策类型。
canwu-event带因果、受众与证据引用的事件canwu-knowledge角色知识,包括来源、可信度与信息时效canwu-decision决策票据、控制者与决策策略canwu-routing通行时间随时间变化的路线规划canwu-transport运输执行、预订、交接与运力池类型化 ID、确定性随机数、schema 元数据与模拟时间运算。其他所有 crate 都建立在这两个 crate 之上。
canwu-core稳定 ID、确定性随机数与 schema 基础类型canwu-time模拟时间与带溢出检查的时长运算第 1 步,共 7 步
客户端构造一个类型化命令。在 starter 示例中,参考世界把 MovementCommand 包装成命令信封。
let envelope = order_movement(Issuer::Actor(commander), &command)?;第 2 步,共 7 步
enqueue_command 把请求按到期时间放入输入队列,并返回回执。原样重发同一请求会得到同一张回执;同一请求 ID 配不同内容会失败。此时世界状态还没有变化。
canwu.enqueue_command(due_at, priority, CommandRequest::new(id, revision, envelope))?;第 3 步,共 7 步
advance_canonical 推进模拟时间。每一批到期的工作都在一个边界中结算。
canwu.advance_canonical(SimDuration::hours(19))?;第 4 步,共 7 步
引擎检查签发者的权限、命令所基于的修订版本以及预期时间,被拒绝的命令连同原因一起记录。随后插件系统按 14 个固定阶段运行并提出变更,这些变更一起提交,或一起回滚。
14 个结算阶段 →第 5 步,共 7 步
提交的变更生成带有因果与受众的事件。角色知识与报告随之更新,重放日志也多记录一个边界。
第 6 步,共 7 步
玩家或智能体通过 viewer_for_actor 读取,只能看到该角色知道的内容。受信任的上层应用工具还可以用 events() 列出事件,用 explain() 查询原因。
let viewer = canwu.viewer_for_actor(actor)?;安全读取状态 →第 7 步,共 7 步
snapshot_json 保存模拟运行,replay_journal 返回它记录的全部输入。replay_from_journal 根据这些输入重建运行;检查点哈希相同,说明重放完全一致。
let replayed = Canwu::replay_from_journal(&[&plugin], &canwu.replay_journal())?;
assert_eq!(replayed.checkpoint_hash(), canwu.checkpoint_hash());存档、重放与派生分支 →设计保证
replay时间、调度工作和随机抽样都按固定顺序执行。根据重放日志可以重建一次模拟运行,检查点哈希用来确认结果完全一致。
knowledge世界的真实状态与角色知识分开保存。每条观察都记录来源、可信度和信息时效,因此角色可能依据迟到或有误的报告行事。
commands客户端只通过提交命令改变世界,通过快照、事件或角色视图读取世界。游戏、研究笔记本和 AI 智能体调用的是同一组接口。
安装
[dependencies]
canwu-api = "=0.13.0"五分钟上手
git clone https://github.com/PeiyuanQi/canwu
cd canwu
cargo run -p canwu-reference-world --example starterarmy_location=3 checkpoint=dd9796a606983d02eafd3495df11b20bb8423cec33f96c0eaddc24e44e4e2de1AI 智能体
viewer_for_actor(actor)viewer.query_knowledge(query)viewer.visible_changes_since(time)viewer.evaluation_traces(subject, after)enqueue_command(...)advance_canonical(...)actor-scoped// 1. 读取该角色知道的内容
let viewer = canwu.viewer_for_actor(actor)?;
let knowledge = viewer.query_knowledge(&query)?;
// 2. 作出决定,提交类型化命令
canwu.enqueue_command(time, priority, request)?;
canwu.advance_canonical(duration)?;
// 3. 查看该角色能看到的变化
let changes = canwu.viewer_for_actor(actor)?.visible_changes_since(since);应用场景
在任意地图或渲染器下运行模拟。
运行可重复的场景,派生平行现实并比较结果。
为多个智能体提供不同的知识、权限与命令。
在可重放的状态之上构建时间线、地图与课堂工具。
参与贡献