[P1][T21] 重写 scripts/verify-items.ts:全量真实表列映射断言 + TC 树不变量 #109

Closed
opened 2026-09-18 10:50:54 +00:00 by troytt · 1 comment
Owner

问题

母 issue 的验收项明确要求「新增 scripts/verify-items.ts:全量物品表列映射断言」。

现在这个文件已经存在(193 行),但一行 MPQ 都没读 —— 全部用手写的 4 行内联假表:

parseTable(['name\tcode\tlevel', 'Sword\tswo\t1', ...].join('\n'))

等于从零开始。

实现要求

重写为读真实 MPQ,断言下列不变量。所有期望值都是本机 1.13c 实测所得。

数据规模

断言 期望值
基础物品解析数 659(Armor 202 + Weapons 306 + Misc 151,均已剔除 Expansion)
魔法词缀解析数 1378(637 + 741)
其中 spawnable=1 1162(587 + 575)
ItemTypes(有 Code) 98
TreasureClass=1 的 ItemTypes 恰好 5 个(bow weap mele armo abow)
TC 行索引数 852
auto-TC 生成数 100(weap/armo/bow 各 29 + mele 13)
UniqueItems 401 行,enabled=1 385
SetItems / Sets 127 / 32
Runes 169 行,complete=1 78、server=1 23
Gems 68
ItemStatCost / Properties 359 / 268
ItemRatio 6

TC 树不变量

  • 218 个悬空引用全部可解析:100 个 auto-TC + 118 个真实物品码,未知数为 0
  • TC 树无环;从任一怪物 TC 出发可在有限步收敛到叶子
  • 展开深度 ≤ 64
  • 每个节点 NoDrop + ΣProb > 0
  • Magic 品质因子列的 231 个非空值全部等于 1024(防止数据源被换掉)

定点概率验算

  • Act 1 H2H A 单人空掉落率 = 100/160 = 62.5%
  • Cow (H) 单人空掉落率 = 62.5%
  • Act 1 H2H A 的完整展开与手算概率树一致:
gld              21/160 = 13.125%
Act 1 Equip A    16/160 = 10.000%  → weap3 5% / armo3 5%
Act 1 Junk       21/160 = 13.125%
Act 1 Good        2/160 =  1.250%
NoDrop          100/160 = 62.500%
  • Act 5 (H) Equip B 的注释列自洽:SumItems=36 = 本层 ΣProb,TotalProb=1566 = 36+1530,DropChance=0.9770 = 1530/1566

列映射交叉核对

  • cap:level=1 minac=3 maxac=5 reqstr=0 durability=12 gemsockets=2 ubercode=xap ultracode=uap
  • Weapons.txt 索引 18 的空列名不污染任何字段
  • Armor.txt 重复的 mindam/maxdam 取到的是正确那一组
  • 所有 Properties.statN → ItemStatCost.Stat 悬空数为 0
  • 所有词缀 modNcode → Properties.code 悬空数为 0
  • 所有 itype/etype → ItemTypes.Code 悬空数为 0

本地化覆盖

  • ENG:Armor 202/202、Weapons 306/306、Misc 143/151
  • CHI 用 UTF-8 解码,cap → 帽子
  • CHI 缺 expansionstring 时 fallback 到 ENG 生效

RNG(Q1 选 B)

  • 固定 seed 下 state64 = lo * 0x6AC690C5 + hi 的前 N 个输出与手算序列逐位相等
  • Rand(seed, 1024) 走 AND 分支、Rand(seed, 1000) 走取模分支

模拟器 vs 解析概率

  • 100 万次蒙特卡洛采样,各叶子物品频率落在 T-22 解析值的 99% 置信区间内

陷阱

[!WARNING]
期望值写死在测试里是有意为之 —— 它们是金标准的快照。如果哪天这些数字变了,说明 MPQ 数据源被换了(或解析器坏了),应当让测试失败而不是自适应。

[!NOTE]
脚本要读 samples/d2/(指向本机 /usr/local/google/home/taodao/d2-data 的软链接)。CI 环境没有 MPQ 时应跳过并明确提示,而不是静默通过。

验收标准

  • 上述所有断言实现并通过
  • 不再有任何内联假表
  • npm run typecheck 0 error
  • npx vitest run 全绿
  • npx tsx scripts/verify-items.ts 新增断言通过

溯源

金标准(数据) 1.13c MPQ data\global\excel\ 下的 全部 17 张物品表(本机 /usr/local/google/home/taodao/d2-data,挂载序 d2data → d2exp → Patch_D2,Patch_D2 优先)
本 issue 结论强度 全部期望值为本机 1.13c 实测确证

[!WARNING]
D2MOO 是 1.10f,不是本项目的金标准。 它只能作线索使用。任何涉及常量、列序、整数截断位置或 RNG 消耗次数的结论,都必须回到 1.13c 的表格或 DLL 复核后再落地。

