附录 D 全书公式、参数与数据集索引

《小游戏开发实战》卷三《上线、变现与长线运营》的复现与索引附录:按 16 章列出 pebble/ 下全部 config / sim / core / web 文件,写清本卷工具链的 l<章号>_ 前缀约定(与卷一 ch、卷二 e 前缀区分)、两个 python3 与 Node 26 的环境速查、六层可运行性口径(S/P/W/D/X/B)的逐层复现命令,并给出全书公式与参数索引。

这一页解决一个问题:卷三里出现的每一段代码、每一份配置、每一个数据集,在 pebble/ 里叫什么、放在哪、怎么跑。 它是一张可检索的清单,也是一份复现手册——比卷二附录 C 多出一份「全书公式与参数索引」。

一、这份索引解决什么

卷三的每一节都至少带一个 S / P / W 层的可运行产物。产物多了就需要一张总表,回答三件事:

  1. 这个产物叫什么名字、放在哪个目录——按 16 章列出 pebble/ 下的文件,命名规则统一,一眼能定位。
  2. 它属于哪一层、怎么验证——六层口径(S / P / W / D / X / B)各自的复现命令不同,这一页把它们并排写出来。
  3. 它依赖什么环境——两个 python3、Node 26、Playwright 复用,任何一处不对都会让「代码看起来错了」,其实只是环境错了。

贯穿全书的仍是《合成小岛》(merge-island)与工具链 pebble。卷三沿用前两卷的两条主张:一份 config,三种消费、一份逻辑,两种宿主;config/ 是唯一真源,Python 仿真、TS 内核、浏览器原型读同一份数字。

二、卷三工具链命名约定

这是卷三最容易踩的坑,先讲清楚。

卷一用 ch00–ch15 前缀,卷二改用 e<章号>_ 前缀,卷三一律用 l<章号>_ 前缀(两位章号,l01…l16):

config/l03_ltv.json            # 卷三的配置
sim/l03_ltv.py                 # 卷三的仿真
core/src/l03/revenue.ts        # 卷三的内核源码目录
core/test/l03_revenue.test.ts  # 卷三的测试
web/build_l03_calculator.mjs   # 卷三的单文件 HTML 构建脚本
web/verify_l03_calculator.mjs  # 卷三的 Playwright 无头验收

为什么必须换前缀:node --test 接收的是 glob,不是目录。若卷三继续沿用旧前缀,通配符会同时匹配到两卷的测试文件,一条命令里混进另一卷的断言,输出就不可信了。换成 l<章号>_ 之后:

  • 卷一:node --test 'core/test/ch*.test.ts'
  • 卷二:node --test 'core/test/e*.test.ts'
  • 卷三:node --test 'core/test/l*.test.ts'

三个 glob 互不重叠。注意要带上两位章号与下划线(l04_ 而不是裸 l*),复现命令里写全 l04_*.test.ts 这样的精确前缀,才不会被无关文件污染。

各章文件对照表

下表把卷三 16 章各自的产物按目录列出。同一章在 config/、sim/、core/test/ 之间尽量保持同名,便于对照;core/src/l<NN>/ 是该章的内核源码目录。

