# VERTICAL SLICE - NOT FOR PRODUCTION # 自动玩家策略集(headless 冒烟/扫描/转储共用的理性玩家近似): # choose_dig = 挖最近可挖格(先移到邻接已开格)。 # 无 window_selection(道具窗口确定性选择——道具窗口已下线)。 # 读取面:战斗结算.presentation_snapshot() / 排雷玩法 查询面(E1/E2 收口后的成文 API——禁直读核心状态)。 # 回归护栏:smoke_all 同种子回放断言(步骤流逐行相同)。 extends RefCounted # 行主序枚举未开格,找与已开集 8 邻接者;小人先移到该邻接已开格(BFS 可达)。无可挖 → {} static func choose_dig(minesweeper) -> Dictionary: var revealed: Array = minesweeper.revealed_cells() var revealed_set := {} for c in revealed: revealed_set[c] = true var board = minesweeper.current_board() for y in board.height: for x in board.width: var c := Vector2i(x, y) if minesweeper.is_revealed(c) or minesweeper.has_seal(c): continue for n in board.neighbors8(c): if revealed_set.has(n): var path: Array = minesweeper.compute_move_path(n) if not path.is_empty() or n == minesweeper.hero_pos(): return {"dig_cell": c, "hero_cell": n} return {} # ==================== 普通策略档 v2·基础定式画像 ==================== # 扫雷推理分级(经典定式文献口径):新手级 = 单数字满足规则——「数字 = 邻雷未开数 ⇒ 全是雷(满足即标)」 # 「数字 = 已封印数 ⇒ 余邻全安全(满足即开)」,教学一眼会、正常玩家两局即会的最低门槛判断; # 进阶级 = 联动定式(1-1 / 1-2 / 1-2-1 / 1-2-2-1 / 子集缩减)与概率分析——本切片归完美档 subset 推理承载, # 不入普通画像(新手不可靠掌握)。本档语义 = 只做新手级推理 + 无处可推时按既有口径盲挖边界格(新手冲动)+ # 不主动刷新(新手无刷新策略意识)。测量定位:教会基础规则后的真实玩家下限近似 # (乱点/最近格口径测的是无认知玩家下限,对关卡完成率带判读失真,不作普通画像)。 # 普通画像单步决策入口(每动作一查——挖掘揭示新数字后重推理):move/dig/seal/stop 四值(无 refresh)。 static func novice_action(minesweeper) -> Dictionary: var ded := _deduce_single(minesweeper) for c in ded["mines"]: if not minesweeper.is_revealed(c) and not minesweeper.has_seal(c): return _plan_on(minesweeper, c, "seal") for c in ded["safe"]: if not minesweeper.is_revealed(c) and not minesweeper.has_seal(c): return _plan_on(minesweeper, c, "dig") var plan := choose_dig(minesweeper) if plan.is_empty(): return {"kind": "stop"} return _plan_on(minesweeper, plan.dig_cell, "dig") # 新手级推理不动点:仅 single 规则(约束余雷=0⇒未知全安全 / 余雷=未知数⇒全雷),无 subset 联动—— # 满足规则连锁(标一雷使邻数字余雷清零)由 while-changed 不动点承载。 static func _deduce_single(minesweeper) -> Dictionary: var board = minesweeper.current_board() var safe := {} var mines := {} var changed := true while changed: changed = false for con in _constraints(minesweeper, board, mines): if int(con["mines"]) == 0: for c in con["unknown"].keys(): if not safe.has(c): safe[c] = true changed = true elif int(con["mines"]) == (con["unknown"] as Dictionary).size(): for c in con["unknown"].keys(): if not mines.has(c): mines[c] = true changed = true return {"safe": _row_major(safe), "mines": _row_major(mines)} # ==================== 完美策略档(OQ7 技能轴上界测量仪——只作测量,非玩法数值) ==================== # 策略语义:零盲挖(只挖已证安全格 ⇒ 构造保证零踩雷)· 已证危险格全封印(封印先于挖——封印 = 纯收益 # 〔轮末 weaken + 魔法 首次充能〕且雷上封印不阻连锁)· 卡死且刷新资源可用 → 轮内刷新 # (经 settlement.player_request_refresh 核心编排)· 否则停手(残 AP 1:1 转存 RAP——下轮刷新资源)。 # 推理规则集 = 棋盘生成 F1 measure_inferability 同族(single + subset——solver_rules=single_subset 冻结域); # 口径差异 = unknown 集不含 棋盘生成 生成期 safe_set oracle(真实玩家约束的未知邻格含危险格)——比生成期闸 # 保守,卡死率略高属口径诚实。输入只经 排雷玩法 查询面(reveal_record/is_revealed/has_seal/current_board # 的邻居与可达面),禁读棋盘雷区真值(测推理上界,非全知作弊)。 # 待拍板(未实现):非卡死主动刷新、囤刀倾泻 vs 即用即耗、完美板目标加权。 # 单步决策入口(每动作一查——挖掘揭示新数字后重推理):move/dig/seal/refresh/stop 五值。 static func perfect_action(minesweeper, settlement) -> Dictionary: var ded := _deduce(minesweeper) for c in ded["mines"]: if not minesweeper.is_revealed(c) and not minesweeper.has_seal(c): return _plan_on(minesweeper, c, "seal") for c in ded["safe"]: if not minesweeper.is_revealed(c) and not minesweeper.has_seal(c): return _plan_on(minesweeper, c, "dig") if settlement != null and bool(settlement.can_refresh()): return {"kind": "refresh"} return {"kind": "stop"} # 目标格需英雄 8 邻——不可达先移到目标的已开 8 邻格(行主序首个可达者;同 choose_dig 寻路口径) static func _plan_on(minesweeper, c: Vector2i, kind: String) -> Dictionary: var d := Vector2i(absi(c.x - minesweeper.hero_pos().x), absi(c.y - minesweeper.hero_pos().y)) if maxi(d.x, d.y) == 1: return {"kind": kind, "cell": c} var board = minesweeper.current_board() for n in board.neighbors8(c): if minesweeper.is_revealed(n) and (n == minesweeper.hero_pos() or not minesweeper.compute_move_path(n).is_empty()): return {"kind": "move", "cell": n} return {"kind": "stop"} # 不可达(理论不可达——已证格必邻接已开区);防御性停手防死循环 # 推理不动点:single(约束余雷=0⇒未知全安全 / 余雷=未知数⇒全雷)+ subset(U_a ⊂ U_b ⇒ 差集约束)。 # 约束只依赖 revealed/seals/mines 推断集——mines 集增长才需重建约束(safe 标记不改正约约束)。 static func _deduce(minesweeper) -> Dictionary: var board = minesweeper.current_board() var safe := {} var mines := {} var changed := true while changed: changed = false var cons := _constraints(minesweeper, board, mines) for con in cons: if int(con["mines"]) == 0: for c in con["unknown"].keys(): if not safe.has(c): safe[c] = true changed = true elif int(con["mines"]) == (con["unknown"] as Dictionary).size(): for c in con["unknown"].keys(): if not mines.has(c): mines[c] = true changed = true var cons2 := _constraints(minesweeper, board, mines) for i in cons2.size(): for j in range(i + 1, cons2.size()): var der := _subset_derived(cons2[i], cons2[j]) if der.is_empty(): continue if int(der["mines"]) == 0: for c in der["unknown"].keys(): if not safe.has(c): safe[c] = true changed = true elif int(der["mines"]) == (der["unknown"] as Dictionary).size(): for c in der["unknown"].keys(): if not mines.has(c): mines[c] = true changed = true # 0 格连锁算子(架构整改批补齐——与棋盘生成 _apply_chain_rule 对位):safe 集中未揭示格 # 挖开必触发 0 格连锁(连锁只经安全格扩散=闭包集全安全,真实玩家挖 c 免费获得的信息面), # 经 preview_reveal 真实闭包取新开格集并入 safe——双口径显式登记:unknown 不含 safe_set oracle # 〔口径差异=board_generator 版含 oracle,本版 no-peek——:80-83 注释在账〕 for c in (safe.keys() as Array).duplicate(): if board.is_safe(c) and not minesweeper.is_revealed(c): for rc in minesweeper.preview_reveal(c): if not safe.has(rc): safe[rc] = true changed = true return {"safe": _row_major(safe), "mines": _row_major(mines)} # 约束集 = 已开数字格:mines_left = digit − 已知雷封印(封印位 + 已证危险格),unknown = 未开无封印 8 邻。 # 已爆危险格(kind=mine)不带数字约束——其贡献已含在邻格 digit 内(数字恒真不变式 inv3)。 static func _constraints(minesweeper, board, mines: Dictionary) -> Array: var out: Array = [] for c in minesweeper.revealed_cells(): var rec: Dictionary = minesweeper.reveal_record(c) if String(rec.get("kind", "")) != "safe": continue var d := int(rec.get("digit", -1)) if d < 0: continue var unknown := {} var known := 0 for n in board.neighbors8(c): if minesweeper.has_seal(n): known += 1 elif not minesweeper.is_revealed(n): if mines.has(n): known += 1 else: unknown[n] = true var d_eff := d - known if unknown.is_empty() or d_eff < 0: continue # 零未知 = 无信息;负余雷 = 矛盾态(非法板/误封印)——不产约束 out.append({"mines": d_eff, "unknown": unknown}) return out # pair 差集约束:U_a ⊂ U_b ⇒ {unknown = U_b∖U_a, mines = mines_b−mines_a}(互不包含/相等集 → 空) static func _subset_derived(ca: Dictionary, cb: Dictionary) -> Dictionary: var res := _subset_one_way(ca, cb) if not res.is_empty(): return res return _subset_one_way(cb, ca) static func _subset_one_way(ca: Dictionary, cb: Dictionary) -> Dictionary: var ua: Dictionary = ca["unknown"] var ub: Dictionary = cb["unknown"] if ua.is_empty() or ub.is_empty() or ua.size() >= ub.size(): return {} for k in ua.keys(): if not ub.has(k): return {} var diff := {} for k in ub.keys(): if not ua.has(k): diff[k] = true var dm := int(cb["mines"]) - int(ca["mines"]) if dm < 0: return {} # 矛盾态防御(完美档无误封印——理论不可达) return {"unknown": diff, "mines": dm} static func _row_major(cells: Dictionary) -> Array: var out: Array = cells.keys() out.sort_custom(func(a, b): return a.y * 1000 + a.x < b.y * 1000 + b.x) return out # 卡死赌挖选格(普通档 v3.1):概率启发近似真人「挑看起来最安全的一格」。 # 危险度 = 该格对每个邻接已证数字约束取(余雷数/未知数)比值中的最小值(= 任一邻数字视角下的 # 最低雷占比——single 已证安全格在进入本函数前已被 perfect_action 消化,故卡死态比值恒 >0); # 同危险度行主序先到先得。纯查询面零 RNG(确定性保回放);寻路同 choose_dig(目标已开 8 邻 # 首个可达者为 hero_cell)。 static func gamble_pick(minesweeper) -> Dictionary: var cons := _constraints(minesweeper, minesweeper.current_board(), {}) var best_h := 2.0 var best := Vector2i(-1, -1) var revealed: Array = minesweeper.revealed_cells() var revealed_set := {} for c in revealed: revealed_set[c] = true var board = minesweeper.current_board() for y in board.height: for x in board.width: var c := Vector2i(x, y) if minesweeper.is_revealed(c) or minesweeper.has_seal(c): continue var adj := false for n in board.neighbors8(c): if revealed_set.has(n): adj = true break if not adj: continue var h := 1.0 for con in cons: if (con["unknown"] as Dictionary).has(c): var ratio := float(con["mines"]) / float((con["unknown"] as Dictionary).size()) h = minf(h, ratio) if h < best_h - 0.0001: best_h = h best = c if best.x < 0: return {} for n in board.neighbors8(best): if minesweeper.is_revealed(n) and (n == minesweeper.hero_pos() or not minesweeper.compute_move_path(n).is_empty()): return {"dig_cell": best, "hero_cell": n} return {}