🌻 设计文档小农场 · 野花笔记
← 全部文档/夜市/玩法

数值实测

现行 #数值#模拟器#实测#节奏 docs/夜市/数值实测.md
怎么跑
node tools/sim/night.mjs --hours 1  --policy active --trace    # 开局到一小时
node tools/sim/night.mjs --hours 24 --policy active --trace    # 二十四小时
node tools/sim/night.mjs --compare --hours 4 --runs 3          # 松手判据

配表全部写在 night.mjs 顶部(FOODS / SIGNATURES / COST / TUNING)。 改数不改码。改完必须重跑本文所有数字。

本文所有数字都是这个脚本的实测输出,没有一个字是估的。 报告口径:贪心策略(每次都买回本最快的那一项)跑 N 次取中位数。

一、前 10 分钟:节奏是设计出来的#

时间 金币 收入/秒 客流 λ 产能 μ 队伍 菜单 灶 店员
10 秒 28.6 2.8 0.5 0.4 1.0 1 1 0
30 秒 84.7 2.8 0.5 0.4 2.8 1 1 0
1 分 2.2 4.3 0.5 0.8 5.8 2 2 0
2 分 92.5 5.8 0.8 1.2 5.8 2 3 0
5 分 446 38.1 3.7 5.9 5.8 3 8 3
10 分 1.07 万 750 33.0 27.9 8.0 5 18 9

关键节点(中等运气的一轮):

事件 时间
第一枚金币 0.2 秒
第二口灶 1.0 分
菜单扩到两种 1.0 分
第一位店员 3.3 分
第一次升小费罐 5.2 分
第一位贵客 1.0 分
读这张表要看的三件事
  1. 第一枚金币 0.2 秒。开局不能有任何等待——街上空着超过 2 秒,玩家就关掉了。
  2. 队伍长度前 1 分钟从 1 涨到 6。这是"我忙不过来"的第一次信号, 也是玩家第一次主动想去加灶。这个信号不能来得太早(开局就排满会让人焦虑), 也不能太晚(一分钟还没感觉就太平)。
  3. 3.3 分钟出现第一位店员。从这一刻起玩家可以松手了。 这是整局的第一个"我赢了"的时刻,不能晚于 5 分钟。

二、24 小时曲线#

时间 金币 累计收入/秒 客流 λ 产能 μ 菜单 灶 店员
5 分 663 39.6 3.9 5.8 3 9 2
10 分 7 677 783 33.8 27.9 5 18 9
30 分 57.53 万 2.15 万 158.0 120.2 8 34 19
1 时 691.42 万 10.47 万 269.2 200.0 9 41 24
2 时 3 898.44 万 30.98 万 400.4 296.6 10 48 28
4 时 108.67 万 135.04 万 594.7 450.1 12 58 32
8 时 4.41 亿 386.91 万 748.0 554.1 13 64 36
24 时 134.11 亿 1 165.51 万 955.5 707.7 14/20 72 41
三处要说明的
  1. 4 时那一行金币反而变少了(3 898 万 → 108 万):因为刚买了一口大件。 "收入/秒"这一列是累计毛收入的增量,不含支出,所以是单调的。
  2. 24 小时解锁 14/20 道菜。剩下 6 道是长尾,按当时的收入率还要几十小时。 这是故意的——增量游戏不该在第一天就把菜单开完。
  3. λ 永远是 μ 的 1.35 倍(955.5 / 707.7)。这就是核心玩法 4.2 那个稳态解,说明"产能永远是瓶颈、队伍永远排到街角"这条设计成立。

三、松手判据#

判的是「一直点 ÷ 完全不点」,但要分两个口径看,混在一起会得出错误结论:

3.1 同一摊子状态下(纯系数)#

在场 ÷ 离场 = 1.30×

就是 TUNING.presentBonus = 0.3 这一项。这就是"注意力"的全部标价。

3.2 跑完整局(含复利)#

4 小时,三次取中位:

口径 收入率 相对离线
离线不点(idle) 82.23 万/秒 1.00×
在场不点(present) 169.15 万/秒 2.06×
在场一直点(active) 209.89 万/秒 2.55×

收入结构(active 一轮):贵客占 10%、小费占 1%,其余 89% 是挂机自动收入。

3.3 换算成时间口径#

倍数不能直接读

这是工程约定里记过的一条: 指数档位下"差 3118 倍"换算成时间可能只是"落后 4 分钟"。 报倍数一定要同时给时间口径,否则会把自己吓到。

时点 T 在场不点落后 一直点落后
10 分 1.8 分 3.4 分
30 分 7.9 分 14 分
1 时 17 分 29 分
2 时 35 分 56 分

