From cc2d75d396a3230fe01f7416f045502d518387a2 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Thu, 1 Oct 2026 02:42:33 +0800 Subject: [PATCH] =?UTF-8?q?iter9-numfix-c:=20=E6=99=AE=E9=80=9A=E6=A1=A3?= =?UTF-8?q?=20v3.2-v3.4=20=E5=A4=B1=E8=AF=AF=E6=A8=A1=E5=9E=8B=E6=A0=A1?= =?UTF-8?q?=E5=87=86=EF=BC=88=E5=8D=A1=E6=AD=BB=E8=B5=8C=E6=8C=96+?= =?UTF-8?q?=E6=A6=82=E7=8E=87=E5=90=AF=E5=8F=91+=E7=A9=BA=E9=97=B2?= =?UTF-8?q?=E6=97=A9=E8=B5=8C=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - v3.2:卡死赌挖替代刷新/停手(用户实证口径「看不到数字也盲挖」;普通档不刷新=用户自报全程 未用刷新);choose_dig 行主序=概率最差格,实证 199/200 死第二战——退役 - gamble_pick(auto_player):概率启发选格=对邻接已证数字约束取(余雷/未知)比最小值——真人 「挑看起来最安全的一格」单步近似;纯查询面零 RNG 确定性保回放 - v3.3:标雷误挖 0.22→0.10(22% 全旗误挖=保底命中非真人失误形态,普通档 0/100 全灭实证) - v3.4:空闲提前赌挖 0.10(完美系把不确定性攒到残局 hazard→1=扫雷;真人早赌滚信息)+ _gamble_action 组装抽出 - 读数轨迹(N=100,种子 910003 起,route min):v3.3=0%(L1 2%)/v3.4=0%(L1 2%)—— 死因改判:非失误率而是 L1 四连战无治疗夹缝(雷伤+垫刀双压),表值刀归调参批 --- game/smoke/auto_player.gd | 42 ++++++++++++++++++++++++++++++++++ game/smoke/level_autoplay.gd | 44 +++++++++++++++++++++++++++++------- 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/game/smoke/auto_player.gd b/game/smoke/auto_player.gd index 97934a99..c790e2a7 100644 --- a/game/smoke/auto_player.gd +++ b/game/smoke/auto_player.gd @@ -208,3 +208,45 @@ 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 2026-10-01——模拟档位重构):概率启发近似真人「挑看起来最安全的一格」。 +# 危险度 = 该格对每个邻接已证数字约束取(余雷数/未知数)比值中的最小值(= 任一邻数字视角下的 +# 最低雷占比——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_flag(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 {} diff --git a/game/smoke/level_autoplay.gd b/game/smoke/level_autoplay.gd index e7d4d76b..6a3fb3ed 100644 --- a/game/smoke/level_autoplay.gd +++ b/game/smoke/level_autoplay.gd @@ -34,10 +34,14 @@ extends RefCounted const AutoPlayer := preload("res://smoke/auto_player.gd") # O4 收口:自动玩家策略单一实现 # ── 普通档 v3 失误注入参数(2026-10-01 用户规格:踩雷期望=完美+2/板、每板少挖几个正确格)── -# 标雷→误挖转化率:每板已证雷 ~9-14 颗 × p ≈ 2-3 次踩雷(目标完美+2/板±0.3——首测读数后校准此值) -const NORMAL_MINE_SLIP_P := 0.22 +# 标雷→误挖转化率:v3.2 实证 0.22 时普通档 0/100 全灭(首轮大量已证旗=保底命中,非真人失误 +# 形态——真人几乎不挖自己插旗的格);降 0.10 = 偶发真手滑,主踩雷源=卡死赌挖(不确定性概率命中) +const NORMAL_MINE_SLIP_P := 0.10 # 安全挖跳过率:每板安全格 ~70-90 × p ≈ 3-5 次漏挖(信息与推进双损失) const NORMAL_SKIP_DIG_P := 0.05 +# 空闲提前赌挖率(v3.4):已证安全格在手时小概率先赌一格——完美系把不确定性攒到残局 +# (残局 hazard→1=扫雷),真人早赌滚信息(低 hazard 期吃掉不确定性);p×~40 安全 AP/板 ≈ 4 次 +const NORMAL_IDLE_GAMBLE_P := 0.10 static func run_level(db, seed_val: int, level_index: int, atk_pct: int, hp_pct: int, collect_rows: bool = false, logger = null, policy: String = "normal", route: String = "min", template_pick: int = -1) -> Dictionary: @@ -376,7 +380,7 @@ static func _autoplay_battle(runner, settlement, st: Dictionary, logger) -> void action_guard -= 1 var act: Dictionary if bool(st.get("adv", false)): - act = _apply_normal_slips(AutoPlayer.perfect_action(settlement.minesweeper, settlement), st) # v3:完美+失误 + act = _apply_normal_slips(AutoPlayer.perfect_action(settlement.minesweeper, settlement), settlement, st) # v3:完美+失误 else: act = AutoPlayer.novice_action(settlement.minesweeper) # 单关机制夹具:v2 新手画像保持 var kind := String(act.get("kind", "stop")) @@ -405,12 +409,15 @@ static func _autoplay_battle(runner, settlement, st: Dictionary, logger) -> void logger.log_action("stop", Vector2i(-1, -1), _accepted(out_s), {"round": start_round}) -# 普通档 v3 失误注入(2026-10-01 用户规格):只在完美策略的两类动作上出手—— +# 普通档 v3 失误注入(2026-10-01 用户规格):三类—— # ①标雷误挖:把「标旗已证雷」动作直接转成「挖该格」(真人手滑原型:知道是雷、位置点错, # 踩雷吃伤且该雷奖励报废);期望踩雷增量 ≈ 每板已证雷数 × NORMAL_MINE_SLIP_P(目标完美+2/板) # ②安全挖跳过:把「挖已证安全格」动作转成本轮停手(少挖几个正确的雷——信息与推进双损失) +# ③卡死赌挖:完美档无已证动作时刷新/停手拖轮次吃敌方垫刀(实证=完美档 41% 通关全败于慢玩 +# 垫刀非踩雷)——真人会按概率赌一个边界格(用户实证口径「有些地方因为我看不到数字,也是盲挖 +# 的」);普通档不刷新(用户自报全程没用刷新),赌挖替代刷新/停手 # 失误流 = st.prng(策略流 RNG,种子派生自局种子——同种子逐位可回放;核心七流零消费零扰动) -static func _apply_normal_slips(act: Dictionary, st: Dictionary) -> Dictionary: +static func _apply_normal_slips(act: Dictionary, settlement, st: Dictionary) -> Dictionary: var prng: RandomNumberGenerator = st.get("prng") if prng == null: return act @@ -418,12 +425,33 @@ static func _apply_normal_slips(act: Dictionary, st: Dictionary) -> Dictionary: if kind == "flag" and prng.randf() < NORMAL_MINE_SLIP_P: st.mine_slips = int(st.get("mine_slips", 0)) + 1 return {"kind": "dig", "cell": act.get("cell")} - if kind == "dig" and prng.randf() < NORMAL_SKIP_DIG_P: - st.skip_digs = int(st.get("skip_digs", 0)) + 1 - return {"kind": "stop"} + if kind == "dig": + var roll := prng.randf() + if roll < NORMAL_SKIP_DIG_P: + st.skip_digs = int(st.get("skip_digs", 0)) + 1 + return {"kind": "stop"} + if roll < NORMAL_SKIP_DIG_P + NORMAL_IDLE_GAMBLE_P: + var idle := AutoPlayer.gamble_pick(settlement.minesweeper) + if not idle.is_empty(): + return _gamble_action(idle, settlement) + if kind == "refresh" or kind == "stop": + var plan := AutoPlayer.gamble_pick(settlement.minesweeper) # v3.2:概率启发赌挖(choose_dig 行主序=概率最差格,实证 199/200 死第二战——退役) + if not plan.is_empty(): + return _gamble_action(plan, settlement) return act +# 赌挖动作组装(v3.4 抽出):目标邻接=直接挖,否则先移到目标的已开邻格(下一拍再挖) +static func _gamble_action(plan: Dictionary, settlement) -> Dictionary: + var ms = settlement.minesweeper + var hero: Vector2i = ms.hero_pos() + var target: Vector2i = plan.dig_cell + var d := Vector2i(absi(target.x - hero.x), absi(target.y - hero.y)) + if maxi(d.x, d.y) == 1: + return {"kind": "dig", "cell": target} + return {"kind": "move", "cell": plan.hero_cell} + + # 完美策略档战斗驱动(2026-09-14):单步决策循环——move/dig/flag 经 排雷玩法 settle_batch 提交, # refresh 经 settlement.player_request_refresh(战斗结算 核心编排:行动经济 扣 RAP + 退役 + 当场换新板), # stop 出内环走统一停手提交。连续拒绝 ≥3 = 编排违约防御(fail-fast 语义,停手防死循环)。