章主题configsimcore/srccore/testweb
1广告变现config/l01_*.jsonsim/l01_*.pycore/src/l01/core/test/l01_*.test.tsweb/build_l01_ads.mjs · web/verify_l01_ads.mjs
2内购与混合变现config/l02_*.jsonsim/l02_*.pycore/src/l02/core/test/l02_*.test.tsweb/build_l02_hybrid.mjs · web/verify_l02_hybrid.mjs
3收入建模与 LTVconfig/l03_*.jsonsim/l03_*.pycore/src/l03/core/test/l03_*.test.tsweb/build_l03_calculator.mjs · web/verify_l03_calculator.mjs
4定价与促销config/l04_*.jsonsim/l04_*.pycore/src/l04/core/test/l04_*.test.tsweb/build_l04_discount.mjs · web/verify_l04_discount.mjs
5买量与素材config/l05_*.jsonsim/l05_*.pycore/src/l05/core/test/l05_*.test.tsweb/build_l05_consistency.mjs · web/verify_l05_consistency.mjs
6自然增长与裂变config/l06_*.jsonsim/l06_*.pycore/src/l06/core/test/l06_*.test.tsweb/build_l06_traffic.mjs · web/verify_l06_traffic.mjs
7留存漏斗与召回config/l07_*.jsonsim/l07_*.pycore/src/l07/core/test/l07_*.test.tsweb/build_l07_joint.mjs · web/verify_l07_joint.mjs
8实验与显著性config/l08_*.jsonsim/l08_*.pycore/src/l08/core/test/l08_*.test.tsweb/build_l08_experiment.mjs · web/verify_l08_experiment.mjs
9发布与灰度config/l09_*.jsonsim/l09_*.pycore/src/l09/core/test/l09_*.test.ts—
10合规与防沉迷config/l10_*.jsonsim/l10_*.pycore/src/l10/core/test/l10_*.test.ts—
11数据体系config/l11_*.jsonsim/l11_*.pycore/src/l11/core/test/l11_*.test.ts—
12活动与赛季运营config/l12_*.jsonsim/l12_*.pycore/src/l12/core/test/l12_*.test.tsweb/build_l12_season.mjs · web/verify_l12_season.mjs
13长线运营与退场config/l13_*.jsonsim/l13_*.pycore/src/l13/core/test/l13_*.test.ts—
14数据表现与迭代config/l14_*.jsonsim/l14_*.pycore/src/l14/core/test/l14_*.test.ts—
15失败模式复盘config/l15_*.jsonsim/l15_*.pycore/src/l15/core/test/l15_*.test.tsweb/build_l15_switchboard.mjs · web/verify_l15_switchboard.mjs
16全链路装配config/l16_*.jsonsim/l16_*.pycore/src/l16/core/test/l16_*.test.tsweb/build_l16_replay.mjs · web/verify_l16_replay.mjs

实测规模:config/ 48 个 l*.json、sim/ 53 个 l*.py(其中 5 个 *_bench.py 属 B 层)、core/test/ 26 个 l*.test.ts、web/ 11 对 build_l*.mjs + verify_l*.mjs、web/dist/ 11 个 l*.html、out/ 146 个产物文件(*.json / *.csv / *.svg)。同一章的 config/ / sim/ / core/ / web/ 四类文件用同一个 <name> 串联,这是「一份 config 三种消费」在文件系统上的体现。

三、环境速查

项值说明
Python(S 层)3.14.6,/opt/homebrew/bin/python3带 numpy 2.5.1 / pandas 3.0.3 / PIL 12.3.0
Python(系统)3.9.6,/usr/bin/python3无 numpy/pandas,正文命令一律写全路径
Node26.5.0原生跑 TypeScript,strip-only,禁 enum / namespace / 参数属性 / 装饰器
Playwright1.59.1,Chromium 已缓存装在 /opt/homebrew/lib/node_modules,用 NODE_PATH + createRequire 复用
matplotlib / scipy未安装图表走纯 Python SVG(xml.etree);显著性用自实现 erf / probit
tsc / tsx / vitest / npm 包均不使用无 package.json、无 npm install

两个高频陷阱,先记住:

  1. 用错 python3 会静默降级——S 层仿真依赖 numpy / pandas,系统 3.9.6 没有这些库,import pandas 直接报 ModuleNotFoundError。命令一律写全路径 /opt/homebrew/bin/python3。
  2. NODE_PATH 对 ESM import 无效——W 层脚本里必须 createRequire(import.meta.url) 之后 require("playwright"),不能写 import { chromium } from "playwright"。

下面这段是 S 层环境自检脚本 sim/ch00_env_check.py(与卷一、卷二共用)。它依次确认解释器、依赖与 Node 版本,是整套书稿的第一条可运行命令:

# file: sim/ch00_env_check.py
# tier: S
# verify: /opt/homebrew/bin/python3 sim/ch00_env_check.py
"""环境自检:确认《小游戏开发实战》三卷需要的运行时都在,并打印 pebble 的能力阶梯。

本机有两个 python3,不要混:
  /opt/homebrew/bin/python3 = 3.14.6(带 numpy / pandas / PIL),S 层仿真一律用它;
  /usr/bin/python3          = 3.9.6(系统自带,无 numpy/pandas),只能跑最朴素的脚本。
"""