离线会越落越远(落后时间随时间线性增长),因为收入增长本身在减速: 落后时间 = ln(倍数) / 增长率,增长率降下来,落后就拉长。

这是增量游戏的常态,不是缺陷:离线依然在完整地赚,只是慢一点。 真正的红线是收入率之比不发散——3.1 那一项恒等于 1.30×,就够了。

四、调参过程中踩的五个坑#

前四个是设计问题(调参救不了),第五个是工程问题。 每一个都是先跑模拟器才发现的,不是想出来的。

4.1 坑一:花钱买名气,λ 指数爆炸#

现象:模拟器跑 15 分钟就卡死;打日志看到 λ = 228 人/秒。

根因:把「名气」做成花金币买 +18%。λ 成为瓶颈时买名气给的是线性回报, 而它的涨价被其他轨道的收入增长抵消 → 回本时间恒定 → 无限正反馈。 这就是工程约定里那种"增长率差一点就有一条通吃"的刀刃失衡。

修法不是调参,是改设计:把名气从"花钱买"改成"跟着生意走"—— λ 向 1.35 × 实际成交率 一阶收敛。稳态只剩唯一自洽解,λ 再也不可能失控。 顺带把主界面的概念从 4 个砍到 3 个。

4.2 坑二:加了新菜,收入反而降#

现象:跑了 8 小时,菜单还是只有 1 种。解锁按钮明明买得起(120 金币),贪心策略就是不买。

根因:收入公式写成了「菜单均价 × 产能」。加一道便宜的新菜会拉低均价, 算出来的收益是负的,于是贪心永远不扩菜单。

修法:改成每口灶各自按自己的单价结算: 收入 = 产能利用率 × Σ(每口灶的 单价 / 出餐时间)。 修完顺带出来一条设计:"往灶上放哪道菜"看的是 p/t,不是 p。

4.3 坑三:新菜只配一口灶,把队伍整条堵死#

现象:解锁茶蛋和糖葫芦之后,λ 从 26.7 塌到 4.15,而产能还有 39 却卖不动。 打日志看到 待取 57 / 上限 60(货堆满了),队伍 7/8(人堵满了)。

根因:客人从菜单里均匀点单。新菜只配一口灶,却要分走 1/3 的客流, 那口灶供不上 → 点它的客人永远卡在队首 → 后面的人进不来。

修法(两条一起改才够):

  1. 客人按产能加权点单——需求 ∝ 供给,队列不会再被错配堵住;
  2. 解锁新菜时按 0.5 份/秒 给它配够灶。

只改其中一条,问题还剩一半。

4.4 坑四:贪心策略自己停摆#

现象:跑 2 小时卡在 11.41 亿金币,收入一动不动。

根因:给贪心加了个"回本时间 < 900 秒才买"的门槛。 后期所有项的回本都超过了 900 秒,于是有钱也不花。

修法:改成"永远买当前最优",门槛交给各条轨道自己的涨价曲线去管。

4.5 坑五:离散化假象,λ 卡在 128.00#

现象:λ 涨到 128.00 就不动了,正好是 1.35 × 80。

根因:逐份撮合的模型里,每 tick 最多只能服务「排队位数」个客人—— 12 位 × 10 次/秒 = 120/s 的假上限,而真实产能有 317/s。 把步长调小能绕过,但 24 小时的局要跑几百万 tick。

修法:改成解析式结算(成交率 = min(λ, μ),按产能占比分摊到各道菜)。 顺带把 24 小时的局从跑不完变成几秒。

还有两个小的
  • 试算不能靠 JSON 快照:灶数上百之后每 0.5 秒序列化五遍,8 小时的局直接跑不完。 改成 apply() 返回一个 undo 闭包。
  • "收入/秒"这一列必须用毛收入:用净金币的话,买一次大件会让收入看起来掉 17 倍。

五、还没验的#

诚实列一下,这些是设计里写了但模拟器盖不到的:

项目 为什么没验 打算怎么验
前 60 秒的手感 模拟器量不到"爽不爽" 做出可玩 demo,找 3 个人看前 60 秒
队伍满时的视觉密度 是渲染问题,不是数值问题 场景演示页跑 40 人同屏,量帧率
贵客的回头率是不是真的 +10% 要真人行为数据 上线后埋点:贵客停留时长、点击转化
招牌组合的实际最优搭配 贪心策略没做组合搜索 模拟器加一个"菜单搜索"模式
离线 8 小时的上限是不是太短 需要真实留存数据 A/B:8 小时 vs 24 小时