From bba962a5d3403f216e1dae20ed05c8c1781e1a68 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Sun, 4 Oct 2026 02:19:20 +0800 Subject: [PATCH] =?UTF-8?q?T7=E6=89=B9=E6=AC=A1=E4=B8=80MC=E9=94=9A+?= =?UTF-8?q?=E7=BA=A2=E7=BA=BF=E8=87=AA=E6=A3=80=E2=91=A0+=E5=87=BA?= =?UTF-8?q?=E5=8F=A3=E6=9C=BA=E5=99=A8=E9=97=B8=EF=BC=88=E8=BF=AD=E4=BB=A3?= =?UTF-8?q?12=E6=89=B9=E6=AC=A1=E4=B8=80=E5=87=BA=E5=8F=A3=E2=80=94?= =?UTF-8?q?=E2=80=94R3=E4=B9=99=E5=8D=95=E9=94=9A=E5=85=A8=E9=87=8F?= =?UTF-8?q?=EF=BC=89=EF=BC=9AMC=E7=AC=AC7=E5=8F=82relic=E5=88=86=E5=B1=82?= =?UTF-8?q?=EF=BC=88off=3D=E5=9F=BA=E7=BA=BF/formed=3D=E6=88=90=E5=9E=8B?= =?UTF-8?q?=E7=94=BB=E5=83=8F=E5=BC=80=E5=B1=80=E6=B3=A8=E5=85=A5=E4=B8=83?= =?UTF-8?q?=E6=B5=81=E6=B4=BE=E5=90=842=E4=BB=B6=E6=A1=A3=E4=B8=80?= =?UTF-8?q?=E6=88=90=E5=9E=8B=E9=9B=86=E2=80=94=E2=80=94params.relics=5Fse?= =?UTF-8?q?ed=E6=B5=8B=E9=87=8F=E9=9D=A2=E4=B8=93=E7=94=A8=E6=B3=A8?= =?UTF-8?q?=E5=85=A5=E7=9C=9F=E5=AE=9E=E5=B1=80=E6=B5=81=E6=B4=BE=E4=BB=B6?= =?UTF-8?q?=E5=8F=AA=E8=B5=B0=E5=B1=80=E5=86=85=E9=9A=8F=E6=9C=BA=E6=B8=A0?= =?UTF-8?q?=E9=81=93=E7=BA=A2=E7=BA=BF=E2=91=A1=E4=B8=8D=E7=A0=B4+?= =?UTF-8?q?=E5=80=99=E9=80=89relic=E4=BC=98=E5=85=88=E6=8D=95=E6=8D=9E?= =?UTF-8?q?=EF=BC=89=EF=BC=9B=E9=94=9A=E4=B8=A4=E4=BB=B6off/formed?= =?UTF-8?q?=E9=85=8D=E5=AF=B9N=3D20=E5=90=8Cseed0=EF=BC=88=E6=9C=AC?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E7=82=B9=E6=80=BB=E9=87=8F40=E8=B4=B4?= =?UTF-8?q?=E8=BE=B9=E4=BA=8C=E6=A1=A3=E5=90=88=E8=A7=84=EF=BC=89=EF=BC=9B?= =?UTF-8?q?=E5=88=86=E5=B2=94=E8=AF=BB=E6=95=B0=3D=E5=85=A8=E9=80=9A0?= =?UTF-8?q?=E2=86=9210%/L1=205=E2=86=9275%/L2=200=E2=86=9220%=E6=96=B9?= =?UTF-8?q?=E5=90=91=E6=88=90=E7=AB=8B=EF=BC=9B=E7=BA=A2=E7=BA=BF=E8=87=AA?= =?UTF-8?q?=E6=A3=80=E2=91=A0=E6=96=B9=E5=90=91=E8=BF=87=EF=BC=88=E6=AF=8F?= =?UTF-8?q?=E8=BD=AE=E8=B8=A9=E9=9B=B7=E7=8E=87+8%=3D=E5=BC=80=E6=A0=BC?= =?UTF-8?q?=E6=B5=81=E4=BB=B6=E8=AE=BE=E8=AE=A1=E5=86=85=E4=BB=A3=E4=BB=B7?= =?UTF-8?q?=E9=9D=9E=E6=8E=A8=E7=90=86=E8=BE=B9=E7=BC=98=E5=8C=96=C2=B7?= =?UTF-8?q?=E5=BC=80=E6=A0=BC=E9=9D=A2=E6=B4=BB=E6=9B=B4=E4=B9=85=E6=8C=96?= =?UTF-8?q?=E6=9B=B4=E5=A4=9A=E4=B8=8D=E5=A1=8C=E9=99=B7=EF=BC=89=EF=BC=9B?= =?UTF-8?q?=E8=87=AA=E7=84=B6=E5=B1=80=E6=8E=A2=E9=92=88=E8=B7=91=E4=BD=9C?= =?UTF-8?q?=E5=BA=9F=E4=B8=8D=E7=95=99=E9=94=9A=EF=BC=88=E5=80=99=E9=80=89?= =?UTF-8?q?relic=E7=8E=8721.4%=E2=89=88=E6=B1=A0=E5=8D=A0=E6=AF=9421.6%?= =?UTF-8?q?=E4=BB=A4=E7=89=8C=E7=AD=89=E6=A6=82=E7=8E=87=E4=BD=90=E8=AF=81?= =?UTF-8?q?+=E7=99=BD=E6=9D=BF=E5=B1=80=E5=88=B0=E4=B8=8D=E4=BA=86?= =?UTF-8?q?=E6=88=90=E5=9E=8B=3D=E6=88=90=E5=9E=8B=E6=80=81=E8=AF=BB?= =?UTF-8?q?=E6=95=B0=E9=A1=BB=E6=B3=A8=E5=85=A5=E7=94=BB=E5=83=8F=E5=8F=A3?= =?UTF-8?q?=E5=BE=84=E8=A3=81=E5=AE=9A=E7=95=99=E7=97=95=EF=BC=89=EF=BC=9B?= =?UTF-8?q?=E8=AF=BB=E6=95=B0=E6=8A=A5=E5=91=8Adesign/qa=EF=BC=88provision?= =?UTF-8?q?al=E6=8A=AB=E9=9C=B2=E5=85=A8=E7=9B=AE=E9=9A=8F=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=EF=BC=89=EF=BC=9B=E5=87=BA=E5=8F=A3=E6=9C=BA=E5=99=A8?= =?UTF-8?q?=E9=97=B8=3D=E5=86=92=E7=83=9F696/0+run=5Fall=2015+=E6=8E=A2?= =?UTF-8?q?=E9=92=88=E4=BA=94=E7=BB=BFclick=E5=AD=98=E9=87=8F=E7=BA=A2?= =?UTF-8?q?=E9=9B=B6=E6=96=B0=E5=A2=9E+doc=E4=B8=89=E9=97=B8=E5=9F=BA?= =?UTF-8?q?=E7=BA=BF=E5=90=8C=E6=AC=BE+=E7=86=B5=E5=A2=9EPASS=E2=80=94?= =?UTF-8?q?=E2=80=9412/12b=E5=88=87=E5=88=86=E5=88=A4=E5=AE=9A=E7=82=B9=3D?= =?UTF-8?q?=E9=A2=84=E8=A3=81=E5=85=A8=E7=BB=BF=E8=87=AA=E5=8A=A8=E7=BB=AD?= =?UTF-8?q?=E6=89=B9=E6=AC=A1=E4=BA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../m4iter12-t7-batch1-readings-2026-10-04.md | 35 +++++++++++++++++++ game/core/adventure_runner.gd | 7 ++++ game/smoke/adventure_monte_carlo.gd | 15 +++++++- game/smoke/level_autoplay.gd | 27 ++++++++++---- 4 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 design/qa/m4iter12-t7-batch1-readings-2026-10-04.md diff --git a/design/qa/m4iter12-t7-batch1-readings-2026-10-04.md b/design/qa/m4iter12-t7-batch1-readings-2026-10-04.md new file mode 100644 index 00000000..1cce0d9f --- /dev/null +++ b/design/qa/m4iter12-t7-batch1-readings-2026-10-04.md @@ -0,0 +1,35 @@ +# 迭代 12 批次一 T7 MC 读数报告(2026-10-04) + +- 锚件:`runsum_m4iter12_t7off_n20_20261004.jsonl`(基线)/ `runsum_m4iter12_t7formed_n20_20261004.jsonl`(成型画像)——N=20/锚,seed0=90000 同种子配对,route=min,normal 档,attune=off,shop=legacy +- 采集档位:**二档·数值修改验证**(本触发点总量 40=每锚 20×2 贴边合规;会话累计 MC 分钟 ~6 ≤20) +- 成型画像口径:开局注入七流派各 2 件(14 件=档一成型集,`params.relics_seed` 测量面专用注入——真实局流派件只走局内随机渠道,红线②层级纪律不破);基线=零注入。锚内 autoplay=316-f(最稀有/最左)+候选 relic 优先(on 面) + +## 分岔读数(R3 乙单锚全量——一锚覆盖七流派) + +| 指标 | 基线(off) | 成型(formed) | 判读 | +|---|---|---|---| +| normal 全通率 | 0.0% | **10.0%** | 分岔方向成立 ✓ | +| L1 通关率 | 5.0% | **75.0%** | 流派成型显著变强(+70pp ≥40pp 可判阈值)✓ | +| L2 通关率 | 0.0% | **20.0%** | 方向成立 ✓ | +| 局均轮数 | 12.85 | 22.6 | 活得更久(battles 3.7→8.0)——非拖局 | +| 局均踩雷 | 5.9 | 11.2 | 随局时长自然增长;**每轮踩雷率 0.459→0.496(+8%)**=开格流件代价兑现(贪婪矿灯负向 +25% 惊动在画像内),非推理边缘化 | +| 局均承伤 | 23.4 | 45.5 | 同上随局时长 | + +## 红线自检①(硬断言方向面——阈值 provisional 归数值批) + +**「成型态场均安全开格数/旗占比不得塌陷」方向判定:过。** +- 开格面:成型局轮均挖格上界(rounds×AP/轮)不降反升(活更久挖更多)——流派件未使挖格降格为次要变量; +- 扫雷推理主因面:每轮踩雷率 +8% 系开格流派件设计内代价(正负双修兑现),非「按数字推理」被绕过;旗占比由 autoplay 固定策略承载,两态同构; +- 档位激活确定性:同集合同档位断言在冒烟(T1 引擎断言+T4 四流派档二断言)——MC 逐位可复放由同种子配对承载 ✓。 + +## 自然局捕捞率佐证(候选面) + +成型锚跑前曾跑自然局 on 面探针跑(20 局,**未留锚作废**):候选 relic 出现率 21.4% ≈ 池占比 27/125=21.6%(完美吻合——令牌级抽签等概率承载);normal 白板局死 L1 早期、三选一停点稀少(候选卡仅 28 张/20 局)——自然局到不了成型=成型态读数必须走注入画像(本锚口径)。 + +## 现行锚声明 + +本批两锚(off/formed 配对)=**批次一出口现行锚**。TP0 锚(runsum_m4iter12_tp0_n20)随池换代(98→125 张)作废只测不判。批次二 T10 树 A/B 面读数=下一触发点(树切面读数另跑)。 + +## provisional 披露(T2-T6 随批代拍板全目——人工检查清单母本) + +「每关首次开格」→每战首次(魔能共鸣)/贯穿射击半额弹射(原溢出追踪单分量模型不可行)/猎巢人全体附加值(原对其中敌)/战旗军规 default_target(原旗所在巢穴敌)/连珠矢段值固定 2 不乘 atk/深挖钻头每 4 格深层产出对价 +1 AP/商店保底 devour 广义映射+基价 15 固定锚/整备军备停手侧转蓄力(cleared 分支维持作废)/矿脉直觉连击 +10%/格上限 5/greedy 族同 hook 累加/锚点件双效果承载(共鸣 rider 提升以奥术矢+档位承载)。 diff --git a/game/core/adventure_runner.gd b/game/core/adventure_runner.gd index 4e96d8f8..a9f9f49e 100644 --- a/game/core/adventure_runner.gd +++ b/game/core/adventure_runner.gd @@ -127,6 +127,13 @@ func start_adventure(seed_val: int, params: Dictionary = {}) -> bool: _attunement = params.get("attunement", []) if params.get("attunement", []) is Array else [] # additive(迭代 11 第①批 T5) _attune_refresh_y = maxi(0, int(params.get("attune_refresh_y", 0))) # additive(迭代 11 第①批 T5b——Y 冒险周期池) _seed_attune_charges() # 迭代 11 第①批 T6——冒险开始充能账本按装备主动件重置(每冒险充能语义) + # 流派件开局注入(迭代 12 T7——MC 成型画像载体:params.relics_seed 非空=开局持有集〔七流派 + # 各 2 件档一成型——测量面专用;真实局流派件只走局内随机渠道,红线②层级纪律不破〕) + var relic_seed: Array = params.get("relics_seed", []) if params.get("relics_seed", []) is Array else [] + if not relic_seed.is_empty(): + _relics = [] + for r in relic_seed: + _relics.append(String(r)) ledger["hero_hp"] = effective_max_hp() # 生命账本置满(生效上限口径——T17 修复:含 TunePanel pct,与战斗内同源) state = "running" return _start_current_level() diff --git a/game/smoke/adventure_monte_carlo.gd b/game/smoke/adventure_monte_carlo.gd index c6bdc09b..83f7ca42 100644 --- a/game/smoke/adventure_monte_carlo.gd +++ b/game/smoke/adventure_monte_carlo.gd @@ -42,6 +42,18 @@ func _initialize() -> void: var shop_mode := "off" if args.size() >= 6 and args[5] in ["off", "on"]: shop_mode = args[5] + # 流派件分层开关(迭代 12 T7——off=基线;on=成型画像开局注入七流派各 2 件档一成型集 + # 〔测量面专用 params.relics_seed——真实局流派件只走局内随机渠道〕+候选 relic 优先捕捞) + var relic_mode := "off" + if args.size() >= 7 and args[6] in ["off", "formed"]: + relic_mode = args[6] + var relic_profile := {} + if relic_mode == "formed": + relic_profile = {"mode": "on", "seed_ids": [ + "arcane_ember", "mana_weaver", "pierce_shot", "chain_arrows", + "miner_stubborn", "miner_hammer", "greedy_lantern", "vein_intuition", + "mine_atlas", "nest_hunter", "armory_prep", "iron_wall", + "ley_harvest", "thorn_weaver"]} var db = load("res://core/config_db.gd").new() if not db.load_tables("res://data/tables", false): print("表加载失败") @@ -86,11 +98,12 @@ func _initialize() -> void: var seed_val := seed0 + _run_cursor _run_cursor += 1 var st = autoplay.run_adventure(db, seed_val, 0, 0, false, logger, policy, route, {}, attune_profile, - {"mode": "pool"} if shop_mode == "on" else {}) + {"mode": "pool"} if shop_mode == "on" else {}, relic_profile) var sum: Dictionary = st["summary"] var rec := { "policy": policy, "seed": seed_val, "atk_pct": 0, "hp_pct": 0, "route": route, "attune": attune_mode, "equipped": attune_profile.get("equipped", []), + "relic_mode": relic_mode, "result": st["result"], "cleared": bool(sum.get("cleared", false)), "levels_cleared": int(sum.get("levels_cleared", -1)), diff --git a/game/smoke/level_autoplay.gd b/game/smoke/level_autoplay.gd index 84a3695f..a1773bc5 100644 --- a/game/smoke/level_autoplay.gd +++ b/game/smoke/level_autoplay.gd @@ -114,7 +114,8 @@ static func _drive_level(runner, st: Dictionary, logger, policy: String, route: # (last_battle_enemies——末场战斗敌方显示名序列,失败局即死亡遭遇)。 static func run_adventure(db, seed_val: int, atk_pct: int, hp_pct: int, collect_rows: bool = false, logger = null, policy: String = "normal", route: String = "min", - hero_override: Dictionary = {}, attune: Dictionary = {}, shop: Dictionary = {}) -> Dictionary: + hero_override: Dictionary = {}, attune: Dictionary = {}, shop: Dictionary = {}, + relic: Dictionary = {}) -> Dictionary: var own_session := logger == null if own_session: logger = load("res://presentation/playtest_logger.gd").new() @@ -133,6 +134,7 @@ static func run_adventure(db, seed_val: int, atk_pct: int, hp_pct: int, collect_ "log_path": "", "policy": policy, "refreshes": 0, "mine_hits": 0, "mine_dmg": 0, "visited_ids": [], "choices": 0, "last_battle_enemies": [], "summary": {}, "wallet_events": [], "encounter_max_hp_seq": [], "reward_events": [], + "relic_mode": String(relic.get("mode", "off")), # 流派件分层开关(迭代 12 T7——off=316-f 通用竞争/on=成型画像优先捕捞 relic) "shop_gp_seq": [], # 商店房到达余额序(逐关 1 次——GP 跨关延续验收取数面) "prng": _policy_rng(seed_val), "mine_slips": 0, "skip_digs": 0, "shop_buys": 0, "adv": true, # v3 档位行为开关(选路评分/失误注入/商店购买只挂冒险测量面——单关夹具保持旧语义零扰动) @@ -154,6 +156,9 @@ static func run_adventure(db, seed_val: int, atk_pct: int, hp_pct: int, collect_ if not attune.is_empty(): adv_params["attunement"] = attune.get("equipped", []) adv_params["attune_refresh_y"] = int(attune.get("refresh_y", 0)) + # 流派件注入(迭代 12 T7——formed 画像=开局注入七流派各 2 件档一成型集;测量面专用) + if relic.has("seed_ids"): + adv_params["relics_seed"] = relic.get("seed_ids", []) # 商店分层注入(迭代 11 第②批 T5——缺省 legacy 与既有调用方逐位一致零行为变更;pool=共享池货架面) var pool_shop: bool = String(shop.get("mode", "")) == "pool" if pool_shop: @@ -214,7 +219,7 @@ static func _on_adventure_node_entered(node: Dictionary, info: Dictionary, adr, r.continue_after_node() # 牌库空合法态直通(池耗即枯竭——防御保留) else: adr.present_node_bonus(ncands, ndraw.get("aux_detail", []) as Array) - var npick: Dictionary = _pick_token(ncands) + var npick: Dictionary = _pick_token(ncands, String(st.get("relic_mode", "off"))) if adr.apply_node_bonus(String(npick.get("kind", "")), String(npick.get("tier", "")), String(npick.get("relic_id", ""))): if not adr._node_bonus_pending.is_empty() and adr._node_bonus_pending.has("incoming"): adr.resolve_bonus_replace(-1) # 满位替换:放弃新件 @@ -252,17 +257,27 @@ static func _on_adventure_bonus(payload: Dictionary, st: Dictionary, adr) -> voi if candidates.is_empty(): st.result = "advance_failed" return - var pick: Dictionary = _pick_token(candidates) + var pick: Dictionary = _pick_token(candidates, String(st.get("relic_mode", "off"))) if not adr.pick_bonus(String(pick.get("kind", "")), String(pick.get("tier", "")), String(pick.get("relic_id", ""))): st.result = "advance_failed" # 三选一确定性选件(TP0 316-f——1 级优先度=最稀有〔档位最高 rarest>rare>common〕、2 级=最左; -# 候选并列同档取最左=稳定序保证。空候选/未知档位 = 回退最左防御口径) -static func _pick_token(candidates: Array) -> Dictionary: +# 候选并列同档取最左=稳定序保证。空候选/未知档位 = 回退最左防御口径。 +# 成型画像(迭代 12 T7——relic 模式 on:候选含 relic 令牌时优先在 relic 集内按 316-f 择一 +# 〔贪心捕捞→流派件入局→成型画像全流派覆盖——R3 乙单锚承载〕;off=relic 与数值件同规则竞争) +static func _pick_token(candidates: Array, relic_mode: String = "off") -> Dictionary: + var pool: Array = candidates + if relic_mode == "on": + var relics_only: Array = [] + for tok in candidates: + if String((tok as Dictionary).get("kind", "")) == "relic": + relics_only.append(tok) + if not relics_only.is_empty(): + pool = relics_only var best: Dictionary = {} var best_rank := -1 - for tok in candidates: + for tok in pool: var t: Dictionary = tok as Dictionary var rank := 0 match String(t.get("tier", "")):