from __future__ import annotations

import os
import shutil
import subprocess
import sys

EXPECT_PY = "/opt/homebrew/bin/python3"


def main() -> int:
    ok = True

    # /opt/homebrew/bin/python3 是符号链接,比较时解析到真实二进制再判断
    real = os.path.realpath(sys.executable)
    print(f"python     {sys.version.split()[0]}  ({real})")
    if real != os.path.realpath(EXPECT_PY):
        print(f"           注意:正文脚本一律用 {EXPECT_PY},当前解释器不同")

    try:
        import numpy
        import pandas

        print(f"numpy      {numpy.__version__}")
        print(f"pandas     {pandas.__version__}")
    except ImportError as e:  # noqa: BLE001
        ok = False
        print(f"FAIL 缺依赖:{e}(S 层需要 numpy 与 pandas)")

    node = shutil.which("node")
    if not node:
        ok = False
        print("FAIL node 未安装(P 层需要 Node 26 或更高)")
    else:
        ver = subprocess.run([node, "-v"], capture_output=True, text=True).stdout.strip()
        major = int(ver.lstrip("v").split(".")[0])
        note = "" if major >= 26 else "  (低于 26,原生 TypeScript 不可用)"
        print(f"node       {ver}{note}")
        ok = ok and major >= 26

    print("pebble 能力阶梯:S 仿真 / P 内核 / W 网页 / D 静态 / X 不可复现 / B 基准")
    print("OK 环境就绪" if ok else "FAIL 环境不满足")
    return 0 if ok else 1


if __name__ == "__main__":
    raise SystemExit(main())

四、六层可运行性口径与复现命令

每一层都有固定的复现命令。<NN> 换成章号(如 l03),<name> 换成该章的文件名,命令均在 pebble/ 下执行:

