摘要

一场战斗里有左右两方,分别是红方和蓝方。状态估计(SE,把截图观察累积成对局状态和对手构筑推断的程序)需要知道“左边是什么颜色、右边是什么颜色”,才能把行动条上带颜色的部队卡片归到我方或敌方。

颜色有两个来源:一是屏幕顶部角落的玩家头像环(真人对局时有一个带计时器的圆形头像,外环是阵营色);二是 HUD(战场顶部显示双方英雄属性的条)上英雄旗帜的颜色。旧代码让头像环颜色逐帧覆盖,最后一帧说了算;英雄旗帜颜色只在头像环从未读到时才用。

一段和官方 AI 对战的录像(10 月 9 日 08:38)没有玩家头像,固定的头像框里只是偏红的战场背景,于是两侧都被读成红色,红方部队全部被记成敌方。本实验把 SE 的判定改为按“左右颜色配对”投票、要求明确多数,经过两轮外部审阅修正。修好后该局结算画面 14 行由 5 MISSING_END / 9 UNMATCHED 变为 11 MATCH / 1 MISSING_END / 2 UNMATCHED;另外三段录像结果逐字节不变。

要回答的问题

  1. 08:38 录像为什么把红方部队记成敌方?是视觉识别读错了还是 SE 用错了?
  2. 怎样决定左右颜色,才能既修好这一局,又不在其它情况下给出错色?

数据与来源

四段已存会话的最新 SE 请求(只读,没有改会话、没有读冻结数据):

会话 帧数
训练录像(10 月 10 日 16:57) 261
训练录像(10 月 10 日 01:39) 554
实时对局(10 月 14 日 23:58) 268
官方 AI 对局(10 月 9 日 08:38) 242;插回 3 张恢复的准备页后 245

起因是此前一个准备页修复实验:3 张准备页恢复后 SE 定出我方在左(红方),却把红方部队记成敌方。

方法

先查视觉识别是否读错

用 3 张原始截图裁出头像环框,按生产代码的红蓝像素统计重算:

截图 左环 红/蓝 像素 右环 红/蓝 像素
08:38 截图一 796 / 0 902 / 0
08:38 截图二 770 / 0 1153 / 0
08:38 截图三 698 / 0 1145 / 0
16:57 对照 704 / 0 4 / 620

目视顶部区域:08:38 两侧框里根本没有头像环,是红色调的战场;16:57 两侧各有带计时器的圆形头像。所以右侧读成红色不是颜色阈值错,而是这段录像里没有可读的对象。

视觉识别其实已经正确拒绝了:这些帧的头像状态全是“未知 / 战斗布局未确认”(它要求两环恰好一红一蓝才算确认),只是按合同仍把原始观察值保留下来。改视觉识别会改变所有会话的输入,不是安全的小修,所以没动。问题在 SE 把未确认布局下的观察当成了定论。

在 08:38 全部帧里,头像环两侧都读成红色;而 HUD 英雄旗帜颜色是左红 191 帧、右蓝 191 帧。旧代码用环覆盖后得到“左红、右红”,最后红色被归到敌方。

改动:按配对投票(只改 SE)

最终做法(第三轮):

  1. 配对票:一帧里两侧读成两种不同颜色,就为这个(左色, 右色)配对投一票,每帧一票。来源有两种,同权:HUD 上两个英雄都有颜色(有没有识别出英雄身份都算);头像环帧,且视觉识别确认了布局。
  2. 明确多数:票数最多的配对至少是其它配对票数总和的两倍才定;否则两侧都记为未知。
  3. 单侧读数(一帧只读到一侧颜色)只在完全没有配对票时才使用,同样要明确多数;两侧得出同一颜色则两侧都未知。单侧读数从不推翻已定的配对。
  4. 两侧同色的帧、布局未确认的环帧、出错的帧都不计票。
  5. 某侧最终未知、有计票读数和最终结果相反、或出现两侧同色帧时,在结果里写一条“阵营颜色冲突”说明,列出全部票数和原因。

颜色映射之后的用法(行动条部队、战场部队、减员时间线)不变;结果格式的字段不变。

为什么改了三轮

  • 第一轮:按来源优先级(有身份的 HUD → 环 → 无身份的 HUD)每侧取第一个有颜色的来源。外部审阅用合成场景指出:一帧有身份的 HUD 误读就能压过 40 帧确认的环;未确认的单侧环仍在计票;两侧同色时直接丢掉一侧,可能把红方当敌方。
  • 第二轮:每侧分两级各自按明确多数定。第二次审阅又指出:10 帧 HUD 左红右蓝 + 40 帧只读到右旗且读成红色时,右侧红色 40:10 胜出,两侧同红,又把我方红军当成敌方——而旧代码在这个场景反而是对的;另外 191 帧无身份 HUD 对 3 帧颜色互换的确认环时会判错;某侧唯一证据是未确认单侧环时被置未知却没有说明。
  • 第三轮:改成按配对投票,即上面的做法。四段真实录像里没有一帧是无身份的 HUD 英雄,没有数据支持给它不同的权重,所以同权。

