摘要

SE(状态估计)负责把一局中一次次截图读取的观察累积起来,得出“到某个时刻为止”我们知道什么:某个字段当前读到的值、冲突、未知,以及玩家手动输入和撤销。原有的公开接口一次最多处理 64 次读取(read),第 65 次请求会整体拒绝。一局真实对战很容易超过这个数。

本实验实现了一个私有的纯函数(下称“历史累积函数”):输入是整份按时间记录的观察日志(journal)和一个截止时刻,输出是截止时刻为止的部分观察视图,外加一份“实际处理了哪些记录、哪些属于未来被排除”的账本。它不读媒体、不保存会话、不跨调用持有状态。按事先冻结的协议,在 2/64/65 条读取 × 六个截止时刻上运行,21 项全部通过。公共接口、命令行和战斗记录 App 都没有接入。

要回答的问题

  • 新函数能否在超过 64 次读取时正确累积,并保留未知、冲突、撤销和未来记录,而不是截断或丢弃?
  • 在 64 次以内,结果是否与旧接口完全一致?
  • 超出容量时能否整体拒绝、不返回截断的“成功”结果?成本是多少?

数据与来源

  • 全部输入是软件构造的观察日志,复用已有的测试数据(fixture),不是新的真实游戏来源;没有读图片、视频或做 OCR。
  • 每份日志包含:第 1 次读取有值、后续读数里“法强”为未知;两个用户手动输入的攻击值(0 和 2,互相冲突);同一张图上两次手动设置防御(77 和 78)以及随后两次撤销。
  • 64 和 65 条的日志里,倒数第六到倒数第二次读取换成另外五类原生读数(战场、生物面板、法术书、攻击预览、日志),使六类读数都实际经过原校验;书页的归属是明确的软件假设。最后一条是同一张准备页的“未知”重读。

方法

  1. 先冻结:把完整输入、独立写下的预期结果和容量反例保存到新目录;确认存在后才运行。运行阶段不重新生成输入,也不根据实际结果改预期;第一个意外结果就停止。(首次冻结后、执行前核对上下文规则,调整了五类读数的位置,另存为最终冻结目录,首次目录保留未执行。)
  2. 函数做什么:先验证全部输入的原生格式、身份、引用和生命周期,再按每条记录的接受时间选出“截止时刻之前、因果闭合”的前缀,用既有的同一个累积逻辑处理整个前缀。未来记录完整保留在请求和“已排除”账本中,但不进入早期视图。
  3. 账本完整性:每条记录在被实际处理时登记;只有整个前缀投影成功、且访问账本无遗漏无重复,才返回“完整”。
  4. 严格读取器:另写一个私有读取器,独立计算应有的前缀集合和顺序,检查外层结构、账本、嵌套字段和预算,再重算视图核对。它与函数共享同一个累积逻辑,所以是严格一致性检查,不是独立算法;独立预期值和旧接口对照补充这一限制。

设置

每个向量串行调用一次,在 Python 内存追踪(tracemalloc)开启下计时;不报告 p95、进程常驻内存或冷/热对比。

结果

21/21 通过:18 个语义向量(3 种长度 × 6 个截止时刻)全部符合预期并通过严格读取器;其中 13 个 ≤64 条的前缀与旧公开接口的完整视图相等(除版本名和耗时),5 个完整 65 条前缀被旧接口按预期拒绝;3 个容量反例都整体拒绝。所有请求在调用后保持不变。

65 条日志在六个截止时刻的结果:

截止时刻 防御值 法强 实际处理记录 / 未来记录
12:00 14 27 1 / 71
12:01 14 未知 68 / 4
12:02 77 未知 69 / 3
12:03 冲突 未知 70 / 2
12:04 77 未知 71 / 1
12:05 14 未知 72 / 0

后五个时刻,用户攻击值 0/2 保持“冲突”,不补值。冲突时刻准备页组的选定项为空、保留 60 张备选;撤销后选回原读数,59 张备选仍保留。

容量反例:

反例 请求编码字节 结果
65 个同时含准备页和书页的路由读数 817,708 超出“归一化字段”容量,整体拒绝
257 个同字段用户手动根 85,572 超出“证据”容量,整体拒绝
65 个合法但填充过的单条原始读数 17,623,876 超出请求字节上限,整体拒绝

成本(最后一个截止时刻的单次调用):

读取数 输入 / 输出编码字节 函数秒 / 追踪峰值字节 读取器秒 / 追踪峰值字节
2 4,070 / 53,060 0.01919 / 464,168 0.02988 / 484,344
64 103,642 / 1,540,106 0.58282 / 14,297,254 0.99322 / 14,436,242
65 104,737 / 1,566,409 0.59775 / 14,546,814 1.00790 / 14,639,530

失败与负结果

  • 首次私有测试 30 通过、1 失败:错误的测试数据把第 2 条读数改成“不支持”却仍让手动设置引用它,原规则正确地拒绝了这个不存在的页面。改为追加一条无引用的独立拒识读数,失败记录保留。正式 21 项实验没有失败。
  • 一个注入反例(让登记回调漏掉一条撤销记录)被正确拒绝,说明不能靠简单回显输入 ID 掩盖遗漏。

局限

  • 输入是软件构造的旧数据,不是新的真实来源,也不证明游戏规则或识别精度。
  • 65 条成功不证明 128/256 条有值页面或整局可用;字节上限的拒绝过程中仍可能有完整 JSON 编码的临时副本,不能把 16 MiB 报文上限当作 16 MiB 内存上限。
  • 新私有回归 39 项和受影响旧回归 531 项通过(合计 570 项软件检查),没有跑完整 SE 测试。
  • App 侧的新保存版本、修订数和缓存上限、关键帧按需读取、恢复与界面代次等都待联合决定,本批没有修改 App 或其保存格式。

结论与下一步

纯函数路线在有界范围内可行:它能跨过 64 条的限制,同时完整保留未知、冲突、撤销与未来记录,并在超出容量时整体拒绝。下一步是评审一份公共接口的精确提案(唯一入口、版本、字段域、嵌套预算、新旧兼容矩阵),在获得批准和验收之前不导出、不接入 App。