这一页解决一个问题:书里的代码怎么在我自己的机器上跑起来,跑出来的数字为什么可能和书上不完全一样。
一、本机实测环境
这套书的环境前提是实测过的,不是「一般来说」:
| 项 | 实测结果 | 说明 |
|---|---|---|
| Python | 3.14.6 | S 层仿真 |
| numpy | 2.5.1 | 数组与随机数 |
| pandas | 3.0.3 | CSV 与数据表 |
| matplotlib | 未安装 | 所以图表走纯 Python SVG |
| scipy | 未安装 | 拟合用 numpy 手写最小二乘 |
| Node | 26.5.0 | P 层原型 |
| tsc / tsx / vitest | 均未安装 | 所以不用它们 |
一条结论:这套书不需要安装任何东西。 Python 有 numpy 和 pandas 就够了,Node 直接跑 TypeScript。没有 package.json,没有 npm install,没有 tsconfig.json。
先确认你机器上的前提:
# file: check_env.sh
# tier: S
# verify: sh check_env.sh
python3 -c "import numpy, pandas; print('numpy', numpy.__version__, 'pandas', pandas.__version__)"
node -v
期望输出类似:
numpy 2.5.1 pandas 3.0.3
v26.5.0
只要 Python 能 import numpy, pandas、Node 版本 ≥ 26,这套书的所有代码都能跑。
二、bastion 目录结构
bastion/
config/
resources.json # 资源定义与上限
buildings.json # 建筑等级、产出曲线、升级成本
units.json # 兵种属性与克制环
battle.json # 战斗规则常量
season.json # 赛季参数(卷二使用)
pricing.json # 付费与定价(卷二使用)
sim/
bastion_core.py # 仿真核心:产出、成本、结算、战斗
svg_line.py # 零依赖 SVG 折线图
ch01_*.py ... ch15_*.py # 各章仿真脚本
proto/
src/
core.ts # 原型核心:与 bastion_core.py 逐位一致
report.ts # 打印对照报告
ch01_*.ts ... ch15_*.ts
test/
bastion.test.ts # 核心断言
ch01.test.ts ... ch15.test.ts
out/ # 仿真产物:JSON / CSV / SVG
config/ 是唯一真源,sim/ 与 proto/ 都只读它。
三、全部实跑命令
S 层(Python 仿真)
# file: run_sim.sh
# tier: S
# verify: sh run_sim.sh
set -e
cd "$(dirname "$0")"
# 1) 仿真核心:产出、结算、升级成本、一场战斗
python3 sim/bastion_core.py
# 2) 零依赖 SVG 折线图
python3 sim/svg_line.py
# 3) 各章仿真(按需)
python3 sim/ch12_loop_sim.py --config config/ --days 180
P 层(TypeScript 原型)
# file: run_proto.sh
# tier: P
# verify: sh run_proto.sh
set -e
cd "$(dirname "$0")"
# 1) 直接执行一个 .ts 文件
node proto/src/report.ts
# 2) 跑断言(注意:必须用 glob,目录形式不发现 .ts)
node --test 'proto/**/*.test.ts'
一个容易踩的坑:
node --test proto/(传目录)不会发现.ts文件,会报MODULE_NOT_FOUND。必须用 glob:node --test 'proto/**/*.test.ts',或者显式指定文件node --test proto/test/ch01.test.ts。这是实测结论。
一键校验
把两侧的对照报告都跑出来,逐位比对:
# file: sim/verify_two_consumers.py
# tier: S
# verify: python3 sim/verify_two_consumers.py
"""验证「一份配置两处消费」:Python 与 TypeScript 读同一份 config,结果必须逐位一致。"""
from __future__ import annotations
import json
import os
import subprocess
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
BASTION = os.path.dirname(HERE)
sys.path.insert(0, HERE)
import bastion_core as core # noqa: E402
def ts_report() -> dict:
out = subprocess.run(
["node", os.path.join(BASTION, "proto", "src", "report.ts")],
capture_output=True, text=True, check=True,
)
return json.loads(out.stdout)
def main() -> int:
py = core.battle_report()
ts = ts_report()
if py == ts:
print("OK 两侧逐位一致")
print(json.dumps(py["productionPerHour"], ensure_ascii=False))
print("battle:", py["battle"]["winner"], "rounds=", py["battle"]["rounds"])
return 0
print("FAIL 两侧不一致")
for k in py:
if py[k] != ts.get(k):
print(f" diff {k}: py={py[k]!r} ts={ts.get(k)!r}")
return 1
if __name__ == "__main__":
raise SystemExit(main())
本机实跑输出:
OK 两侧逐位一致
{"wood": 132, "stone": 110, "iron": 27, "grain": 199}
battle: a rounds= 1
四、跨语言逐位一致的三条硬规则
这是这套工程的地基。违反任意一条,「一份配置两处消费」就会变成「一份配置两个答案」。
规则一:配置里不写浮点系数,写整数比或整数百分比。
{ "outputBase": 60, "outputGrowthNum": 122, "outputGrowthDen": 100 }
不要写 "outputGrowth": 1.22。原因是浮点幂运算在不同运行时末位可能不一致,取整后就会差 1。
规则二:幂运算在 TypeScript 侧必须用 BigInt。
122^9 已经超过 2^53,用普通 number 会丢精度。Python 侧用原生大整数就没这个问题。
// snippet: proto/src/core.ts 中的整数幂实现
function ipow(base: number, exp: number): bigint {
let acc = 1n;
const b = BigInt(base);
for (let i = 0; i < exp; i++) acc *= b;
return acc;
}
规则三:除法一律整数地板除,上取整用整数公式。
- 地板除:Python
a // b,TypeScriptMath.floor(a / b)(且 a、b 为整数)。 - 上取整:
(a + b - 1) // b,不要用浮点ceil。
# snippet: sim/bastion_core.py 中的整数上取整
def alive_count(hp_pool: int, hp: int) -> int:
if hp_pool <= 0:
return 0
return (hp_pool + hp - 1) // hp # 整数上取整
五、数字对不上怎么办
先按这个顺序排查:
- 配置被改过吗? 这正是「一份配置两处消费」该有的行为。改
outputGrowthNum从 122 到 125,5 级伐木场产出会从 132 变到另一个值,书上写的是 122 时的结果。 - 环境版本差得多吗? numpy/pandas 的大版本变化可能影响随机数序列。涉及随机的地方,本套都用自己实现的确定性 PRNG(如 mulberry32),不吃运行时随机数实现。
- 是不是「同机相对量级」的说明? 正文写「同机相对量级,绝对数值不可移植」的地方(比如结算耗时、吞吐),绝对值本来就依赖机器,只有相对关系可移植。
- 还不对,那就是书错了。 请告诉我具体的节号和数字。
六、两条纪律
复现这件事上有两条纪律,这套书从头守到尾:
- 不编造竞品后台数字。真实竞品只作「公开可观察机制」的引用并标注观察时点,需要进后台才能拿到的数字一律不写,这类内容标为 X 层。
- 所有数字来自本机实跑,正文不写「提升 N 倍」式的绝对话,只写「同机同输入下 A 比 B 少 X 个结算轮次」这类可复核的比较。
配置表的字段字典与完整 schema 见附录 B 。回到目录页 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。