16 KiB
机关门语义与数据集清洗修正文档
背景
当前数据集清洗已经能处理连续门团、连续怪团、无用分支和一部分闲置分支,但还有一类常见且重要的结构没有被正确表达:机关门驱动的战斗房间。
这类结构的共同点是:
- 地图中存在机关门。
- 门前或门附近会摆放一组怪物。
- 这些怪物在局部上未必直接守护资源,也未必在拓扑上形成“打掉以后暴露新区域”的普通分支。
- 但它们仍然有明确设计意义,因为它们承担了“开机关门”的推进语义。
一个典型例子可以概括为:上方是一扇机关门,下方是一个小房间,房间里放若干怪物和空地。若只按当前“邻居数”或“后侧资源收益”来判断,这些怪物很容易被视为闲置怪;但从实际玩法上看,它们显然不是噪声结构。
因此,这个问题本质上不是“过滤阈值太松或太紧”,而是当前标签空间里没有把机关门作为独立语义类保留下来,导致后续清洗算法看不到这层结构信息。
问题概述
当前异常现象
在当前规则下,存在一批地图会因为“闲置怪”而被过滤,但人工复核时会发现这些怪物实际上属于机关门房间的一部分。它们看起来像是:
- 站在一片空地边缘。
- 不直接守护资源。
- 拓扑上可能只连接到一个邻接节点。
- 但所在区域同时存在机关门,击败这些怪物本身就是推进条件的一部分。
也就是说,这些怪物虽然满足了“局部无影响分支”的几何特征,却不满足“没有设计意义”这一真正的清洗目标。
误判根因
这个问题目前至少有三层原因:
- 标签层压扁:机关门在原始塔数据中可被识别,但在当前主配置里没有变成独立 tile,而是与普通门共用同一标签。
- 拓扑层压扁:拓扑图只区分
Door和Enemy两种分支,不区分普通门和机关门。 - 过滤层失语义:闲置怪、重复守卫、无用分支等规则只能看到“门/怪/空地/资源”的局部拓扑,看不到“这个怪物与机关门联动”的语义。
只要这三层没有拆开,即使继续微调 allowIdleBranch 一类阈值,也很难稳定解决问题。
当前实现现状
转换层已经能识别机关门,但没有保留独立标签
当前实现中,原始塔数据里的 specialDoor 已经能在转换阶段被识别出来;这一点说明问题不是“完全不知道机关门存在”,而是识别到了,但在标签化输出时被折叠掉了。
现状可以概括为:
- 转换阶段会把原始
specialDoor映射到tiles.specialDoors[0]。 - 但当前主配置里,
specialDoors与commonDoors实际共用了同一个标签值。 - 共享常量里,
specialDoorTiles也与普通门使用同一个集合值。
这带来两个直接后果:
- 训练数据里的地图矩阵无法区分“普通门”和“机关门”。
- 当前的
specialDoorCount统计并不是真正意义上的“机关门数量”,而只是复用了普通门的标签统计,观测值没有独立语义。
换句话说,当前代码里虽然存在 specialDoors 这一字段,但它还没有真正成为独立的数据集类别。
拓扑层只有 Door/Enemy 两类分支
当前拓扑图中的分支类型只有两种:
DoorEnemy
这意味着:
- 连续门检测会把普通门和机关门都看成同一类门分支。
- 闲置门检测也无法区分普通门和机关门。
- 更重要的是,怪物侧根本无法知道“我所在的这个房间是否与机关门有关”。
对于本问题来说,真正缺失的并不是“门是不是门”,而是“这个门是不是机关门”。
当前闲置筛选为什么会误杀
现有闲置规则里,最敏感的是这两类:
neighbors.size === 1的闲置分支规则。- 基于后侧收益缺失的无用分支规则。
它们的问题并不是逻辑错误,而是默认假设了:
- 分支的价值主要来自局部守护资源。
- 分支的价值主要来自是否暴露新区域。
但机关门关联怪的价值来自第三种来源:全局或半全局的开门触发语义。如果标签空间里没有 Special Door,这类语义就无法进入筛选逻辑,于是就会被误判成“看起来没用的怪”。
目标
本轮文档希望固定以下目标:
- 在数据集标签层中,为机关门新增一个独立 tile 类别
Special Door。 - 在拓扑和清洗阶段,能够识别“机关门关联怪”,避免其被简单当作闲置怪过滤。
- 在不大幅重写现有拓扑框架的前提下,尽量复用已有的区域合并、邻接和入口连通逻辑。
- 让过滤结果继续保持可解释,至少能回答“为什么这个怪没有再被判成闲置怪”。
- 第一版实现优先保守,宁可少杀一些,也不要把常见机关门房间结构大量误删。
非目标与边界
本轮文档暂时不做以下承诺:
- 不要求第一版就精确恢复脚本级联动关系。也就是说,暂不要求从原始事件脚本中严格求出“哪一只怪控制哪一扇机关门”。
- 不要求立即重写所有门/怪规则。本次重点是修正与机关门相关的误杀,而不是重构整套清洗体系。
- 不要求在本轮文档里完成模型改造细节。但需要明确指出:新增 tile 会影响训练数据词表和 mask token 编号。
- 不改变机关门在大类上的门属性。例如门密度、门类分支连通块等统计,默认仍然把机关门视为门的一种。
设计一:在标签层新增 Special Door
设计原则
这里需要强调一个重要约束:当前训练数据里 MASK_ID = 6,因此不能直接把新语义无脑塞到现有编号上而不处理 mask token。
第一版比较稳妥的编号方案是:
| 语义 | 当前主要编号 | 建议编号 |
|---|---|---|
| Empty | 0 | 0 |
| Wall | 1 | 1 |
| Common Door | 2 | 2 |
| Resource | 3 | 3 |
| Enemy | 4 | 4 |
| Entry | 5 | 5 |
| Special Door | 无独立编号 | 6 |
| Mask | 6 | 7 |
这个方案的优点是:
- 现有 0 到 5 的主语义尽量不动。
- 只为
Special Door新增一个实际 tile。 MASK_ID顺延到 7,语义清晰,不与真实地图 tile 混用。
对数据预处理侧的影响
新增独立标签后,数据预处理侧至少需要满足:
commonDoorTiles与specialDoorTiles不再共享同一标签值。doorTiles仍然是二者并集,用于总门密度等大类统计。specialDoorCount才能真正表示机关门数量,而不是“普通门标签的重复统计”。- 主配置中的
specialDoors需要改成独立编号,而不是继续与普通门共用标签。
对训练侧的影响
虽然本轮不直接修改训练代码,但文档必须明确:新增 Special Door 之后,训练侧至少要同步处理以下问题:
- 数据集的 tile 词表大小会加 1。
MASK_ID需要后移。- Stage 1 / Stage 2 / Stage 3 的退化逻辑需要把
Special Door视作门类的一部分处理。 - 统计目标里的门密度若仍按“大类门”处理,则需要把普通门和机关门一起计入。
也就是说,Special Door 虽然是一个新 tile,但它在训练目标上仍然应属于“门超类”中的细分子类,而不是完全独立的结构域。
设计二:在拓扑层保留机关门子语义
为什么不能只改 tile,不改拓扑
如果只是把地图矩阵里的机关门编号拆出来,但拓扑层仍然把它和普通门都压成同一个 Door 分支,那么清洗算法依然无法利用这条信息。
因此,除了标签层扩展以外,拓扑层也需要补上“门子类型”这一维语义。
建议的数据结构方向
当前更合适的方向,不是把 BranchType 直接从两类扩成三类,而是保留现有大类,再补一个门子类型字段。原因是:
- 现有很多逻辑只关心“门 vs 怪”,并不希望被额外分支复杂化。
- 连续门团、门密度等统计,仍然需要把普通门和机关门归到门大类里。
- 但闲置修正又确实需要知道“这个门是不是机关门”。
因此更推荐的方向是:
branch仍然保持Door | Enemy。- 对门分支额外补一个
doorKind = Common | Special。
这样可以同时满足:
- 老规则不需要大改。
- 新规则在需要的时候可以单独读取机关门语义。
机关门在拓扑统计中的默认归属
当前版本建议固定以下口径:
- 大类统计:机关门计入门密度、门连通块、门热力图。
- 子类统计:另外单独记录机关门数量,必要时可追加机关门密度。
- 异常清洗:仅在与机关门关联怪的识别相关时,读取
doorKind = Special这一子语义。
这意味着:新增 Special Door 不是为了把机关门彻底从“门”里面剥出去,而是为了给清洗逻辑一个足够可靠的判别信号。
设计三:为“机关门关联怪”增加保守豁免
问题本质
需要修正的并不是“所有机关门附近的怪都要保留”,而是:
如果某只怪所在的局部区域明显属于机关门房间的一部分,那么它不能仅仅因为
neighbors.size === 1或“背后无资源”就被直接视为闲置怪或无用怪。
换句话说,当前需要补的是一条保守豁免规则,而不是另一条更强的删除规则。
为什么第一版不建议直接解析脚本联动
理论上,最精确的做法是从原始塔事件里直接解析:
- 哪扇机关门存在。
- 哪些怪物被击败后会触发开门。
- 一只怪是否同时参与多个门的触发。
但这个方案的工程代价很高,而且不同塔版本、脚本写法、自定义事件逻辑都可能造成兼容成本。因此,第一版不建议把问题拉到脚本层求精确真值,而是先使用当前拓扑图已经具备的信息,做一个偏保守的结构性修正。
建议主规则:基于“机关门关联区域”的豁免
当前版本更合适的主规则是:
- 先沿用现有的非分支区域合并逻辑,把
Empty与Resource节点合并成更大的可进入区域。 - 若某个合并区域与至少一个
Special Door分支相邻,则把这个区域记为“机关门关联区域”。 - 若某个怪物分支与某个机关门关联区域相邻,则把该怪记为“机关门关联怪候选”。
- 对机关门关联怪候选,不再仅凭
neighbors.size === 1就直接计入idleEnemyBranchCount。 - 对机关门关联怪候选,也不再仅凭“局部无资源收益”就直接计入无用分支命中。
这条规则的核心思想是:
- 当前并不试图证明“这只怪一定会开门”。
- 当前只要求证明“这只怪所在区域与机关门强相关”。
- 一旦存在这种强相关,第一版就保守地不把它当作典型闲置怪删除。
为什么这个规则适合作为第一版
这条规则有几个优点:
- 它只依赖现有拓扑图与门子类型,不依赖脚本解析。
- 它可以复用当前重复守卫规则里已经存在的“合并非分支区域”思路。
- 它解释性很强,可以直接回答“这只怪因为与机关门处在同一关联区域,所以没有被判成闲置怪”。
- 它天然偏保守,符合当前“宁可少杀,不要误杀重要结构”的工程目标。
已知代价
这条豁免规则也有明确代价:
- 它可能会保留少量“恰好和机关门在同一区域、但其实也不太重要”的怪物。
- 它并不能恢复真正的脚本级触发关系,只是利用局部结构做代理。
但当前版本更需要避免的是高频、显眼、结构性明确的误杀。与之相比,这种偏保守的漏删代价是可以接受的。
对现有筛选规则的具体影响
规则一:idleEnemyBranchCount
这是当前最需要优先修正的规则。
建议口径:
- 怪物若命中
neighbors.size === 1,先作为普通闲置怪候选。 - 再检查它是否属于机关门关联怪。
- 若是,则不计入
idleEnemyBranchCount。 - 若不是,则维持原判。
规则二:hasUselessBranch
这条规则也可能误伤机关门关联怪,因为这类怪物往往不守局部资源。
建议口径:
- 对普通分支,仍按现有“删除该分支后,后侧是否失去入口连通且没有资源收益”的规则处理。
- 对机关门关联怪,第一版默认不参与这条规则的硬过滤。
原因是:机关门关联怪的价值本来就未必体现为“守护后侧资源”,继续套用这条规则容易再次把它们误杀。
规则三:hasRepeatedGuardIdleBranch
这条规则的风险相对次要,但仍需要关注。因为一个机关门房间里可能存在多个同类怪,它们可能被错误理解成“重复守同一连通区域”。
当前建议是:
- 第一版先增加观测统计,不急着强行改判。
- 如果抽样发现机关门房间经常命中这条规则,再补“机关门关联怪不参与 repeated guard 统计”这一豁免。
也就是说,这条规则暂时列为重点观察项,不一定与 idleEnemyBranchCount 同步收紧。
规则四:连续门团 / 连续怪团
这两条规则当前不建议因机关门而修改。
原因是:
- 机关门仍然是门的一种,继续计入门类连通块是合理的。
- 机关门关联怪即使有特殊语义,也不意味着它们应当不受连续怪团规则约束。
换句话说,当前修的是“局部价值误判”,不是“所有机关门怪都特殊”。
统计与可观测性要求
为了让后续人工复核和规则调整有抓手,新增机关门语义后,至少应补以下统计:
specialDoorCount:真正意义上的机关门数量。specialDoorFloorCount:包含机关门的楼层数量。specialDoorLinkedEnemyCount:被识别为机关门关联怪的数量。ignoredIdleEnemyBySpecialDoorCount:原本会命中闲置怪,但因机关门关联而被豁免的数量。ignoredUselessBranchBySpecialDoorCount:原本会命中无用分支,但因机关门关联而被豁免的数量。
当前 filtered 数据集与图片标注体系也应支持这一点,至少能在抽样时回答:
- 某张图为什么被保留。
- 某张图为什么仍被过滤。
- 如果某个闲置怪没有被过滤,是不是因为它属于机关门关联怪。
验收方向
本问题后续落地时,至少应满足以下验收方向:
- 数据集标签中,机关门拥有独立 tile 编号,不再与普通门共用同一个语义值。
specialDoorCount能真正反映机关门数量,而不是普通门的重复统计。- 典型机关门房间中的怪物,不会再仅因
neighbors.size === 1被直接判成闲置怪。 - 对包含机关门的地图进行抽样复核时,能明显看到误杀减少。
- 连续门团、连续怪团等其他清洗规则在大方向上保持不变,不因为这次修正而整体失真。
当前建议的实施顺序
从工程风险看,当前更稳妥的顺序是:
- 先把
Special Door从标签层独立出来。 - 再在拓扑层补上门子类型。
- 优先修正
idleEnemyBranchCount的误判。 - 然后观察
hasUselessBranch和hasRepeatedGuardIdleBranch的命中样本,决定是否追加豁免。
这个顺序的原因是:如果连独立 tile 都没有,后面的筛选修正只能停留在模糊的局部几何启发式上,无法稳定描述“机关门关联怪”这一目标对象。
本文档暂不回答的问题
以下问题在当前版本里先不定稿,留待后续实现与抽样验证后再讨论:
- 是否有必要进一步解析原始脚本,恢复精确的“怪 -> 机关门”触发关系。
- 机关门关联怪是否应该影响资源密度、怪物密度等统计解释方式。
- 是否需要为“机关门房间”本身增加一个独立结构标签,供后续训练条件使用。
- 机关门是否需要从普通门的连续门团统计中拆出去单独观测。
当前文档只固定一件事:机关门必须先成为独立语义类,闲置筛选才有可能在这个问题上做对。