## 问题 母 issue 的验收项明确要求「新增 `scripts/verify-items.ts`:全量物品表列映射断言」。 现在这个文件**已经存在**(193 行),但**一行 MPQ 都没读** —— 全部用手写的 4 行内联假表: ```ts parseTable(['name\tcode\tlevel', 'Sword\tswo\t1', ...].join('\n')) ``` 等于从零开始。 ## 实现要求 重写为读**真实 MPQ**,断言下列不变量。所有期望值都是本机 1.13c 实测所得。 ### 数据规模 | 断言 | 期望值 | |---|---| | 基础物品解析数 | **659**(Armor 202 + Weapons 306 + Misc 151,均已剔除 `Expansion`) | | 魔法词缀解析数 | **1378**(637 + 741) | | 其中 `spawnable=1` | **1162**(587 + 575) | | ItemTypes(有 Code) | **98** | | `TreasureClass=1` 的 ItemTypes | **恰好 5 个**(`bow` `weap` `mele` `armo` `abow`) | | TC 行索引数 | **852** | | auto-TC 生成数 | **100**(`weap`/`armo`/`bow` 各 29 + `mele` 13) | | UniqueItems | 401 行,`enabled=1` **385** | | SetItems / Sets | **127 / 32** | | Runes | 169 行,`complete=1` **78**、`server=1` **23** | | Gems | **68** | | ItemStatCost / Properties | **359 / 268** | | ItemRatio | **6** | ### TC 树不变量 - [ ] **218 个悬空引用全部可解析**:100 个 auto-TC + 118 个真实物品码,**未知数为 0** - [ ] TC 树**无环**;从任一怪物 TC 出发可在有限步收敛到叶子 - [ ] 展开深度 **≤ 64** - [ ] 每个节点 `NoDrop + ΣProb > 0` - [ ] `Magic` 品质因子列的 231 个非空值**全部等于 1024**(防止数据源被换掉) ### 定点概率验算 - [ ] `Act 1 H2H A` 单人空掉落率 = `100/160` = **62.5%** - [ ] `Cow (H)` 单人空掉落率 = **62.5%** - [ ] `Act 1 H2H A` 的完整展开与手算概率树一致: ``` gld 21/160 = 13.125% Act 1 Equip A 16/160 = 10.000% → weap3 5% / armo3 5% Act 1 Junk 21/160 = 13.125% Act 1 Good 2/160 = 1.250% NoDrop 100/160 = 62.500% ``` - [ ] `Act 5 (H) Equip B` 的注释列自洽:`SumItems=36` = 本层 ΣProb,`TotalProb=1566 = 36+1530`,`DropChance=0.9770 = 1530/1566` ### 列映射交叉核对 - [ ] `cap`:`level=1 minac=3 maxac=5 reqstr=0 durability=12 gemsockets=2 ubercode=xap ultracode=uap` - [ ] `Weapons.txt` 索引 18 的空列名不污染任何字段 - [ ] `Armor.txt` 重复的 `mindam`/`maxdam` 取到的是正确那一组 - [ ] 所有 `Properties.statN` → `ItemStatCost.Stat` 悬空数为 0 - [ ] 所有词缀 `modNcode` → `Properties.code` 悬空数为 0 - [ ] 所有 `itype`/`etype` → `ItemTypes.Code` 悬空数为 0 ### 本地化覆盖 - [ ] ENG:Armor **202/202**、Weapons **306/306**、Misc **143/151** - [ ] CHI 用 UTF-8 解码,`cap` → `帽子` - [ ] CHI 缺 expansionstring 时 fallback 到 ENG 生效 ### RNG(Q1 选 B) - [ ] 固定 seed 下 `state64 = lo * 0x6AC690C5 + hi` 的前 N 个输出与手算序列**逐位相等** - [ ] `Rand(seed, 1024)` 走 AND 分支、`Rand(seed, 1000)` 走取模分支 ### 模拟器 vs 解析概率 - [ ] 100 万次蒙特卡洛采样,各叶子物品频率落在 T-22 解析值的 **99% 置信区间**内 ## 陷阱 > [!WARNING] > 期望值写死在测试里是**有意为之** —— 它们是金标准的快照。如果哪天这些数字变了,说明 MPQ 数据源被换了(或解析器坏了),应当**让测试失败**而不是自适应。 > [!NOTE] > 脚本要读 `samples/d2/`(指向本机 `/usr/local/google/home/taodao/d2-data` 的软链接)。CI 环境没有 MPQ 时应**跳过并明确提示**,而不是静默通过。 ## 验收标准 - [ ] 上述所有断言实现并通过 - [ ] 不再有任何内联假表 - [ ] `npm run typecheck` 0 error - [ ] `npx vitest run` 全绿 - [ ] `npx tsx scripts/verify-items.ts` 新增断言通过 --- ### 溯源 | | | |---|---| | 金标准(数据) | 1.13c MPQ `data\global\excel\` 下的 `全部 17 张物品表`(本机 `/usr/local/google/home/taodao/d2-data`,挂载序 d2data → d2exp → Patch_D2,**Patch_D2 优先**) | | 本 issue 结论强度 | 全部期望值为**本机 1.13c 实测确证** | > [!WARNING] > **D2MOO 是 1.10f,不是本项目的金标准。** 它只能作线索使用。任何涉及常量、列序、整数截断位置或 RNG 消耗次数的结论,都必须回到 1.13c 的表格或 DLL 复核后再落地。
troytt added this to the [M10] 真实物品系统:500 基础物品、1000 词缀、TreasureClassEx 掉落树 milestone 2026-09-18 10:50:54 +00:00
Author
Owner

已重写 scripts/verify-items.ts:断言 659 基础物品、1378 词缀、98 类型、852+100 TC、385 暗金、127 套装、78 符文之语、68 宝石、218 悬空引用解析、TC 树深度<=64无环、固定点概率、中文 UTF-8 与 100万次蒙特卡洛 99% 置信区间收敛,合并入 main (commit 93874f4)。

已重写 scripts/verify-items.ts:断言 659 基础物品、1378 词缀、98 类型、852+100 TC、385 暗金、127 套装、78 符文之语、68 宝石、218 悬空引用解析、TC 树深度<=64无环、固定点概率、中文 UTF-8 与 100万次蒙特卡洛 99% 置信区间收敛,合并入 main (commit 93874f4)。
Sign in to join this conversation.
No Label
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: troytt/diablo2-web#109
No description provided.