层含义命令
S 仿真Python 读 config 跑仿真,产出 out/*.json / .csv / .svg`for f in sim/l_*.py; do /opt/homebrew/bin/python3 “$f” –config config/ –out out/
P 内核TypeScript 内核 + node --test 断言node --test 'core/test/l<NN>_*.test.ts'
W 网页单文件 HTML 构建 + Playwright 无头验收node web/build_l<NN>_<name>.mjs && NODE_PATH=/opt/homebrew/lib/node_modules node web/verify_l<NN>_<name>.mjs web/dist/l<NN>.html
D 静态config schema、检查清单、规则表离线校验/opt/homebrew/bin/python3 sim/ch00_validate_config.py --config config/ --schema config/ch00_schema.json
X 不可复现真实平台后台、真机帧率、审核结果、真实出价无命令——正文每处已标注
B 基准*_bench.py 软告警基准`for f in sim/*_bench.py; do /opt/homebrew/bin/python3 “$f” –config config/ –out out/

为什么 S 层要写 for f in …; do …; done 而不是 python3 sim/l03_*.py:shell 会把通配符展开成多个文件名传给同一个 python3,只有第一个脚本真的执行,其余被当成参数吃掉。逐个跑或用 for 循环才安全。node --test 自己展开 glob,不受影响。

五、全书公式与参数索引

下面按章列出核心公式与关键参数名。参数名一律取自对应 config/l<NN>_*.json 的键,金额一律用整数(分 / 0.1 分 deciFen)、比例用整数百分比 / 千分比,全程地板除、无浮点,故同一份 config 在 Python、TS 内核、浏览器三处逐位一致。

  • 第 1 章 广告变现:impressionsPerDau = requestsPerDau × fillPermille // 1000;revenueDeciFen = requestsPerDau × fillPermille × ecpmFen // 100000。参数 requestsPerDau / fillPermille / ecpmFen / floorFen(填充率与 eCPM 反向,收益是乘积的内部最大值)。
  • 第 2 章 内购与混合变现:内购按 priceFen × buyersPerDauPermille 折算人均;混合协同把 IAA 与 IAP 的 adRequestsPerDau / fillPct / ecpmFen / arppuFen 放进同一份 base,扫描 adLoadSweep 看总收益峰值。参数 priceTiers / buyersPerDauPermille / synergy。
  • 第 3 章 收入建模与 LTV:iaaArpdauDeciFen = adRequestsPerDau × fillPct × ecpmFen // 10000;iapArpdauDeciFen = arppuFen × payRatePermille // 100;arpdauDeciFen = iaa + iap;dayArpuDeciFen(d) = arpdauDeciFen × retentionPermille[d] // 1000;ltvDeciFen(D) = Σ_{d=0..D-1} dayArpuDeciFen(d);paybackDay = min{ D : ltvDeciFen(D) ≥ cpaFen × 10 }。参数 arpdauDeciFen / cpaFen / horizonDays / retentionPermille。
  • 第 4 章 定价与促销:弧弹性绝对值 |E|‰ = ΔQ × (P1 + P2) // ((Q1 + Q2) × ΔP)(|E| > 1000 为富有弹性);discountPct = (listPriceFen − priceFen) × 100 // listPriceFen;净收入 = 毛收入 − 需求前置 + 参考价侵蚀 + 鲸鱼流失三项副作用。参数 listPriceFen / priceFen / units / pullForwardPct。
  • 第 5 章 买量与素材:cpiFen = cpmFen × 1000 // (ctrPermille × ipmPermille);activePermille(d) = Σ_{t=0..d} retentionPermille(t) // 1000;ltvDeciFen(d) = arpdauDeciFen × activePermille(d) // 1000;roasPermille = ltvDeciFen(d) × 100 // cpiFen(1000 即回本)。参数 cpmFen / ctrPermille / ipmPermille / arpdauDeciFen(每日留存按 D1/D7/D30 锚点整数线性插值)。
  • 第 6 章 自然增长与裂变:ampl = kPermille // viralCycleDays;viralNew = 上一日新增 × ampl // 1000;DAU = 上日 DAU × retentionPermille // 1000 + 当日新增;有效获客成本 = 买量花费 // 全部新增。参数 kPermille / viralCycleDays / retentionPermille / paidNewPerDay / wordOfMouthPerDay(裂变放大的是「上一日新增流」而非存量 DAU)。
  • 第 7 章 留存漏斗与召回:风险分 = 触发特征权重之和,按 minScore 分档,以「高风险」为预测正类算精确率 / 召回率 / 提升度;reached = 高风险人数 × reachPermille // 1000;reactivated = reached × reactivatePermille // 1000;ROI = 回收 // 成本。参数 churnBasePermille / triggerIfChurnPermille / triggerIfStayPermille / reachPermille / reactivatePermille / reactivatedValueDeciFen / costCent。
  • 第 8 章 实验与显著性:两比例 z 检验,normal_cdf(z) = 0.5 × (1 + erf(z / √2)),erf 用 A&S 7.1.26 自实现(误差 < 1.5e-7),probit 用 Acklam 逆正态近似(scipy 未装,须自实现);按基线率 / MDE / α / 功效算每臂最小样本量。参数 baselineRatePermille / mdePermille / alphaPermille / powerPermille / dailyUsersPerArm / observed。
  • 第 9 章 发布与灰度:新版本按 rampSteps 五档(1% / 5% / 20% / 50% / 100%)逐步放量,每档观察一段时间;崩溃率越 thresholds 上限或留存低于下限即回滚。参数 rampSteps / thresholds / defectRatePermille / dauPerDay。
  • 第 10 章 合规与防沉迷:红线规则为子串匹配(大小写归一)分级处置,severityLevels 决定处置等级;防沉迷按实名 / 时段 / 时长的规则表过滤。参数 severityLevels / rules / texts。
  • 第 11 章 数据体系:指标树北极星在根,向下分解为 L1 / L2;每条边带 contributionPermille,同一父节点下子权重之和必须为 1000;叶子给 raw / target / direction,归一到 0–1000 的 scorePermille,内部节点由子节点加权回算。参数 northStar / nodes / contributionPermille / direction(up 越高越好 / down 越低越好)。
  • 第 12 章 活动与赛季运营:每周自然衰减 weeklyDecayPermille,季末冲刺加成 sprintBoostPermille,季间衔接 offSeasonDecayPermille;阶梯达成率由种子化 LCG 生成积分分布后统计。参数 economy / season / ladder / targetIndexPermille。
  • 第 13 章 长线运营与退场:dau 每周按 weeklyDecayPermille 衰减且不低于 floorDau;周收入 = dau × arpdauDeciFen × 7;周成本 = 固定成本 + dau × 变动成本 × 7;利润 = 收入 − 成本;退场触发 = 连续 lossWeeks 周利润为负,或 dau 跌破 dauFloorForSunset。参数 weeklyDecayPermille / floorDau / arpdauDeciFen / fixedWeeklyCostDeciFen / variableCostPerDauDeciFen / sunsetRules。
  • 第 14 章 数据表现与迭代:所有指标以「基线 = 1000」的指数表示,每周自然衰减 decayPermillePerWeek,迭代在某周注入一次持久增量 liftPermille。参数 baseline / decayPermillePerWeek / iterations。
  • 第 15 章 失败模式复盘:以一组健康 baseline 为参照,failures 里每条用一个 patch 覆盖若干参数,喂进第 5 章(买量 ROI)与第 7 章(留存)同一套模型,看它跑出什么坏数字。参数 baseline / failures / budgetFen。
  • 第 16 章 全链路装配:回放录制由 seed + count + spanMs + cols 确定性生成,时间戳均匀铺在 (0, spanMs]、整数毫秒升序,下标由整数 LCG 产生;Node 侧与浏览器侧读同一份参数、跑同一段 core/src/l16/replay.ts,终局摘要与 digest 必须逐位相同。参数 seed / count / spanMs / cols / startValue / mergeAt。

六、一键复现与排错顺序

复现任意一节的标准动作:把该节的 file: 块还原到 pebble/<file>(工作区已就绪),再跑它 verify: 里那条命令。建议的整卷复现顺序是:

  1. 环境自检:/opt/homebrew/bin/python3 sim/ch00_env_check.py,确认解释器、numpy / pandas 与 Node ≥ 26 都在。
  2. S 层:for f in sim/l*.py; do /opt/homebrew/bin/python3 "$f" --config config/ --out out/ || exit 1; done,跑通全部仿真并落 out/。
  3. P 层:node --test 'core/test/l*.test.ts',全部内核断言通过。
  4. W 层:对第 1–8、12、15、16 章逐个 node web/build_l<NN>_*.mjs,再 NODE_PATH=/opt/homebrew/lib/node_modules node web/verify_l<NN>_*.mjs web/dist/l<NN>.html 无头验收。
  5. B 层:for f in sim/*_bench.py; do /opt/homebrew/bin/python3 "$f" --config config/ --out out/ || exit 1; done,看软告警基准。

数字对不上时的排错顺序:

  1. 解释器对吗? 用了系统 3.9.6 会在 import pandas 处报 ModuleNotFoundError,看起来像「代码错了」,其实是解释器错了。命令一律写 /opt/homebrew/bin/python3。
  2. node 版本对吗? 低于 26 就没有原生 TypeScript,.ts 直接跑不起来。
  3. glob 加引号了吗? node --test 'core/test/l04_*.test.ts' 的引号不能省,否则 shell 先展开、再被 node --test 当多个参数处理。node --test <目录> 不会发现 .ts,必须传 glob。
  4. 前缀对吗? 卷三是 l<NN>_,不是卷一的 ch<NN>_ 或卷二的 e<NN>_。写错前缀会匹配不到文件(空跑)或误匹配另一卷。
  5. Playwright 预热了吗? W 层未用 createRequire 复用全局包时,无头浏览器起不来,canvas 截图为空白——这是环境问题,不是渲染错。
  6. 配置被改过吗? 这正是「一份 config 三种消费」该有的行为——改 config/l03_*.json 里的参数,sim/、core/、web/ 三处结果都会跟着变。
  7. 是不是「同机相对量级」? 正文标了「同机相对量级,绝对数值不可移植」的地方,绝对值依赖机器,只有相对关系可移植。
  8. 还不对,那就是书错了。 请告诉我具体的节号与数字。

环境口径与卷一、卷二的实跑命令另见卷一的附录 A 与卷二的附录 C 。回到卷三目录页 。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「minigame」更多文章

  1. 附录 F:全书公式、参数与数据集索引
  2. 9.3 同意管理与数据出境
  3. 9.2 COPPA 与儿童向游戏的限制