feat(迭代35卡②T1): 加载校验换代——LevelTable.validate_graph(nodes) 静态单点承载四检+良构基底(①入口≥1 且终点≥1 ②全员从某入口可达且可达某终点 ③终点恒 Boss 且 Boss 无出边 ④出边目标深度严格大于源=无环;良构=句柄唯一·出边全命中·type 五类枚举·battle 带深度),表加载期图校验与旧六判据(Boss 恰一/战斗≥2/单链末 Boss/前三位纯战斗/出度≤2/Boss 编号最大)连同模板池校验分支、BFS 层宽≤5 一并退役,行级校验保留
- 新宿主 tests/unit/core/level_table_validate_test.gd〔v1〕15 条(正例三形:多入口多终点/含岔口单终点/单链退化显式出边;负例八类;范围反证三条:无商店图不报错·双入口合法·缺 next 字段仍拒)+run_all manifest 登记一行(59 套件全绿) - 判据②与④在 DAG 上互斥的实证:无环图每个源点必为入口、每条极长路必止于终点,故「孤儿/断头」构造不出独立负例——② 保留为 ④ 失效时的纵深防御面,测试以两独立链正证不误伤(该关系拟入反哺点,文档判据②的表述宜注明其兜底定位) - 旧断言随动四条(声明,非静默放宽):level_shop_economy ③ 段整段改走 validate_graph 并把「入口唯一违例」翻为合法正例、「孤儿」改为非 Boss 终点;level_map_type_vis 夹具注释订正;round_flow_skill_pool 敌表池清单 20→28 行补齐覆盖批八行池克隆键;board_gen_reward「(2,3) ceil 上取」改判精确命中并以临时摘同行的夹具面保留 ceil 能力判据(覆盖批补齐键域后旧判据对象消亡) - 件核:全仓 grep 六判据消息文本零残留(唯一余留在 level.tres 表尾模板生成史注释,随 T3 模板清出一并消失)
This commit is contained in:
1 parent
6a8bde9a8e
commit
c0dd442060
9 files changed
+249
-197
No files matched your search
@@ -54,6 +54,7 @@ func _suites() -> Array:
|
||||
{"path": "res://tests/unit/core/treasure_items_test.gd", "expect": 0}, # 秘宝结构冻结断言面
|
||||
{"path": "res://tests/unit/core/node_map_generator_test.gd", "expect": 0}, # 节点图生成器产出合格/可复现/岔路取舍/恰一店/归属锁定/战斗保底/出口结构/RNG 纯流面(卡① V1·V3·V5–V7·V9·V10)
|
||||
{"path": "res://tests/unit/core/node_map_growth_test.gd", "expect": 0}, # 生成图生长递增+参数无解 fail-fast 子进程通道(卡① V8/V4)
|
||||
{"path": "res://tests/unit/core/level_table_validate_test.gd", "expect": 0}, # 消费侧加载校验四检+良构基底正负向(卡② T1/V1〔v1〕)
|
||||
{"path": "res://tests/unit/core/breakpoint_resume_test.gd", "expect": 0}, # 断点往返/续玩确定性/幂等/退出清断点
|
||||
{"path": "res://tests/unit/core/curio_refresh_paid_test.gd", "expect": 0}, # 三选一刷新免费双源+GP 付费档(对齐商店 MC-1:价曲线/优先序/停点清零/钱包单口)
|
||||
{"path": "res://tests/unit/core/level_map_type_vis_test.gd", "expect": 0}, # 信息迷雾——类型可见性(层距 N+Boss/候选豁免+单圈+N 读表+续玩同值)
|
||||
|
||||
@@ -596,6 +596,16 @@ func _smoke_skill_pool_data() -> void:
|
||||
"3_battle_6_hook_horror": {"attack": 6, "bite": 2, "charge": 1},
|
||||
"3_battle_7_duergar": {"attack": 6, "charge": 2, "heavy": 2, "shield_bash": 2},
|
||||
"3_boss_9_duergar_king": {"attack": 6, "heavy": 2, "earth_pulse": 2, "rockslide": 2},
|
||||
# 迭代 35 生成图深度扩面覆盖批八行=同关同 row_kind 顶档行的池克隆(零新池、零新技能引用):
|
||||
# 战斗带顶 L1→6/L2→9/L3→13;Boss 按终点深度分带 L1→6、L2→8、L3→11/12/13
|
||||
"1_battle_6_bugbear": {"attack": 6, "charge": 2, "heavy": 2},
|
||||
"1_boss_6_ogre_chief": {"attack": 6, "heavy": 2, "harden": 2, "quake_smash": 2},
|
||||
"2_battle_9_giant_spider": {"attack": 6, "bite": 2, "charge": 1, "venom_spit": 2},
|
||||
"2_boss_8_drider": {"attack": 6, "heavy": 2, "venom_burst": 2, "web_snare": 2, "venom_curb": 2},
|
||||
"3_battle_13_duergar": {"attack": 6, "charge": 2, "heavy": 2, "shield_bash": 2},
|
||||
"3_boss_11_duergar_king": {"attack": 6, "heavy": 2, "earth_pulse": 2, "rockslide": 2},
|
||||
"3_boss_12_duergar_king": {"attack": 6, "heavy": 2, "earth_pulse": 2, "rockslide": 2},
|
||||
"3_boss_13_duergar_king": {"attack": 6, "heavy": 2, "earth_pulse": 2, "rockslide": 2},
|
||||
}
|
||||
var pools_ok := true
|
||||
for row in _db.enemy.rows:
|
||||
@@ -608,7 +618,7 @@ func _smoke_skill_pool_data() -> void:
|
||||
got[String(entry.skill_id)] = int(entry.weight)
|
||||
if got != expect_pools[rk]:
|
||||
pools_ok = false
|
||||
Kit.check("二十行九种池模板 = 建议池行值((L,row_kind,depth_band,enemy_id) 四元键逐行精确比对;L1 地精 9/1 · 熊地精 6/2/2 · 酋长 6/2/2;L2 熊地精 6/1/2 · 蜘蛛 6/2/1+毒吐2〔首遇带不投——迭代 24 N2〕 · 重击手 5/2/2 · 蛛化卓尔精灵 6/2/2/2〔Boss〕;L3 灰矮人 6/2/2+盾击2 · 钩爪恐魔 6/2/1 · 重装手 5/2/2+盾击2 · 石君王 6/2/2〔earth_pulse〕——全部 provisional;三 Boss 池尾 +1 门控条目〔quake_smash/venom_curb/rockslide 权重 2+unlock_hp_pct 50——迭代14④ T5〕;混编行集同带多行各一池合法——首批投放 (2,battle,5) 双行〔迭代 18 卡⑤:重击手+巢群〕,其余带键仍恰一行)", pools_ok)
|
||||
Kit.check("二十八行九种池模板 = 建议池行值((L,row_kind,depth_band,enemy_id) 四元键逐行精确比对;L1 地精 9/1 · 熊地精 6/2/2 · 酋长 6/2/2;L2 熊地精 6/1/2 · 蜘蛛 6/2/1+毒吐2〔首遇带不投——迭代 24 N2〕 · 重击手 5/2/2 · 蛛化卓尔精灵 6/2/2/2〔Boss〕;L3 灰矮人 6/2/2+盾击2 · 钩爪恐魔 6/2/1 · 重装手 5/2/2+盾击2 · 石君王 6/2/2〔earth_pulse〕——全部 provisional;三 Boss 池尾 +1 门控条目〔quake_smash/venom_curb/rockslide 权重 2+unlock_hp_pct 50——迭代14④ T5〕;混编行集同带多行各一池合法——首批投放 (2,battle,5) 双行〔迭代 18 卡⑤:重击手+巢群〕;迭代 35 覆盖批八行为顶档池克隆,逐行同检)", pools_ok)
|
||||
var basic_count_ok := true
|
||||
for row in _db.enemy.rows:
|
||||
var n := 0
|
||||
|
||||
@@ -134,9 +134,24 @@ func _smoke_board_generator() -> void:
|
||||
var row = _db.resolve_board_gen(L, 0)
|
||||
Kit.check("BoardGen 行解析 (%d,0)" % L, not row.is_empty())
|
||||
if L == 2:
|
||||
# R1 ceil 口径:无精确行向上取整保证偏难——L2 深度 3 为恢复点无行,解析到深度 4
|
||||
# 迭代 35 生成图深度扩面覆盖批随动(声明):旧口径「(2,3) 无精确行→ceil 上取到 4」的判据对象
|
||||
# 是模板时代键域缺口;覆盖批补齐 0–13 后 (2,3) 有精确行,生产表上不再存在可走 ceil 的空位。
|
||||
# 判据不丢——精确命中改判在册,ceil 能力用「临时摘除该行」的夹具面保留,随后原样还原。
|
||||
var exact_row: Dictionary = _db.resolve_board_gen(2, 3)
|
||||
Kit.check("BoardGen 精确命中 (2,3)(覆盖批键域补齐)", int(exact_row.get("node_depth", -1)) == 3)
|
||||
var gap_idx := -1
|
||||
for i in _db.board_gen.rows.size():
|
||||
if int(_db.board_gen.rows[i].get("level_index", -1)) == 2 and int(_db.board_gen.rows[i].get("node_depth", -1)) == 3:
|
||||
gap_idx = i
|
||||
break
|
||||
var saved: Dictionary = _db.board_gen.rows[gap_idx]
|
||||
var rows_before: int = _db.board_gen.rows.size()
|
||||
_db.board_gen.rows.remove_at(gap_idx)
|
||||
var ceil_row: Dictionary = _db.resolve_board_gen(2, 3)
|
||||
Kit.check("R1 ceil 解析 (2,3)→(2,4)(向上取整偏难)", int(ceil_row.get("node_depth", -1)) == 4)
|
||||
Kit.check("R1 ceil 能力不失(夹具摘除 (2,3)→向上取整到 4·偏难口径)", int(ceil_row.get("node_depth", -1)) == 4)
|
||||
_db.board_gen.rows.insert(gap_idx, saved)
|
||||
Kit.check("夹具表原样还原(行数与解析回到覆盖批现值)",
|
||||
_db.board_gen.rows.size() == rows_before and int(_db.resolve_board_gen(2, 3).get("node_depth", -1)) == 3)
|
||||
var b1 = board_generator.generate(L, 0, _rng.open_stream("board_gen"), _db)
|
||||
Kit.check("L%d 生成产物非空" % L, b1 != null)
|
||||
if b1 == null:
|
||||
|
||||
@@ -57,7 +57,7 @@ func _chain_nodes() -> Array:
|
||||
]
|
||||
|
||||
|
||||
# validate 正样本(无跨层跳边的合法图:入口唯一、Boss 编号最大收尾、层宽 ≤5)
|
||||
# 表行夹具的正样本(无跨层跳边的合法图——表加载期只核行级字段,图合法性判据见 validate_graph·卡② T1 换代)
|
||||
func _valid_graph_nodes() -> Array:
|
||||
return [
|
||||
{"node_depth": 0, "type": "battle", "next": [1, 2]},
|
||||
|
||||
@@ -147,8 +147,8 @@ func _battle_st(policy: String) -> Dictionary:
|
||||
func _smoke_level_graph() -> void:
|
||||
print("--- 关卡节点地图·分支节点图(L2 幽暗地域)---")
|
||||
var autoplay = load("res://sim/level_autoplay.gd")
|
||||
# ① L1 单链与 L2 图经同一套加载校验(单链 = 出边恰一的退化形态——load_tables 全绿已含,显式复述)
|
||||
Kit.check("LevelTable 图合法性校验:L1 单链 + L2 图同套算法零错误", _db.level.validate().is_empty())
|
||||
# ① 关卡表行级校验零错误(图合法性自 T1 起不再由表加载期承担——关卡表携带的模板数据随卡② T3 清出)
|
||||
Kit.check("LevelTable 行级校验零错误(图合法性移交 validate_graph)", _db.level.validate().is_empty())
|
||||
# ② L2 行集结构(网状图建议规模)
|
||||
var l2 := {}
|
||||
for row in _db.level.rows:
|
||||
@@ -164,39 +164,37 @@ func _smoke_level_graph() -> void:
|
||||
Kit.check("L2 普通战斗节点 = 6 + Boss 恒一(编号最大恒终点)", int(tcount.battle) == 6 and int(tcount.boss) == 1)
|
||||
Kit.check("L2 恢复节点 1(rest_heal_pct 行字段)+ 奖励节点 1(直通空态)+ 商店房节点 1(商店房批)", int(tcount.reward) == 1 and int(tcount.rest) == 1 and int(tcount.shop) == 1)
|
||||
Kit.check("L2 真实岔路 = %d 处(≥2)" % branch, branch >= 2)
|
||||
# ③ 图校验正反断言(违规行集定位到类别——加载期致命的同款算法)
|
||||
var lt = load("res://data/schemas/level_table.gd").new()
|
||||
var good := [{"node_depth": 0, "type": "battle", "next": [1, 2]}, {"node_depth": 1, "type": "battle", "next": [2]},
|
||||
{"node_depth": 2, "type": "boss"}]
|
||||
lt.rows.assign([{"level_index": 9, "display_name": "t", "rest_heal_pct": 30, "nodes": good}])
|
||||
Kit.check("图校验正例:两岔合流小图零错误", lt.validate().is_empty())
|
||||
# ③ 图校验正反断言改走 validate_graph 公有面(迭代 35 卡② T1 随动·声明):
|
||||
# · 夹具字段随生成图读面换代(id/type/depth/next 取代 node_depth 双编号空间);
|
||||
# · 「入口唯一违例」判据随模板池退役——多入口是现行合法形态(关卡节点图生成 v1.1 结构铁律),
|
||||
# 该条改判正例;「孤儿节点」条去掉「双入口同体违例」措辞,只保留到不了终点/进不去的违例面;
|
||||
# · 前进方向判据由「目标编号严格递增」换为「目标深度严格大于源」(契约 1④)。
|
||||
var LT = load("res://data/schemas/level_table.gd")
|
||||
var good := [{"id": 0, "type": "battle", "depth": 0, "next": [1, 2]},
|
||||
{"id": 1, "type": "battle", "depth": 1, "next": [2]},
|
||||
{"id": 2, "type": "boss", "depth": 2, "next": []}]
|
||||
Kit.check("图校验正例:两岔合流小图零错误〔v1〕", LT.validate_graph(good).is_empty())
|
||||
var dup = JSON.parse_string(JSON.stringify(good))
|
||||
dup[1].node_depth = 0
|
||||
lt.rows.assign([{"level_index": 9, "display_name": "t", "rest_heal_pct": 30, "nodes": dup}])
|
||||
Kit.check("图校验拒绝:节点编号重复(关卡内唯一定位地址)", _has_err_category(lt.validate(), "dup_key"))
|
||||
dup[1].id = 0
|
||||
Kit.check("图校验拒绝:句柄重复(图内唯一句柄)〔v1〕", _has_err_category(LT.validate_graph(dup), "dup_key"))
|
||||
var dual = JSON.parse_string(JSON.stringify(good))
|
||||
dual.append({"node_depth": 9, "type": "battle", "next": [2]})
|
||||
lt.rows.assign([{"level_index": 9, "display_name": "t", "rest_heal_pct": 30, "nodes": dual}])
|
||||
Kit.check("图校验拒绝:第二无入边节点(入口唯一违例——入口节点数 = 3)", _has_err_category(lt.validate(), "structural"))
|
||||
dual.append({"id": 9, "type": "battle", "depth": 0, "next": [2]})
|
||||
Kit.check("图校验正例:多入口合法(入口唯一判据随模板池退役)〔v1〕", LT.validate_graph(dual).is_empty())
|
||||
var boss_out = JSON.parse_string(JSON.stringify(good))
|
||||
boss_out[2].next = [1]
|
||||
lt.rows.assign([{"level_index": 9, "display_name": "t", "rest_heal_pct": 30, "nodes": boss_out}])
|
||||
Kit.check("图校验拒绝:Boss 带出边(终点不可外出)", _has_err_category(lt.validate(), "structural"))
|
||||
Kit.check("图校验拒绝:Boss 带出边(终点不可外出)〔v1〕", _has_err_category(LT.validate_graph(boss_out), "structural"))
|
||||
var cyc = JSON.parse_string(JSON.stringify(good))
|
||||
cyc[1].next = [0]
|
||||
lt.rows.assign([{"level_index": 9, "display_name": "t", "rest_heal_pct": 30, "nodes": cyc}])
|
||||
Kit.check("图校验拒绝:回边 1→0(连线只朝前进方向 ⇒ 无环)", _has_err_category(lt.validate(), "structural"))
|
||||
Kit.check("图校验拒绝:回边 1→0(目标深度须严格大于源)〔v1〕", _has_err_category(LT.validate_graph(cyc), "structural"))
|
||||
var dang = JSON.parse_string(JSON.stringify(good))
|
||||
dang[0].next = [1, 7]
|
||||
lt.rows.assign([{"level_index": 9, "display_name": "t", "rest_heal_pct": 30, "nodes": dang}])
|
||||
Kit.check("图校验拒绝:出边指向不存在编号(悬空致命)", _has_err_category(lt.validate(), "dangling_next"))
|
||||
Kit.check("图校验拒绝:出边指向不存在句柄(悬空致命)〔v1〕", _has_err_category(LT.validate_graph(dang), "dangling_next"))
|
||||
var orphan = JSON.parse_string(JSON.stringify(good))
|
||||
orphan[0].next = [1]
|
||||
orphan[1].next = [2]
|
||||
orphan.append({"node_depth": 5, "type": "battle", "next": [2]})
|
||||
lt.rows.assign([{"level_index": 9, "display_name": "t", "rest_heal_pct": 30, "nodes": orphan}])
|
||||
var orphan_errs: Array = lt.validate()
|
||||
Kit.check("图校验拒绝:孤儿节点(从入口不可达 / 双入口同体违例)", _has_err_category(orphan_errs, "structural"))
|
||||
orphan.append({"id": 5, "type": "battle", "depth": 1, "next": [6]})
|
||||
orphan.append({"id": 6, "type": "rest", "depth": 2, "next": []})
|
||||
Kit.check("图校验拒绝:非 Boss 终点(终点恒为 Boss)〔v1〕", _has_err_category(LT.validate_graph(orphan), "structural"))
|
||||
# ④ L2 选路推进:min / max 双路径全通(确定性路径轴——零随机流自开)
|
||||
# 战斗随机值面随全局序漂移——max 路径种子按行为面扫描(路线形状/商店/选路口为断言本体)
|
||||
var win_min = autoplay.run_level(_db, 42, 2, 200, 100, false, null, "normal", "min", 0)
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
# tests/unit/core/level_table_validate_test.gd — 消费侧加载校验四检单测(迭代 35 卡② T1/V1)
|
||||
# 覆盖:validate_graph 正例(多入口多终点/含岔口单终点/单链退化显式出边)+负例八类
|
||||
#(非 Boss 终点/Boss 带出边/环=回边与自环/双独立链全员可达正证/悬空出边/句柄重复/battle 缺深度/空图)
|
||||
#+「勿扩勿缩」反证:商店位与入口终点个数不在本检范围(关卡节点地图 v2.1「位置约束由冒烟断言把关」)。
|
||||
# 运行:godot --headless --path game --script res://tests/unit/core/level_table_validate_test.gd(expect 0)
|
||||
# 协议(tests/README):extends SceneTree + quit(code);夹具资源直构不碰生产表。
|
||||
extends SceneTree
|
||||
|
||||
const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
const LevelTableScript := preload("res://data/schemas/level_table.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_test_positive()
|
||||
_test_negative()
|
||||
_test_scope_not_extended()
|
||||
quit(Kit.finish("level_table_validate_test"))
|
||||
|
||||
|
||||
func _n(id: int, type_: String, depth: int, outs: Array) -> Dictionary:
|
||||
return {"id": id, "type": type_, "depth": depth, "next": outs}
|
||||
|
||||
|
||||
func _errs(nodes: Array) -> Array:
|
||||
return LevelTableScript.validate_graph(nodes)
|
||||
|
||||
|
||||
func _has(errs: Array, cat: String, needle: String = "") -> bool:
|
||||
for e in errs:
|
||||
if String(e["category"]) != cat:
|
||||
continue
|
||||
if needle == "" or String(e["message"]).contains(needle):
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
# ---- 正例(四检全过)----
|
||||
|
||||
func _test_positive() -> void:
|
||||
# 多入口多终点:两入口共汇入一岔口,两终点各在互异深度
|
||||
var multi := [
|
||||
_n(0, "battle", 0, [2]), _n(1, "battle", 0, [2]),
|
||||
_n(2, "battle", 1, [3, 5]),
|
||||
_n(3, "rest", 2, [4]), _n(4, "shop", 3, [6]), _n(6, "boss", 4, []),
|
||||
_n(5, "reward", 2, [7]), _n(7, "shop", 3, [8]), _n(8, "boss", 5, [])]
|
||||
Kit.check("四检正例:多入口多终点图零错误〔v1〕", _errs(multi).is_empty())
|
||||
# 含岔口单终点:岔口两侧链与主去路同深、汇入同一后继
|
||||
var forked := [
|
||||
_n(0, "battle", 0, [1]),
|
||||
_n(1, "battle", 1, [2, 3]),
|
||||
_n(2, "reward", 2, [4]), _n(3, "rest", 2, [4]),
|
||||
_n(4, "shop", 3, [5]), _n(5, "boss", 4, [])]
|
||||
Kit.check("四检正例:含岔口单终点图零错误〔v1〕", _errs(forked).is_empty())
|
||||
# 单链退化形态合法(校验不设「必有岔口」)
|
||||
var chain := [_n(0, "battle", 0, [1]), _n(1, "battle", 1, [2]), _n(2, "shop", 2, [3]), _n(3, "boss", 3, [])]
|
||||
Kit.check("四检正例:单链退化图零错误(不设必有岔口)〔v1〕", _errs(chain).is_empty())
|
||||
|
||||
|
||||
# ---- 负例九类(各报对应类别与判据消息)----
|
||||
|
||||
func _test_negative() -> void:
|
||||
var non_boss_end := [_n(0, "battle", 0, [1]), _n(1, "battle", 1, [])]
|
||||
Kit.check("负例:终点非 Boss 命中「终点恒为 Boss」〔v1〕",
|
||||
_has(_errs(non_boss_end), "structural", "终点 1 非 Boss"))
|
||||
var boss_out := [_n(0, "battle", 0, [1]), _n(1, "boss", 1, [2]), _n(2, "shop", 2, [])]
|
||||
Kit.check("负例:Boss 带出边被拒〔v1〕", _has(_errs(boss_out), "structural", "Boss 节点 1 带出边"))
|
||||
var back_edge := [_n(0, "battle", 0, [1]), _n(1, "battle", 1, [2]), _n(2, "shop", 0, [3]), _n(3, "boss", 4, [])]
|
||||
Kit.check("负例:回边(目标深度不严格大于源)命中前进方向判据〔v1〕",
|
||||
_has(_errs(back_edge), "structural", "违反前进方向"))
|
||||
var cycle := [_n(0, "battle", 0, [1]), _n(1, "battle", 1, [2]), _n(2, "shop", 2, [1]), _n(3, "boss", 3, [])]
|
||||
Kit.check("负例:环(2→1 回边)被前进方向拦下〔v1〕", _has(_errs(cycle), "structural", "违反前进方向"))
|
||||
# 判据②(无孤儿无断头)与判据④(前进方向)在无环图上互斥:DAG 的每个源点都是入口、每条极长路都止于终点,
|
||||
# 故 ② 是④失效时的纵深防御面,构造不出独立负例——此处以「两独立链双入口双终点」正证 ② 不误伤。
|
||||
var two_components := [
|
||||
_n(0, "battle", 0, [1]), _n(1, "shop", 1, [2]), _n(2, "boss", 2, []),
|
||||
_n(3, "battle", 0, [4]), _n(4, "rest", 1, [5]), _n(5, "boss", 2, [])]
|
||||
Kit.check("判据②正证:两独立链(双入口双终点)不误伤·全员可达〔v1〕", _errs(two_components).is_empty())
|
||||
var self_loop := [_n(0, "battle", 0, [1]), _n(1, "shop", 1, [2]), _n(2, "boss", 2, []), _n(3, "battle", 1, [3])]
|
||||
Kit.check("判据②兜底面:自环先被前进方向拦下(④ 与 ② 互斥的实证)〔v1〕",
|
||||
_has(_errs(self_loop), "structural", "违反前进方向"))
|
||||
var dangling := [_n(0, "battle", 0, [9]), _n(1, "boss", 1, [])]
|
||||
Kit.check("负例:悬空出边报 dangling_next〔v1〕", _has(_errs(dangling), "dangling_next"))
|
||||
var dup := [_n(0, "battle", 0, [1]), _n(1, "boss", 1, []), _n(1, "shop", 2, [])]
|
||||
Kit.check("负例:句柄重复报 dup_key〔v1〕", _has(_errs(dup), "dup_key"))
|
||||
var no_depth := [_n(0, "battle", 0, [1]), {"id": 1, "type": "battle", "next": [2]}, _n(2, "boss", 2, [])]
|
||||
Kit.check("负例:battle 节点缺深度被良构基底拒〔v1〕", _has(_errs(no_depth), "structural", "缺深度"))
|
||||
Kit.check("负例:空节点集被拒〔v1〕", _has(_errs([]), "structural", "图节点集为空"))
|
||||
var bad_type := [_n(0, "mine", 0, [1]), _n(1, "boss", 1, [])]
|
||||
Kit.check("负例:类型出域报 enum〔v1〕", _has(_errs(bad_type), "enum"))
|
||||
|
||||
|
||||
# ---- 「勿扩勿缩」反证:本检不含商店位与个数对参数档的核对 ----
|
||||
|
||||
func _test_scope_not_extended() -> void:
|
||||
# 合法形状但全程无商店/三个终点同入口图——四检不该因此报错(商店位归冒烟断言、个数归产出侧)
|
||||
var no_shop := [_n(0, "battle", 0, [1, 2]), _n(1, "battle", 1, [3]), _n(2, "rest", 1, [3]), _n(3, "boss", 2, [])]
|
||||
Kit.check("范围反证:无商店图不在加载校验内报错(商店位由冒烟断言把关)〔v1〕", _errs(no_shop).is_empty())
|
||||
var two_entries := [_n(0, "battle", 0, [2]), _n(1, "battle", 0, [2]), _n(2, "boss", 1, [])]
|
||||
Kit.check("范围反证:双入口合法(入口唯一判据已随模板池退役,个数归产出侧)〔v1〕", _errs(two_entries).is_empty())
|
||||
# 良构字段缺失(无 next 键)该报错——不属被退役判据
|
||||
var missing_key := [{"id": 0, "type": "battle", "depth": 0}, _n(1, "boss", 1, [])]
|
||||
Kit.check("范围反证:节点缺 next 字段仍被良构基底拒〔v1〕", _has(_errs(missing_key), "structural", "缺字段 next"))
|
||||
@@ -0,0 +1 @@
|
||||
uid://bhymincbe2j4m
|
||||
Reference in new issue
Block a user