iter56-卡2:封印动作改版(迭代56卡2·受理即揭示): 规则核心(target_sealed挖封印格拒/清图口径=挖开∪封印/能清图的封最优先/notify_cleared清图收轮/sealed_identity撤出→BoardData.cell_identity——上限20用户裁)+结算面dug_under_seal删净+破封一击退役+呈现层(地板+身份字形+印记不依赖揭示态+文案四件)+探针V8-V10+sim域(标空封印剔除约束增强+错触直挖补偿注入0.10)+golden三图换代; MC同批重锚50/档=100局(2026-11-08用户批准)三方对照无结构性恶化; 高档审计通过(17行全量·审计后勘误2项闭环)

This commit is contained in:
DKChan committed 2026-11-08 03:47:33 +08:00
1 parent 908e518306
commit 5ec4a311bb
34 files changed
+589 -164

No files matched your search

+58 -46
View File
@@ -8,8 +8,7 @@ extends RefCounted
## 揭示事实流:每格首次进入已开集恰发一次(已开格零事件)。
## 载荷键集(四处发射位并集):cell: Vector2i/kind: "safe"|"mine"/digit: int(安全格邻雷数;危险格 -1)/
## variant_ids: Array(地形变异身份,危险格空集)/channel: "initial"|"dig"|"chain"|"fx_reveal"/
## resource: bool(格面资源兑付资格:initial=false,其余 true)/dug_under_seal: bool(首格带封印直挖标记——
## initial 通道不发此键)。
## resource: bool(格面资源兑付资格:initial=false,其余 true)。
## 发送时点:initial=install_board 初始展开逐格;dig/chain=_do_dig 揭示闭包内(本格 AP 扣费在其后);
## fx_reveal=fx_reveal 闭包内(零 AP 消耗零相位要求)。
## 消费方依赖:战斗结算 _on_cell_revealed 以 resource 分流缓冲(fx_reveal 由兑付点直入账,禁二次缓冲)、
@@ -94,14 +93,16 @@ func has_seal(c: Vector2i) -> bool:
return _seals.has(c)
func revealed_cells() -> Array:
return _revealed.keys()
func _safe_remaining() -> int:
# 清图口径 = 已挖 ∪ 已封印(封印受理即揭示改版——封掉的安全格计入完成)
var n := 0
for c in _board.safe_cells():
if not _revealed.has(c):
if not _revealed.has(c) and not _seals.has(c):
n += 1
return n
@@ -266,25 +267,41 @@ func settle_batch(intents: Array) -> Array:
func _classify(intent: Dictionary) -> int:
# 0 = C_dig(执行后清图——真模拟,禁计数器捷径);1 = C_stop;2 = C_other
# 清图口径 = 已挖 ∪ 已封印(封印受理即揭示改版——「能清图的挖或封」同归最先类)
if intent.get("action", "") == "dig":
var c: Vector2i = intent.get("cell", Vector2i(-9, -9))
if _board_face_dig(c) == "" and not _board.is_mine(c):
# 模拟 Chain(O, c) 后清图判定——挖封印格同口径:本格封印视为已隐式退役(其余封印照挡)
var sim_seals: Dictionary[Variant, Variant] = _seals.duplicate()
sim_seals.erase(c)
var chain := _chain_closure(c, sim_seals) # 与真实执行同口径——空封印集捷径会在封印挡连锁时误判 C_dig(排雷玩法:真模拟)
# 模拟 Chain(O, c) 后清图判定——真模拟禁计数器捷径(连锁不越封印,真封印集口径)
var chain := _chain_closure(c, _seals)
var remaining := 0
for sc in _board.safe_cells():
if not _revealed.has(sc) and not chain.has(sc):
if not _revealed.has(sc) and not chain.has(sc) and not _seals.has(sc):
remaining += 1
if remaining == 0:
return 0
return 2
if intent.get("action", "") == "seal_place":
return _classify_seal(intent.get("cell", Vector2i(-9, -9)))
if intent.get("action", "") == "stop":
return 1
return 2
# 封印意图的清图预判分类(批次语义序「能清图的封」取数面——受理合法性模拟与清图口径模拟)
func _classify_seal(c: Vector2i) -> int:
if not _board.in_bounds(c) or is_revealed(c) or has_seal(c) or _removed.has(c):
return 2
if _board.is_mine(c):
return 2 # 封危险格不清图(封印受理即揭示——身份当场可见但安全格分母不减)
# 模拟封印受理后清图判定(换装口径:安全格完成 = 已揭示 ∪ 已封印)
var sim_seals: Dictionary[Variant, Variant] = _seals.duplicate()
sim_seals[c] = true
for sc in _board.safe_cells():
if not _revealed.has(sc) and not sim_seals.has(sc):
return 2
return 0
func _execute(intent: Dictionary) -> Dictionary:
match intent.get("action", ""):
"dig":
@@ -305,16 +322,8 @@ func _do_dig(c: Vector2i) -> Dictionary:
var sub := _board_face_dig(c)
if sub != "":
return _outcome(false, "rejected", "board_face", sub)
# 挖封印格(封印不可逆):本格封印事务内隐式退役——链闭包按「本格无封印、其余封印照挡」
# 计算(封印格为 0 时连锁照常外扩);request_dig 被拒则封印原地保留(事务性),受理后该封印轮末不判
# (发射面基线 = 在场封印——标中/标空兑付让位于挖掘本体结局,四情况②④)。
var was_sealed := has_seal(c)
var eff_seals: Dictionary[Variant, Variant] = _seals
if was_sealed:
eff_seals = _seals.duplicate()
eff_seals.erase(c)
# 真模拟连锁与清图(F2/F1——单一实现两名字)
var chain := _chain_closure(c, eff_seals)
# 真模拟连锁与清图(F2/F1——单一实现两名字;连锁不越封印,挖封印格已被合法性表拒绝)
var chain := _chain_closure(c, _seals)
var clears: bool = _cleared_after(chain)
# F2 口径:chain_size = |O′ ∖ (O ∪ {c})|——闭包含已开集 O,只数新开格(不含首格)
var chain_size := 0
@@ -325,10 +334,8 @@ func _do_dig(c: Vector2i) -> Dictionary:
if not action_economy.request_dig(chain_size, clears):
return _outcome(false, "rejected", "resource_face", "")
# 事务内执行:首格 + 连锁新格揭示(BFS 规范序,零特判);事件 = O′ ∖ O
if was_sealed:
_seals.erase(c)
var reveal_order: Array[Variant] = _chain_reveal_order(c, eff_seals)
_reveal_chain(c, reveal_order, was_sealed)
var reveal_order: Array[Variant] = _chain_reveal_order(c, _seals)
_reveal_chain(c, reveal_order)
if action_economy.phase != action_economy.PHASE_ACTIVE:
_emit_round_end_seals_and_payload()
return _outcome(true, "executed", "", "")
@@ -337,8 +344,7 @@ func _do_dig(c: Vector2i) -> Dictionary:
## 连锁揭示域:首格+连锁新格揭示(BFS 规范序——cell_revealed 发射序即链序,整体随段搬迁)。
## 格面资源资格:chain/dig 恒 true——安全格与危险格一视同仁(危险格亦携带,
## 负向兑付归 战斗结算 极性路由);initial(封锁格)恒 false。资源本体与极性不在本系统。
## dug_under_seal = 首格带封印直挖标记(战斗结算 情况④兑付改判消费;连锁格恒 false——连锁不越封印)
func _reveal_chain(c: Vector2i, reveal_order: Array, was_sealed: bool) -> void:
func _reveal_chain(c: Vector2i, reveal_order: Array) -> void:
for rc in reveal_order:
if _revealed.has(rc):
continue # 闭包可穿过已开区——已开格零事件(cell_revealed × |O′ ∖ O|)
@@ -348,14 +354,12 @@ func _reveal_chain(c: Vector2i, reveal_order: Array, was_sealed: bool) -> void:
cell_revealed.emit({
"cell": rc, "kind": "mine", "digit": -1,
"variant_ids": [], "channel": channel, "resource": true,
"dug_under_seal": was_sealed and rc == c,
})
else:
_revealed[rc] = {"kind": "safe", "digit": _board.digit_of(rc)}
cell_revealed.emit({
"cell": rc, "kind": "safe", "digit": _board.digit_of(rc),
"variant_ids": _board.variant_ids(rc), "channel": channel, "resource": true,
"dug_under_seal": was_sealed and rc == c,
})
@@ -366,9 +370,10 @@ func _board_face_dig(c: Vector2i) -> String:
return "not_adjacent"
if is_revealed(c):
return "target_revealed"
if has_seal(c):
return "target_sealed" # 封印受理即揭示、不可再挖开(封印承诺 = 对该格的最终处置——改判路径已退役)
if _removed.has(c):
return "target_removed" # 中立移除格不可交互(拒绝先于消耗,pattern 1 board_face 腿)
# 封印不拦挖掘(封印不可逆):有封印格直接挖 = 对承诺的兑现,兑付改判归 战斗结算。
return ""
@@ -387,17 +392,14 @@ func fx_reveal(c: Vector2i) -> bool:
cell_revealed.emit({
"cell": rc, "kind": "safe", "digit": _board.digit_of(rc),
"variant_ids": _board.variant_ids(rc), "channel": "fx_reveal", "resource": true,
"dug_under_seal": false,
})
return true
## 挖一格→真实翻开集只读预览(sim 信息面出口——与 _classify 同口径:本格封印视为已隐式退役、
## 其余封印照挡;复用 _chain_closure 闭包;零状态写入、零事件发射)
## 挖一格→真实翻开集只读预览(sim 信息面出口——与 _classify 同口径:连锁不越封印、
## 真封印集口径;复用 _chain_closure 闭包;零状态写入、零事件发射)
func preview_reveal(c: Vector2i) -> Array[Vector2i]:
var sim_seals := _seals.duplicate()
sim_seals.erase(c)
var closure := _chain_closure(c, sim_seals)
var closure := _chain_closure(c, _seals)
var out: Array[Vector2i] = []
for cc in closure.keys():
if cc != c and not _revealed.has(cc):
@@ -436,34 +438,44 @@ func _chain_reveal_order(start: Vector2i, seal_set: Dictionary) -> Array:
func _cleared_after(chain: Dictionary) -> bool:
# 清图口径 = 已挖 ∪ 已封印(与 _safe_remaining 同源——私有共享口径单点,禁第三处重复实现)
for c in _board.safe_cells():
if not _revealed.has(c) and not chain.has(c):
if not _revealed.has(c) and not chain.has(c) and not _seals.has(c):
return false
return true
# ---- 封印 ----
func _do_seal_place(c: Vector2i) -> Dictionary:
var sub := ""
# 封印棋盘面合法性(board_face 拒绝情形序:目录外/不相邻/已开/已有封印/已移除——与挖掘合法性同构分域)
func _seal_face_reject(c: Vector2i) -> String:
if not _board.in_bounds(c):
sub = "out_of_catalog"
elif not (seal_range_free or _adjacent_to_hero(c)):
sub = "not_adjacent" # 信标网络装配时豁免(封印不限相邻——封印动作仍按标准价计费,规则放宽仅可达性)
elif is_revealed(c):
sub = "target_revealed"
elif has_seal(c):
sub = "seal_present"
elif _removed.has(c):
sub = "target_removed" # 中立移除格不可交互
return "out_of_catalog"
if not (seal_range_free or _adjacent_to_hero(c)):
return "not_adjacent" # 信标网络装配时豁免(封印不限相邻——封印动作仍按标准价计费,规则放宽仅可达性)
if is_revealed(c):
return "target_revealed"
if has_seal(c):
return "seal_present"
if _removed.has(c):
return "target_removed" # 中立移除格不可交互
return ""
func _do_seal_place(c: Vector2i) -> Dictionary:
var sub := _seal_face_reject(c)
if sub != "":
return _outcome(false, "rejected", "board_face", sub)
if not action_economy.try_spend("seal_place"):
return _outcome(false, "rejected", "resource_face", "")
_seals[c] = true # 封印动作零格子事件外发(发射面封闭;反馈经调用返回值命令式驱动)
if _cleared_after({}):
# 封印受理即揭示改版:封掉最后安全格 = 清图完成——轮末仲裁走 cleared(行动经济
# notify_cleared 独立上报口,与 request_dig 的 cleared 语义同源;同帧覆盖 ap_exhausted)
action_economy.notify_cleared()
if action_economy.phase != action_economy.PHASE_ACTIVE:
# 封印动作花完末点 AP 同样终结轮次(行动经济 三出口——AP 用完与动作种类无关;缺漏使 round_end
# 永不发出、战斗结算 的 Waiting 结算点永不到达 = 整局软锁)
# 永不发出、战斗结算 的 Waiting 结算点永不到达 = 整局软锁);清图收轮同此出口发判定批
_emit_round_end_seals_and_payload()
return _outcome(true, "executed", "", "")