设置

改前用当前提交导出的 SE 源码,改后用修改后的源码,同一份输入、同一环境,限 4 核运行;比较完整 SE 结果。合成场景脚本分别用旧代码、第二轮和第三轮源码运行。

结果

真实会话

会话 改前 vs 第三轮
16:57 文件逐字节相同
01:39 文件逐字节相同
23:58 文件逐字节相同
08:38 原请求(无准备页) 只多 1 条冲突说明;我方所在侧仍未知
08:38 插回 3 页 1558 个路径不同,全部来自颜色映射

三段一致的会话里,环颜色和 HUD 颜色从不矛盾,所以映射不变、也不加说明。

08:38 插回 3 页后:

改前 改后
颜色映射 左红、右红 左红、右蓝(配对红/蓝 191 票,全部来自有身份的 HUD)
敌方行动条部队 9 种,全是红方 7 种,全是蓝方
减员时间线 我方 8 堆、敌方 18 堆 我方 16 堆、敌方 16 堆
结算画面 14 行 5 MISSING_END、9 UNMATCHED 11 MATCH、1 MISSING_END、2 UNMATCHED

(结算画面是战斗结束时列出双方部队开局与剩余数量的界面;MATCH 指 SE 的部队与结算行对上,MISSING_END 指缺结尾数量,UNMATCHED 指对不上。)剩下 2 行 UNMATCHED 是两种开局都是 52 的兵种按数量无法唯一对应,属于预期的保守;1 行 MISSING_END 是敌方刺客 95。之前准备页实验报告里写的“12 MATCH”是笔误,诊断输出实为 11 MATCH。

合成场景(部分)

每个场景的真相都是左红右蓝。“对”= 敌方部队为蓝方;“未知”= 敌方颜色未知、部队为空;“错”= 把红方当敌方。

场景 旧代码 第二轮 第三轮
无身份 HUD 左红右蓝 + 右侧单环背景红 错 对 对
同一兵种两种颜色 错 对 对
40 帧确认环 + 1 帧单侧 HUD 左蓝 对 对 对
10 帧 HUD 左红右蓝 + 40 帧只右旗红 对 错 对
191 帧无身份 HUD + 3 帧确认环互换 错 错 对
两局颜色互换 3+5 / 5+3 帧 对 未知 未知
3 帧只左红 + 40 帧只右红 错 错 未知
191 帧左红右蓝 + 100 帧确认环互换 错 未知 未知
5 帧带红蓝观察的出错帧 对 未知 未知

完整场景表(22 行、23 个场景,含上表未列的)里第三轮都不给出错色。相对旧代码由“对”变“未知”的场景,要么是旧代码碰巧取对(例如取了先见的那一局),要么只有未确认或出错的环读数,要么票数够不上明确多数(如 8:5);它们都带说明。

软件测试

轮次 结果
第一轮 新文件 8/8;相关 99 通过;扩大 1024 通过、12 跳过;App 重算 15 通过
第二轮 新文件 17/17;相关 85 通过;扩大 1043 通过、12 跳过
第三轮 新文件 21/21;扩大(50 个 SE 测试文件 + 1 个集成文件)1047 通过、12 跳过

12 个跳过是本机没有符号链接权限。第一轮有 3 个 App 测试在独立工作副本里因找不到运行环境失败,与改动无关,未计入。

失败与负结果

  • 第一、二轮的做法都被审阅用合成场景推翻,第二轮甚至在两个场景里比旧代码更差;两轮记录都保留。
  • 08:38 插回准备页后,有 21 个路径的差异来自一个限时 0.5 秒的英雄升级组合搜索没搜完,属于计时相关,与本改动无关。

局限

  • “至少两倍”的明确多数阈值是人为选择。同一请求里真的混了两局且颜色互换时,两侧都会变未知,比猜一边保守。
  • 如果 HUD 旗帜颜色本身系统性地读反,而确认的环帧又不到 HUD 帧的一半,本改动会跟着错;目前四段录像的 HUD 颜色全部一致。
  • 只靠未确认环或出错帧才有颜色的一侧现在是未知;真实会话里每段都有 191–442 帧的 HUD 配对,不受影响。
  • 冲突说明只在结果数据里,App 界面目前不显示。
  • 已存会话不追溯;需在 App 里“仅重算 SE”(08:38 还需先重读那 3 张准备页)。
  • 08:38 的对手类型仍被判成“玩家”(日志里敌方名读成“英雄”),属日志名读取问题,未处理。

结论与下一步

阵营颜色现在由左右配对的多数票决定,没有玩家头像的录像不再把背景色当成阵营色;三段真实录像结果不变,官方 AI 对局的部队归属和结算对照得到恢复。后续可以考虑在 App 的推断说明里展示颜色冲突说明,以及修正官方 AI 对局的对手类型识别。