fix(iter37①): T1 core 域缺陷修——单据归属判定/数值入表/公式单点/信号契约载体统一
- d1 伤害控制器来源敌作废判定 `==`→`is_same`(同内容异实例不再误废;夹具双敌两断言〔wd1〕) - d11 shop 表新增 relic_base_price/spend_discount 两 knob+域检+ConfigDB 访问器+level_runner 读表; level_shop_economy_test 三表值断言+relic 现价读表与迁移前字面量逐位等价〔wd11〕 - d16a HeroTable.display_name+域检+访问器+unit_container 读表;spawn 真实链断言〔wd16〕 - d14 board_generator 目标雷数公式单点 target_mine_count(两调用点改引)+legality 去 db 参数(两调用点同批改)〔wd14〕 - d15 蓄力层上限 CHARGE_LAYER_CAP 常量(两处字面量+注释随动) - d16b unit_container/status_container 六处 Dictionary 取值显式 int() 包裹(读侧包) - d10 minesweeper 两信号按 §2.1 三要素补契约(载荷键集按发射位并集实查)+全库 28 处信号注释载体 #→## 撤销证明:回退 d1 hunk → 同容异实例断言红(A 单据被 B 死亡误废),还原复绿; 回退 d10 契约 → 契约在位扫描 undocumented=2 红,还原归零。等价类申报(d11/d14/d15/d16)见实现报告。 代拍板:#1 target_mine_count 签名(照接口块)、#2 契约措辞与载荷键并集(实查发射位)、#4 int() 读侧包写法。
This commit is contained in:
1 parent
a0acdf5c43
commit
1b9736f065
25 files changed
+186
-59
No files matched your search
@@ -29,6 +29,7 @@ func _init() -> void:
|
||||
_test_five_recipes_and_replay(db)
|
||||
_test_policy_immediate_mode(db)
|
||||
_test_source_death_void(db)
|
||||
_test_identical_content_dual_enemy(db)
|
||||
_test_requires_gate_and_zero(db)
|
||||
_test_hero_death_halt(db)
|
||||
_test_ac_merge(db)
|
||||
@@ -211,6 +212,46 @@ func _test_source_death_void(db) -> void:
|
||||
Kit.check("作废单零行零扣血", int(h.units.hero_unit().hp) == hero_before and _rows.is_empty())
|
||||
|
||||
|
||||
# ---- 同内容双敌:单据归属按实例身份判定(生产 handle_id 使内容全同不可达——夹具显式钉住语义面) ----
|
||||
|
||||
# 单位字典键集复刻(剥离 handle_id:生产键集含句柄,`==` 误命的可达条件被破坏,夹具刻意不携句柄)
|
||||
func _unit_fixture() -> Dictionary:
|
||||
return {
|
||||
"enemy_id": "fixture_enemy",
|
||||
"display_name": "哥布林",
|
||||
"hp": 5, "max_hp": 5, "atk": 1,
|
||||
"pool": [], "pending_skill": "", "roll_seq": 0, "basic_streak": 0,
|
||||
"armor_type": "light", "armor_class": 0, "attack_type": "normal",
|
||||
"alive": true, "is_hero": false,
|
||||
}
|
||||
|
||||
|
||||
func _test_identical_content_dual_enemy(db) -> void:
|
||||
var a := _unit_fixture()
|
||||
var b := _unit_fixture()
|
||||
Kit.check("前置:双敌字典内容全同且非同一实例", a == b and not is_same(a, b))
|
||||
var h = _harness(db)
|
||||
if not h.spawn_ok:
|
||||
return
|
||||
var ticket: Dictionary = h.ctrl.open_ticket(DamageControllerScript.KIND_BOARD_FLAT_HERO, "mine_hit",
|
||||
"同容双敌单", "棋盘兑付", 1, DamageControllerScript.TARGET_HERO, 1)
|
||||
ticket["source_unit"] = a
|
||||
var t: int = h.ctrl.submit(ticket)
|
||||
h.units.unit_died.emit(b) # 真实死亡事件(单位自行宣布)——同容异实例
|
||||
h.ctrl.flush()
|
||||
Kit.check("同内容异实例敌死亡不误废他人单据〔wd1〕", h.ctrl.outcome(t) == "redeemed")
|
||||
var h2 = _harness(db)
|
||||
if not h2.spawn_ok:
|
||||
return
|
||||
var ticket2: Dictionary = h2.ctrl.open_ticket(DamageControllerScript.KIND_BOARD_FLAT_HERO, "mine_hit",
|
||||
"同容双敌单·自亡", "棋盘兑付", 1, DamageControllerScript.TARGET_HERO, 1)
|
||||
ticket2["source_unit"] = a
|
||||
var t2: int = h2.ctrl.submit(ticket2)
|
||||
h2.units.unit_died.emit(a)
|
||||
h2.ctrl.flush()
|
||||
Kit.check("来源敌自身死亡单据作废(正向对照)〔wd1〕", h2.ctrl.outcome(t2) == "voided")
|
||||
|
||||
|
||||
# ---- requires 前置门:前置单未实际有效兑付 → 后继单静默作废;无存活敌 blast = 合法零兑付 ----
|
||||
|
||||
func _test_requires_gate_and_zero(db) -> void:
|
||||
|
||||
@@ -157,7 +157,7 @@ func _smoke_board_generator() -> void:
|
||||
if b1 == null:
|
||||
continue
|
||||
Kit.check("L%d clearable(安全格 8 邻连通)" % L, board_generator.is_clearable(b1))
|
||||
Kit.check("L%d legality 全过" % L, board_generator.legality(b1, row, _db))
|
||||
Kit.check("L%d legality 全过" % L, board_generator.legality(b1, row))
|
||||
var inf := float(b1.metrics.get("inferability", 0.0))
|
||||
var mode := String(b1.metrics.get("accept_mode", ""))
|
||||
print(" L%d 实测:inferability=%.3f / attempts_used=%d / accept_mode=%s / target=%.2f" % [
|
||||
|
||||
@@ -18,6 +18,7 @@ func _init() -> void:
|
||||
quit(Kit.finish("board_generator_test"))
|
||||
return
|
||||
_test_generate_shape_and_mines()
|
||||
_test_target_mine_count_single_source()
|
||||
_test_digits_equal_neighbor_mine_count()
|
||||
_test_same_seed_bit_identical()
|
||||
_test_clearable_and_inferability_selfconsistent()
|
||||
@@ -54,6 +55,34 @@ func _test_generate_shape_and_mines() -> void:
|
||||
Kit.check("生成期指标集已落盘(metrics 非空)", not b.metrics.is_empty())
|
||||
|
||||
|
||||
# 目标雷数公式单点(d14):同参同值对拍原式 + 真实产图指标读数同源
|
||||
func _test_target_mine_count_single_source() -> void:
|
||||
var bad := 0
|
||||
var checked := 0
|
||||
for L in [1, 2, 3]:
|
||||
var row: Dictionary = _db.resolve_board_gen(L, 0)
|
||||
if row.is_empty():
|
||||
continue
|
||||
var params := {
|
||||
"w": int(row.board_width), "h": int(row.board_height),
|
||||
"density": float(row.mine_density),
|
||||
}
|
||||
var from_fn: int = BoardGeneratorScript.target_mine_count(params)
|
||||
var from_expr: int = int(round(float(params.density) * float(params.w * params.h - BoardData.SPAWN_AREA_CELLS)))
|
||||
checked += 1
|
||||
if from_fn != from_expr:
|
||||
bad += 1
|
||||
Kit.check("target_mine_count 与原式同参同值(%d 档对拍全等)〔wd14〕" % checked, checked > 0 and bad == 0)
|
||||
var b := _generate(1, 0, 74000)
|
||||
if b == null:
|
||||
Kit.check("生成返回棋盘(雷数单点段)", false)
|
||||
return
|
||||
var row1: Dictionary = _db.resolve_board_gen(1, 0)
|
||||
Kit.check("产物断言读数 = target_mine_count 同源(metrics.target_mines)〔wd14〕",
|
||||
int(b.metrics.get("target_mines", -1)) == BoardGeneratorScript.target_mine_count({
|
||||
"w": int(row1.board_width), "h": int(row1.board_height), "density": float(row1.mine_density)}))
|
||||
|
||||
|
||||
func _test_digits_equal_neighbor_mine_count() -> void:
|
||||
var b := _generate(1, 1, 72000)
|
||||
if b == null:
|
||||
|
||||
@@ -242,6 +242,7 @@ func _init() -> void:
|
||||
if d.severity == "error" or d.severity == "fatal":
|
||||
push_error(" [%s] %s: %s" % [d.severity, d.category, d.message])
|
||||
# 套执行序沿源编排(基点 _run_core_smoke 调用序——本组套的相对序)
|
||||
_smoke_table_migration()
|
||||
_smoke_level()
|
||||
_smoke_level_graph()
|
||||
_smoke_gp_wallet()
|
||||
@@ -254,6 +255,20 @@ func _init() -> void:
|
||||
quit(Kit.finish("level_shop_economy_test"))
|
||||
|
||||
|
||||
# ---- 数值入表等价证明(d11/d16a——原字面量迁移:表值 == 迁移前字面量,消费端读表)----
|
||||
|
||||
func _smoke_table_migration() -> void:
|
||||
print("--- 数值入表(shop 两 knob + hero 显示名 · 原值迁移)---")
|
||||
Kit.check("shop.relic_base_price 表值 == 迁移前字面量(15.0)〔wd11〕", _db.shop_relic_base_price() == 15.0)
|
||||
Kit.check("shop.spend_discount 表值 == 迁移前字面量(0.8)〔wd11〕", _db.shop_spend_discount() == 0.8)
|
||||
Kit.check("hero.display_name 表值 == 迁移前字面量(探险者)〔wd16〕", _db.hero_display_name() == "探险者")
|
||||
# 消费端读表:spawn 真实构造链的英雄单位显示名与表值同
|
||||
var units = load("res://core/unit_container.gd").new(_db)
|
||||
var spawned: bool = units.spawn(1, 0, int(_db.hero.max_hp))
|
||||
Kit.check("spawn 后英雄单位 display_name 读表同值(探险者)〔wd16〕",
|
||||
spawned and String(units.hero_unit().get("display_name", "")) == _db.hero_display_name())
|
||||
|
||||
|
||||
# ---- 生成图深度扩面覆盖断言(迭代 35 卡② S5/V11——节点源=卡① 生成器产图,
|
||||
# 敌侧/板侧解析一律走 ConfigDB 真实消费链,禁直读行集自算)----
|
||||
|
||||
@@ -1274,15 +1289,19 @@ func _smoke_shop_pool() -> void:
|
||||
var r8 = scen["r"]
|
||||
# ② 货架投影形制:kind 词表封闭 + 现价公式推导(ceil(base×b^(关序−1)×A^0)——核心现算,呈现零复算)
|
||||
var l2 := int(r8.level_row.level_index)
|
||||
# 现价等价证明(d11——消费端读表推导 vs 迁移前硬编码字面量推导:同输入逐位一致)
|
||||
var relic_price_now: int = r8.pool_price("relic")
|
||||
var relic_price_pre: int = int(ceil(15.0 * pow(_db.shop_price_b(), float(l2 - 1)) * 1.0))
|
||||
Kit.check("relic 现价 = ceil(15.0×b^(关序−1))(读表与迁移前字面量逐位一致,%d)〔wd11〕" % relic_price_now, relic_price_now == relic_price_pre)
|
||||
for c in r8.shop_shelf():
|
||||
# T6 商店保底后 relic 卡合法入架(保底占位)——词表断言放行 relic;价格断言分叉
|
||||
# (数值卡走 shop_pool_row 推导 / relic 卡走 15 固定锚——同公式不同基价)
|
||||
# (数值卡走 shop_pool_row 推导 / relic 卡走表 knob relic_base_price——同公式不同基价)
|
||||
var ckind := String(c.get("kind_id", ""))
|
||||
var is_relic := ckind == "relic"
|
||||
Kit.check("货架 kind 在封闭词表或保底 relic 卡(%s)" % ckind, is_relic or shelf_kinds.has(ckind))
|
||||
var expect_p: int
|
||||
if is_relic:
|
||||
expect_p = int(ceil(15.0 * pow(_db.shop_price_b(), float(l2 - 1)) * 1.0))
|
||||
expect_p = int(ceil(_db.shop_relic_base_price() * pow(_db.shop_price_b(), float(l2 - 1)) * 1.0))
|
||||
else:
|
||||
var prow: Dictionary = _db.shop_pool_row(ckind)
|
||||
expect_p = int(ceil(float(int(prow.get("base_price", 0))) * pow(_db.shop_price_b(), float(l2 - 1)) * 1.0))
|
||||
|
||||
Reference in new issue
Block a user