全局作用域变异首发批 T7 断言批(迭代14③号卡):smoke_all 新段 _smoke_global_variant 29断言(互锁负样本③-⑦=resource_face⇒global/global⇒resource_face·always·any/effect_value[0,50]域;门控三维=boss池含global行·battle静默跳过·缺省双参零扰动·L1Boss免疫·elite前向位;替换制=Boss图board_variants恰一行+零region格+零取号〔_CountVariantRng取号计数桩〕;快照投影;off态逐位零扰动;守恒77;白名单三行不动;v=30构造例=15/18/9/9/3/7;合成序构造例=skew+luck_bias加法合计77+8;v=50域上界恒正;确定性两遍逐位)+rows.size断言6→7换代——冒烟718→747断言只增不减;config_db_test 通配选择器限定region行+global行第三维三断言(run_all 15套全绿恢复)
This commit is contained in:
1 parent
d8dc9b42a1
commit
cb52ccc8ae
2 files changed
+173
-3
No files matched your search
+162
-2
@@ -80,6 +80,7 @@ func _run_core_smoke(db, rng) -> void:
|
||||
_smoke_variation_framework()
|
||||
_smoke_variant_placement(1)
|
||||
_smoke_variant_placement(2)
|
||||
_smoke_global_variant()
|
||||
_smoke_full_battle(1, 0, "L1 · 地精")
|
||||
_smoke_full_battle(2, 0, "L2 · 熊地精")
|
||||
_smoke_level()
|
||||
@@ -1265,8 +1266,8 @@ func _mk_stream(stream_name: String, scope: int) -> RandomNumberGenerator:
|
||||
|
||||
func _smoke_variation_framework() -> void:
|
||||
print("--- 地形变异 框架面(首批四类型 + 候选池门控 + 跨字段互锁)---")
|
||||
check(_db.variation.rows.size() == 6, "变异清单 6 行(荆棘丛/藤蔓缠绕/岩浆/烬覆 + 落石/矿尘——首批类型定稿批 + 第三关内容批)")
|
||||
check(_db.variation.validate().is_empty(), "变异表校验零错误(6 行全过域与跨字段互锁)")
|
||||
check(_db.variation.rows.size() == 7, "变异清单 7 行(荆棘丛/藤蔓缠绕/岩浆/烬覆 + 落石/矿尘 + 贫矿脉——首批类型定稿批 + 第三关内容批 + 全局作用域变异首发批)")
|
||||
check(_db.variation.validate().is_empty(), "变异表校验零错误(7 行全过域与跨字段互锁——含 scope=global 三互锁)")
|
||||
var pool1_ids: Array = []
|
||||
for r in _db.variant_pool(1, "dungeon"):
|
||||
pool1_ids.append(String(r.variant_id))
|
||||
@@ -1300,6 +1301,165 @@ func _errs_has_cross_field(errs: Array) -> bool:
|
||||
return false
|
||||
|
||||
|
||||
# ---- 全局作用域变异 首发批断言面(2026-10-04 迭代 14 ③号卡——T0-T5 集中:互锁/门控三维/
|
||||
# 替换制/零取号/资源面偏斜守恒与合成序/快照投影/呈现面锚)----
|
||||
|
||||
# 取号计数桩(variant_place 零取号断言——子类拦截计数,派生与取数语义零改动)
|
||||
class _CountVariantRng extends RngServiceCore:
|
||||
var variant_calls := 0
|
||||
func open_stream(purpose: String) -> RandomNumberGenerator:
|
||||
if purpose == "variant_place":
|
||||
variant_calls += 1
|
||||
return super.open_stream(purpose)
|
||||
|
||||
|
||||
func _smoke_global_variant() -> void:
|
||||
print("--- 全局作用域变异 首发批(门控三维 + 替换制 + 资源面偏斜)---")
|
||||
# ① 跨字段互锁负样本(resource_face ⟺ scope=global 三条 + effect_value 新域 [0,50])
|
||||
var vt = load("res://data/schemas/variation_table.gd").new()
|
||||
vt.rows.assign([{"variant_id": "g1", "display_name": "负甲", "unlock_level": 2, "scope": "region",
|
||||
"effect": "resource_face", "effect_value": 30, "trigger": "always", "placement": "any",
|
||||
"board_types": [], "counter_items": []}])
|
||||
check(_errs_has_cross_field(vt.validate()), "互锁负样本③:resource_face + scope=region 被拒(resource_face ⇒ global)")
|
||||
vt.rows.assign([{"variant_id": "g2", "display_name": "负乙", "unlock_level": 2, "scope": "global",
|
||||
"effect": "hide_number", "effect_value": 0, "trigger": "always", "placement": "any",
|
||||
"board_types": [], "counter_items": []}])
|
||||
check(_errs_has_cross_field(vt.validate()), "互锁负样本④:scope=global + effect=hide_number 被拒(global ⇒ resource_face)")
|
||||
vt.rows.assign([{"variant_id": "g3", "display_name": "负丙", "unlock_level": 2, "scope": "global",
|
||||
"effect": "resource_face", "effect_value": 30, "trigger": "on_open", "placement": "any",
|
||||
"board_types": [], "counter_items": []}])
|
||||
check(_errs_has_cross_field(vt.validate()), "互锁负样本⑤:scope=global + trigger=on_open 被拒(global ⇒ always 板级恒在)")
|
||||
vt.rows.assign([{"variant_id": "g4", "display_name": "负丁", "unlock_level": 2, "scope": "global",
|
||||
"effect": "resource_face", "effect_value": 30, "trigger": "always", "placement": "closed_only",
|
||||
"board_types": [], "counter_items": []}])
|
||||
check(_errs_has_cross_field(vt.validate()), "互锁负样本⑥:scope=global + placement=closed_only 被拒(global ⇒ any 无格子布置域)")
|
||||
vt.rows.assign([{"variant_id": "g5", "display_name": "负戊", "unlock_level": 2, "scope": "global",
|
||||
"effect": "resource_face", "effect_value": 51, "trigger": "always", "placement": "any",
|
||||
"board_types": [], "counter_items": []}])
|
||||
var vterr: Array = vt.validate()
|
||||
var domain_hit := false
|
||||
for e in vterr:
|
||||
if String(e.get("category", "")) == "domain":
|
||||
domain_hit = true
|
||||
check(domain_hit, "互锁负样本⑦:resource_face effect_value=51 越域 [0,50] 被拒(不适用 flat_damage [1,9] 加法核)")
|
||||
# ② 门控第三维(节点身份——global 行仅 boss/elite 入池;缺省/普通节点静默跳过零报错)
|
||||
var pool_boss_ids: Array = []
|
||||
for r in _db.variant_pool(2, "underdark", "boss"):
|
||||
pool_boss_ids.append(String(r.variant_id))
|
||||
check(pool_boss_ids == ["magma", "ash", "skewed_veins"], "门控三维:L2 underdark boss 池 = region 两行 + 贫矿脉(board_types ∅ 通配)")
|
||||
var pool_battle_ids: Array = []
|
||||
for r in _db.variant_pool(2, "underdark", "battle"):
|
||||
pool_battle_ids.append(String(r.variant_id))
|
||||
check(pool_battle_ids == ["magma", "ash"], "门控三维:battle 节点静默跳过 global 行(普通图零报错零警告)")
|
||||
check(_db.variant_pool(2, "underdark").is_empty() == false and _db.variant_pool(2, "underdark").size() == 2,
|
||||
"门控三维:缺省双参调用 = global 行跳过(既有调用口径零扰动)")
|
||||
check(_db.variant_pool(1, "underdark", "boss").is_empty(), "L1 Boss 免疫:unlock_level=2 门控在 boss 节点同样生效(教学保护带 B)")
|
||||
check(_db.global_variant_row(2, "underdark", "battle").is_empty(), "全局决议:battle 节点恒无全局型")
|
||||
check(_db.global_variant_row(2, "underdark", "boss").get("variant_id", "") == "skewed_veins", "全局决议:L2 boss 取贫矿脉(确定性单行)")
|
||||
check(_db.global_variant_row(1, "underdark", "boss").is_empty() and _db.global_variant_row(1, "dungeon", "boss").is_empty(),
|
||||
"全局决议:L1 boss 全局池空 → 空行(兜底回落承载)")
|
||||
check(_db.global_variant_row(2, "underdark", "elite").get("variant_id", "") == "skewed_veins", "elite 前向词位:boss 同池(首发零 elite 表行——词表位不造行)")
|
||||
# ③ 替换制 + 零取号:L2 Boss 图恰一行板级变异 + 零 region 格 + variant_place 零消费
|
||||
var crng = _CountVariantRng.new()
|
||||
crng.set_master_seed(42)
|
||||
var boss_settlement = load("res://core/battle_orchestrator.gd").new(_db, crng)
|
||||
var boss_ok: bool = boss_settlement.begin_battle({"level_index": 2, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"})
|
||||
check(boss_ok, "L2 Boss 战斗启动(node_depth=4 boss 节点——替换制断言载体)")
|
||||
if boss_ok:
|
||||
var bb = boss_settlement.minesweeper.current_board()
|
||||
check(_arr_str_eq(bb.board_variant_ids(), ["skewed_veins"]), "替换制:Boss 图 board_variants 恰一行 = 贫矿脉")
|
||||
check(bb.variants.is_empty(), "替换制:Boss 图零 region 变异格(无格子级变异标记——呈现件闲置合法态)")
|
||||
check(crng.variant_calls == 0, "零取号合法态:Boss 图 variant_place 零消费(全局取用确定性)")
|
||||
var snap: Dictionary = boss_settlement.presentation_snapshot()
|
||||
check(_arr_str_eq(snap.get("board_variants", []), ["skewed_veins"]), "快照投影:board_variants 呈现面(横幅/角标唯一读取面)")
|
||||
# Boss 图确定性:同种子两遍逐位相同
|
||||
var crng2 = _CountVariantRng.new()
|
||||
crng2.set_master_seed(42)
|
||||
var boss2 = load("res://core/battle_orchestrator.gd").new(_db, crng2)
|
||||
if boss2.begin_battle({"level_index": 2, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}):
|
||||
var bb2 = boss2.minesweeper.current_board()
|
||||
check(_arr_str_eq(bb2.board_variant_ids(), ["skewed_veins"]) and crng2.variant_calls == 0,
|
||||
"确定性:同种子 Boss 图两遍 board_variants/取号逐位相同")
|
||||
# ④ 资源面偏斜投影(RewardEconomy 纯函数面——守恒/白名单/构造例/合成序)
|
||||
var eco = load("res://core/reward_economy.gd").new(_db, load("res://core/rng_service.gd").new())
|
||||
var base_rows: Array = eco._rollable_rows()
|
||||
var base_sum: int = eco._pool_weight(base_rows)
|
||||
check(base_sum == 77, "偏斜基线:正向池现行合计 77(挖掘风险批口径)")
|
||||
eco.board_skew = 0
|
||||
var off_rows: Array = eco._biased_cell_rows()
|
||||
check(off_rows.size() == base_rows.size() and _rows_weights_equal(off_rows, base_rows), "off 态零扰动:board_skew=0 行集与基线逐位相同(不进投影域)")
|
||||
eco.board_skew = 30
|
||||
var on_rows: Array = eco._biased_cell_rows()
|
||||
check(eco._pool_weight(on_rows) == 77, "合计守恒:偏斜后正向池合计 = 77(整数最大余数重归一)")
|
||||
var wmap := _rows_weight_map(on_rows)
|
||||
check(wmap.get("weaken", -1) == 8 and wmap.get("ap_up", -1) == 6 and wmap.get("gp_coin", -1) == 2,
|
||||
"白名单豁免:weaken/gp/action 三行偏斜后逐位不动")
|
||||
check(wmap.get("heal_minor", -1) == 3 and wmap.get("ac_up", -1) == 7, "生存族下调:heal 4→3 / defense 11→7(v=30 构造例)")
|
||||
check(wmap.get("attack_up", -1) == 15 and wmap.get("spark_aoe", -1) == 18 and wmap.get("pierce_strike", -1) == 9 and wmap.get("magic_bolt", -1) == 9,
|
||||
"进攻族上浮:attack 14→15 / blast 16→18 / pierce 8→9 / magic 8→9(v=30 构造例,等比上浮守恒)")
|
||||
eco.board_skew = 30
|
||||
eco.luck_bias = 2
|
||||
var combo_rows: Array = eco._biased_cell_rows()
|
||||
var cw := _rows_weight_map(combo_rows)
|
||||
check(eco._pool_weight(combo_rows) == 77 + 4 * 2, "合成序:图级偏斜先(乘法面)件级 luck_bias 后(加法 +2×四稀有类)——合计 77+8")
|
||||
check(cw.get("heal_minor", -1) == 5 and cw.get("gp_coin", -1) == 4 and cw.get("pierce_strike", -1) == 11 and cw.get("magic_bolt", -1) == 11,
|
||||
"合成序构造例:skew(heal 3)+2=5 / gp 2+2=4 / pierce 9+2=11 / magic 9+2=11(图级定型后玩家构筑叠加)")
|
||||
eco.luck_bias = 0
|
||||
eco.board_skew = 50
|
||||
var v50: Dictionary = _rows_weight_map(eco._biased_cell_rows())
|
||||
check(eco._pool_weight(eco._biased_cell_rows()) == 77 and int(v50.get("heal_minor", -1)) > 0, "域上界 v=50:守恒成立且逐行权重恒正(域 [0,50] 安全)")
|
||||
# 普通图摇号全链路确定性(off 路径)——同种子普通图资源映射逐位相同(golden 对拍另有批前快照)
|
||||
var nrng = load("res://core/rng_service.gd").new()
|
||||
nrng.set_master_seed(42)
|
||||
var normal1 = load("res://core/battle_orchestrator.gd").new(_db, nrng)
|
||||
normal1.begin_battle({"level_index": 2, "node_depth": 0, "hero_hp": int(_db.hero.max_hp)})
|
||||
var n1: Dictionary = _board_resource_dump(normal1)
|
||||
var nrng2 = load("res://core/rng_service.gd").new()
|
||||
nrng2.set_master_seed(42)
|
||||
var normal2 = load("res://core/battle_orchestrator.gd").new(_db, nrng2)
|
||||
normal2.begin_battle({"level_index": 2, "node_depth": 0, "hero_hp": int(_db.hero.max_hp)})
|
||||
check(n1 == _board_resource_dump(normal2) and not n1.is_empty(), "确定性:同种子普通图布置/摇号两遍逐位相同(off 全链路)")
|
||||
|
||||
|
||||
func _arr_str_eq(a, b: Array) -> bool:
|
||||
var arr: Array = a if a is Array else []
|
||||
if arr.size() != b.size():
|
||||
return false
|
||||
for i in arr.size():
|
||||
if String(arr[i]) != String(b[i]):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
func _rows_weights_equal(a: Array, b: Array) -> bool:
|
||||
if a.size() != b.size():
|
||||
return false
|
||||
for i in a.size():
|
||||
if String(a[i].get("reward_id", "")) != String(b[i].get("reward_id", "")) or int(a[i].weight) != int(b[i].weight):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
func _rows_weight_map(rows: Array) -> Dictionary:
|
||||
var m := {}
|
||||
for r in rows:
|
||||
m[String(r.get("reward_id", ""))] = int(r.weight)
|
||||
return m
|
||||
|
||||
|
||||
func _board_resource_dump(settlement) -> Dictionary:
|
||||
var b = settlement.minesweeper.current_board()
|
||||
var dump := {}
|
||||
for c in b.variants.keys():
|
||||
dump["v:%d,%d" % [c.x, c.y]] = ",".join(b.variants[c])
|
||||
for y in b.height:
|
||||
for x in b.width:
|
||||
var ident = settlement._reward_economy.query_identity(Vector2i(x, y))
|
||||
if ident != null:
|
||||
dump["r:%d,%d" % [x, y]] = String(ident.reward_id)
|
||||
return dump
|
||||
|
||||
|
||||
# ---- 地形变异 布置段(首批类型定稿批 2026-09-19——棋盘设置顺序第 4 步断言面)----
|
||||
# 布置发生在编排器战斗初始化(install 初始展开 + roll_board 资源格之后),生成器产物零变异——
|
||||
# 本段经 begin_battle 走完整初始化序后断言:门控一致性 / 布置域 / 数量公式 / 连通片上限 / 确定性。
|
||||
|
||||
@@ -81,13 +81,18 @@ func _test_progress_resolution(db) -> void:
|
||||
Kit.check("关卡越最大行 clamp 取最大关卡行", int(high.get("level_index", -99)) == max_l)
|
||||
|
||||
|
||||
# ---- 变异池门控(unlock_level 闸 + ∅ board_types 通配)----
|
||||
# ---- 变异池门控(unlock_level 闸 + ∅ board_types 通配 + 节点身份第三维)----
|
||||
|
||||
func _test_variant_pool_gate(db) -> void:
|
||||
var rows: Array = db.variation.rows
|
||||
var gated_row := {}
|
||||
var wildcard_row := {}
|
||||
var global_row := {}
|
||||
for row in rows:
|
||||
if String(row.get("scope", "region")) != "region":
|
||||
if global_row.is_empty():
|
||||
global_row = row # global 行第三维门控另测(全局作用域变异批 2026-10-04)
|
||||
continue
|
||||
var tags: Array = row.get("board_types", [])
|
||||
if gated_row.is_empty() and not tags.is_empty():
|
||||
gated_row = row
|
||||
@@ -104,6 +109,11 @@ func _test_variant_pool_gate(db) -> void:
|
||||
var u2: int = int(wildcard_row.unlock_level)
|
||||
var any_pool: Array = db.variant_pool(u2, "nonexistent_board_type_tag")
|
||||
Kit.check("∅ board_types 行通配(任一 board_type 命中)", _rows_contain(any_pool, wildcard_row))
|
||||
if not global_row.is_empty():
|
||||
var u3: int = int(global_row.unlock_level)
|
||||
Kit.check("global 行 boss 节点入池(board_types ∅ 通配)", _rows_contain(db.variant_pool(u3, "nonexistent_board_type_tag", "boss"), global_row))
|
||||
Kit.check("global 行缺省双参调用跳过(既有调用口径零扰动)", not _rows_contain(db.variant_pool(u3, "nonexistent_board_type_tag"), global_row))
|
||||
Kit.check("global 行 battle 节点静默跳过(门控兜底零报错)", not _rows_contain(db.variant_pool(u3, "nonexistent_board_type_tag", "battle"), global_row))
|
||||
|
||||
|
||||
func _rows_contain(pool: Array, row: Dictionary) -> bool:
|
||||
|
||||
Reference in new issue
Block a user