<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>数据格式 on CloudLake</title><link>https://cloudlake.eu.org/tags/%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F/</link><description>Recent content in 数据格式 on CloudLake</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><lastBuildDate>Fri, 16 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://cloudlake.eu.org/tags/%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F/index.xml" rel="self" type="application/rss+xml"/><item><title>615 张蓝图，6.6 秒：我把《异星工厂》的蓝图拆开了</title><link>https://cloudlake.eu.org/p/factorio-lab/</link><pubDate>Fri, 16 Oct 2026 00:00:00 +0000</pubDate><guid>https://cloudlake.eu.org/p/factorio-lab/</guid><description>&lt;img src="https://cloudlake.eu.org/p/factorio-lab/cover.jpg" alt="Featured image of post 615 张蓝图，6.6 秒：我把《异星工厂》的蓝图拆开了" /&gt;&lt;p&gt;我有一个文件夹，里面躺着 615 个 &lt;code&gt;.txt&lt;/code&gt; 文件。每个文件里是一串以 &lt;code&gt;0eJz...&lt;/code&gt; 开头的字符——这是《异星工厂》（Factorio）的蓝图字符串。&lt;/p&gt;&#10;&lt;p&gt;蓝图是玩家花了几个月搭出来的工厂。它现在只是一串文本，你看不出里面有什么，算不出它能产多少东西，也没法验证它是不是真的能跑。&lt;/p&gt;&#10;&lt;p&gt;我想让它变成&lt;strong&gt;能看、能算、能验证&lt;/strong&gt;的东西。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="一字符串里到底装了什么"&gt;&lt;a href="#%e4%b8%80%e5%ad%97%e7%ac%a6%e4%b8%b2%e9%87%8c%e5%88%b0%e5%ba%95%e8%a3%85%e4%ba%86%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;一、字符串里到底装了什么&#10;&lt;/h2&gt;&lt;p&gt;官方没有公开蓝图格式。资料里的说法是：Base64 编码的 zlib 流，解出来是 JSON。&lt;/p&gt;&#10;&lt;p&gt;照着做，第一步就失败了：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;zlib.error: Error -3 while decompressing data: incorrect header check&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;我换了好几种字母表、试了 URL-safe 变体、补了不同数量的填充，全都不对。最后不猜了，&lt;strong&gt;直接读原始字节&lt;/strong&gt;：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;0e 4a 7a 5f ...&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;&lt;code&gt;0e&lt;/code&gt; 是 Base64 解码的第一个字节？不是。真相是——&lt;strong&gt;第一个字符 &lt;code&gt;0&lt;/code&gt; 根本不属于负载&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;0&lt;/code&gt; 是版本号，本身就是一个 Base64 字符（数值 52）。真正的负载从&lt;strong&gt;下标 1&lt;/strong&gt; 开始。从第二个字符往后解，第一个字节正好是 &lt;code&gt;78&lt;/code&gt;，那是 zlib 的合法头。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;坑一：开头那个字符不是负载。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;坑二：填充没被去掉。&lt;/strong&gt; 好几处资料说 Factorio 会去掉 &lt;code&gt;=&lt;/code&gt;，但三个文件&lt;strong&gt;全都以 &lt;code&gt;=&lt;/code&gt; 或 &lt;code&gt;==&lt;/code&gt; 结尾&lt;/strong&gt;。去掉就刚好差两个字符，解出来是残的。&lt;/p&gt;&#10;&lt;p&gt;结论写在项目里的一句话：&lt;strong&gt;以文件为准，不以文档为准。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;验证方式是往返：&lt;code&gt;decode&lt;/code&gt; 之后再 &lt;code&gt;encode&lt;/code&gt;，必须&lt;strong&gt;逐字符完全相同&lt;/strong&gt;。三个原始文件、以及内部嵌的&lt;strong&gt;全部 615 个蓝图&lt;/strong&gt;，逐字符都通过了。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="二数值不能抄得读"&gt;&lt;a href="#%e4%ba%8c%e6%95%b0%e5%80%bc%e4%b8%8d%e8%83%bd%e6%8a%84%e5%be%97%e8%af%bb" class="header-anchor"&gt;&lt;/a&gt;二、数值不能抄，得读&#10;&lt;/h2&gt;&lt;p&gt;蓝图里只有&amp;quot;这台机器在这里、朝这个方向&amp;quot;，没有&amp;quot;它有多快&amp;quot;。速度表在游戏安装目录里。&lt;/p&gt;&#10;&lt;p&gt;所以我写了一个&lt;strong&gt;受限的 Lua 求值器&lt;/strong&gt;（&lt;code&gt;fablue/lua.py&lt;/code&gt;）：它能处理 &lt;code&gt;data:extend&lt;/code&gt; 语句、表达式、内置库和辅助函数，但&lt;strong&gt;绝不执行宿主代码&lt;/strong&gt;——没有 &lt;code&gt;require&lt;/code&gt;、没有文件系统、没有外部依赖。&lt;/p&gt;&#10;&lt;p&gt;第一版解出 1,367 个原型，还有 149 个 &lt;code&gt;data:extend&lt;/code&gt; 解不出来。加了对跨文件、局部变量、函数返回值的支持之后：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;修复前&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;现在&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;解出的原型&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1,367&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;4,633&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;解不出的 &lt;code&gt;data:extend&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;149&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;25&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;传送带速度&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;12&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;机器速度&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;17&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;剩下 25 个全是跨文件 &lt;code&gt;data.raw&lt;/code&gt; 派生的瓦片/装饰类，&lt;strong&gt;不含速度表&lt;/strong&gt;——这个上限是我报出来的，不是藏起来的。&lt;/p&gt;&#10;&lt;p&gt;读出来的真值（全部来自 &lt;code&gt;data.raw&lt;/code&gt;）：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;项目&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;值&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;传送带速度&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0.03125 / 0.0625 / 0.09375 / 0.125 tile/tick&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;双 lane 吞吐&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;15 / 30 / 45 / 60 items/s&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;单 lane 吞吐&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;7.5 / 15 / 22.5 / 30 items/s&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;组装机 1/2/3&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;crafting_speed &lt;strong&gt;0.5 / 0.75 / 1.25&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;电炉 / 石炉 / 钢炉&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;2 / 1 / 2&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="一个犯了两次的-2-倍错误"&gt;&lt;a href="#%e4%b8%80%e4%b8%aa%e7%8a%af%e4%ba%86%e4%b8%a4%e6%ac%a1%e7%9a%84-2-%e5%80%8d%e9%94%99%e8%af%af" class="header-anchor"&gt;&lt;/a&gt;一个犯了两次的 2 倍错误&#10;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;×480&lt;/code&gt; 是&lt;strong&gt;双 lane 合计&lt;/strong&gt;。推导是 &lt;code&gt;0.03125 × 60 / 0.25 = 7.5&lt;/code&gt;（单 lane），乘 2 才得 15。&lt;/p&gt;&#10;&lt;p&gt;早期版本把这个常数标成 &amp;ldquo;per lane&amp;rdquo;，同时 &lt;code&gt;GameDB.belt_items_per_second&lt;/code&gt; 又乘了一次 lanes——&lt;strong&gt;同一个 2 倍错误，在两个地方各犯了一次&lt;/strong&gt;。因为是&amp;quot;自洽的错&amp;quot;，测试抓不到。&lt;/p&gt;&#10;&lt;p&gt;现在常量改名为&amp;quot;双 lane 因子&amp;quot;，新增 &lt;code&gt;belt_items_per_second_lane&lt;/code&gt; 属性，并把推导写进注释。&lt;strong&gt;单位写清楚，就不会再被隐含。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="三615-个蓝图一个-canvas"&gt;&lt;a href="#%e4%b8%89615-%e4%b8%aa%e8%93%9d%e5%9b%be%e4%b8%80%e4%b8%aa-canvas" class="header-anchor"&gt;&lt;/a&gt;三、615 个蓝图，一个 canvas&#10;&lt;/h2&gt;&lt;p&gt;查看器的约束很硬：&lt;strong&gt;单文件、离线、无外链&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;615 个蓝图、334,856 个实体，如果每个实体画一个 SVG 矩形，是数十 MB 的 DOM。所以：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;一个 canvas&lt;/strong&gt;，不是每蓝图一个&lt;/li&gt;&#10;&lt;li&gt;每实体只存 4 个整数：&lt;code&gt;x2, y2, 类别, 朝向&lt;/code&gt;。坐标翻倍是因为 Factorio 有大量实体放在&lt;strong&gt;半格&lt;/strong&gt;上（整数存不下 0.5）&lt;/li&gt;&#10;&lt;li&gt;全部数据一次性嵌进页面 → &lt;strong&gt;6.04 MB&lt;/strong&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;性能靠&lt;strong&gt;视口裁剪&lt;/strong&gt;：懒建空间哈希 + &lt;code&gt;Int32Array&lt;/code&gt;。一个 34,650 实体的蓝图，在深度缩放下&lt;strong&gt;一帧只画 13 个实体&lt;/strong&gt;；全可见时 p95 是 &lt;strong&gt;8.8 ms&lt;/strong&gt;，中位 0.6 ms。&lt;/p&gt;&#10;&lt;p&gt;实体还&lt;strong&gt;按真实尺寸绘制&lt;/strong&gt;——尺寸取自每个原型的 &lt;code&gt;collision_box&lt;/code&gt;（3×3 的机器是 2.4，补齐到 3 格；火箭发射井 8.4 → 9 格；传送带 0.8 → 1 格），690 个尺寸表项随页面发出。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="四让它算"&gt;&lt;a href="#%e5%9b%9b%e8%ae%a9%e5%ae%83%e7%ae%97" class="header-anchor"&gt;&lt;/a&gt;四、让它算&#10;&lt;/h2&gt;&lt;p&gt;看图不够，我关心的是：&lt;strong&gt;这个工厂到底产多少、卡在哪&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;计算内核输出四类东西：净产出速率、外部输入、配比失衡、传送带饱和。&lt;/p&gt;&#10;&lt;p&gt;一个真实例子（34,650 个实体）：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;传送带饱和 copper-cable 151.1/s，是 express-transport-belt 容量的 3.36 倍&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;净产出 copper-cable 151.13/s、sulfuric-acid 97.00/s、petroleum-gas 34.00/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;外部输入 water 1587.50/s、crude-oil 480.00/s、iron-plate 282.15/s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;瓶颈 未知配方：assembling-machine-3 ×18 台无法确定配方，未计入产出速率&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;核心公式全部写在代码注释里，不从别处抄：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;每秒次数 = crafting_speed ÷ recipe.energy&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;每秒产出 = 次数 × 单次产出量&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;每 lane = belt_speed × 240，双 lane = belt_speed × 480&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;模块效应也计入了&lt;/strong&gt;：同一个机器内的模块加成&lt;strong&gt;相加&lt;/strong&gt;（三个 +50% 速度模块 = ×2.5，不是 1.5³）。有一条专门的断言守着：组装机 3 做铜缆，无模块 &lt;strong&gt;5.0/s&lt;/strong&gt;，装三个速度模块 &lt;strong&gt;12.5/s&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="五几处刻意做对的判断"&gt;&lt;a href="#%e4%ba%94%e5%87%a0%e5%a4%84%e5%88%bb%e6%84%8f%e5%81%9a%e5%af%b9%e7%9a%84%e5%88%a4%e6%96%ad" class="header-anchor"&gt;&lt;/a&gt;五、几处&amp;quot;刻意做对&amp;quot;的判断&#10;&lt;/h2&gt;&lt;p&gt;这些地方很容易写成&amp;quot;看起来对、其实错&amp;quot;：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;stone-furnace&lt;/code&gt; 这类实体在蓝图里&lt;strong&gt;没有 &lt;code&gt;recipe&lt;/code&gt; 字段&lt;/strong&gt;，但 Factorio 也不把默认配方写进蓝图。所以必须报成&amp;quot;&lt;strong&gt;未知配方（可用 overrides 指定）&lt;/strong&gt;&amp;quot;，而不是当成&amp;quot;这台机器闲置&amp;quot;。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;信标&lt;/strong&gt;（59 个）持有模块但不执行配方，如果当机器算，每个信标都会变成一条假&amp;quot;未知配方&amp;quot;。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;实验室和采矿机&lt;/strong&gt;的产出取决于科技/矿脉，单独报，不归入&amp;quot;未知配方&amp;quot;。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&amp;ldquo;蓝图没给配方&amp;quot;和&amp;quot;蓝图给了但游戏数据里查不到&amp;quot;分开报&lt;/strong&gt;。后者我核实过 &lt;code&gt;mods&lt;/code&gt; 目录确实没装 mod，所以那批 mod 蓝图算不出来是&lt;strong&gt;如实缺口&lt;/strong&gt;，不是静默失败。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="六验证334-项断言--三个浏览器终检"&gt;&lt;a href="#%e5%85%ad%e9%aa%8c%e8%af%81334-%e9%a1%b9%e6%96%ad%e8%a8%80--%e4%b8%89%e4%b8%aa%e6%b5%8f%e8%a7%88%e5%99%a8%e7%bb%88%e6%a3%80" class="header-anchor"&gt;&lt;/a&gt;六、验证：334 项断言 + 三个浏览器终检&#10;&lt;/h2&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;测试文件&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;断言&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;在验证什么&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;test_blueprint&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;14&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;编码往返、格式事实、拒绝畸形输入&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;test_all_blueprints&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;8&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;全部 615 个蓝图&lt;/strong&gt;逐字符往返&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;test_gamedata&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;79&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;合成数据证明数值真来自文件&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;test_gamedata_independent&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;45&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;正则直接从 Lua 抠数值&lt;/strong&gt; + 物理独立推导&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;test_calc&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;90&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;精确数值、未知配方不静默丢弃、循环保护&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;test_integration&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;22&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;跨模块接线&lt;/strong&gt;：模块效应→计算、尺寸→渲染&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;test_view&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;76&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;页面无外链、嵌入数据条数等于输入&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;code&gt;test_gamedata_independent.py&lt;/code&gt; 是关键一条：它&lt;strong&gt;不经过提取器的 API&lt;/strong&gt;，而是用正则直接从原始 Lua 文件里抠数值比对——提取器的 bug 无法靠&amp;quot;自洽&amp;quot;藏住。&lt;/p&gt;&#10;&lt;p&gt;三个&lt;strong&gt;真浏览器终检&lt;/strong&gt;不是检查 HTML 文本，而是真的操作页面：缩放/平移真的改变渲染、点到饱和蓝图真的显示警告、放大后每实体像素数真的从 3.1 涨到 50,181.6。&lt;/p&gt;&#10;&lt;p&gt;这一层抓到了 &lt;strong&gt;5 个纯 Python 测试看不见的 bug&lt;/strong&gt;：相机 NaN、缺边界、图例按数字索引查却按名字存、&lt;code&gt;renderLegend()&lt;/code&gt; 重建时销毁了正在处理点击的节点、&lt;code&gt;data-i=&amp;quot;0&amp;quot;&lt;/code&gt; 的 falsy 边界。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;这就是为什么要用真浏览器驱动页面。DOM 里的字符串说&amp;quot;我很正常&amp;rdquo;，不算数。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;最终：&lt;strong&gt;334/334 通过，7/7 浏览器终检全过&lt;/strong&gt;，端到端 6.6 秒。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="七还没做到的"&gt;&lt;a href="#%e4%b8%83%e8%bf%98%e6%b2%a1%e5%81%9a%e5%88%b0%e7%9a%84" class="header-anchor"&gt;&lt;/a&gt;七、还没做到的&#10;&lt;/h2&gt;&lt;ol&gt;&#10;&lt;li&gt;&lt;strong&gt;信标效应没建模&lt;/strong&gt;——需要空间分析（每台机器周围有几个信标），目前只在 notes 里计数。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;传送带饱和是按物品总量、不是按段判定&lt;/strong&gt;——蓝图 JSON 不记录机器和传送带的连接关系，要按段判就得从坐标和朝向重建拓扑，这是独立的一大块。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;K2SE 的 mod 配方读不到&lt;/strong&gt;，因为这台机器没装 mod。装上、把 &lt;code&gt;data_root&lt;/code&gt; 指过去就能算。&lt;/li&gt;&#10;&lt;li&gt;品质加成、插入机吞吐、电力网络都还没建模。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;这些限制我都写在页面的结论里，&lt;strong&gt;不含糊过去&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p style="text-align:center;margin:2.5em 0"&gt;&#13;&#10;&lt;a href="https://cloudlake.eu.org/lab/factorio/" style="display:inline-block;padding:12px 26px;border-radius:8px;background:var(--accent-color);color:var(--accent-color-text);text-decoration:none;font-weight:600"&gt;打开蓝图平台（6 MB，含 615 张蓝图）→&lt;/a&gt;&#13;&#10;&lt;/p&gt;&#13;&#10;</description></item></channel></rss>