コンテンツにスキップ

raging_bolt_ogerpon トレース診断と 3 つの構造バグ修正 (2026-07-10)

前日のチューニングラウンド は 「80 trial 探索しても現行 params を超えない、全 config 勝率 2〜8%」で終わり、 ボトルネックがパラメータでなくロジックにあることを示した。本ノートは その敗因診断 (pca.analysis behavior + records の behavior trace) と修正の記録。

診断手順

  1. raging_bolt vs dragapult の 2 体 pool で 60 games、records JSONL を収集
  2. python -m pca.analysis behavior で行動サマリ + readable game logs
  3. records の meta.legal_option_trace / selected_option_trace / board_trace を集計 (提示された攻撃 vs 選択された攻撃、リトリート機会、手張り先の型)

発見した構造バグ (重症度順)

1. ワザ ID が全て None (言語ミスマッチ)

_find_attack_id("きょくらいごう") は ALL_ATTACKS を日本語名で全走査するが、 エンジンのワザ名は英語 ("Bellowing Thunder")。11 個の A_* 定数が全て None に落ち、動的ダメージ計算が死んでいた:

  • Bellowing Thunder は静的 damage=0 (可変打点) → score_attack が常に effect_only (500) → PLAY (1000〜22000) に常敗 → フィニッシャーが 60 games で 0 回発射 (提示は 49 回あった)
  • 修正: カード自身のワザリストからコスト長で構造的に解決 (_attack_of)、 名前は日英両対応のフォールバック。回帰テスト (AttackIdResolutionTest) で全定数の解決を lock

2. score_retreat がほぼ全ての合法リトリートをブロック

エネ準備完了の Bolt がベンチにいる状態で RETREAT 合法 106 回中、実行 15 回。 原因は heuristics.proactive_retreat_scoreenergy_count(active) < retreat_cost → Noneエンジンが合法と判定済みの オプション (Latias スカイラインの無料逃げ含む) を手動再チェックで却下していた。 heuristics は凍結共有のため触らず、raging_bolt の score_retreat を自前化:

  • KO ロック (retreat.lock_ko 25000) / {L}{F} 準備完了 Bolt の昇格 (retreat.promote_ready_bolt 21000、型チェック付き) / 被弾フォールバック
  • 教訓: 合法オプションのスコアリングでコスト・合法性を再チェックしない (エンジンが既に保証している)

3. 手張り・エネサーチが Grass 偏重で {L}{F} が揃わない

Bolt ex への手張り実績: Grass 60 / Lightning 35 / Fighting 17。 Bellowing Thunder のコストは {L}{F} 固定なのに、score_attach は Lightning +5000 / Grass +2500 / Fighting +0 だった。さらに Crispin (アカマツ) のエネ選択も全型フラット 13000 で Grass が先頭優先。

  • 修正: 「欠けているコスト型を埋める手張り」を最優先 (attach_energy.cost_complete_bonus 8000)、Crispin 選択もコスト完成型を 15000 に昇格
  • 併せて attach_energy.* params が main.py 未参照だった乖離を解消 (tuning 空間がこの 3 キーを回しても無効だった — SKILL の「よくあるミス」そのもの)

結果 (A/B、同一 seed)

指標 (vs dragapult 22 games) 修正前 修正後 (3 つ込み)
勝率 0% 9%
Bellowing Thunder 発射 0 回 8/22 games
攻撃回数/game 1.68 2.50

6 体プール 400 games (tuning と同一相手、seed 123): 完走勝率 6.9% → 12% (raging_bolt は 51 games しか出ないため ±7% 級の ノイズ、有意差は主張しない)、攻撃 4.06 回/game。ミラーは山切れ 7/8 → 攻撃 14 回/game の実ゲーム化。

追記 (2026-07-11): Bellowing 非 KO ガード

上記修正で Bellowing が撃てるようになった副作用として、KO に届かない Bellowing (例 280 vs 320HP) がエネを全捨てして発射される局面が現れた。 tier システムは生ダメージ値しか見ないため表現できない劣位手。score_attack に ガードを追加:

  • 非 KO の Bellowing は attack.bellowing.non_ko_skip_score (-500, END より下) → 次ターンの KO まで貯める
  • 次ターン Active を失う (combat.opponent_can_ko_me + ベンチ KO 不在) 時のみ desperation_score (6000) でチップ発射を許可 (エネはどうせ失うため)

A/B (同一 2 体 60g seed 0): vs dragapult 勝率は 9% → 5% (22 games、 ±10% でノイズ範囲)。ガードは劣位手を除去する正しい修正だが、勝率の ボトルネックはセットアップ速度であり単独では動かない (下記残課題 1)。 tier を狭めて非 KO 発射を抑えた前回 tuning best.yaml とも整合するため、 ガードをコードで恒久化し params 化して tuning に委ねた。

追記 (2026-07-11): 0 打点 Bellowing ループ (unfinished の主因)

トレース (pca.analysis behavior + records) で raging_bolt ミラーが 50-70 ターンの無限ループになり max-steps 引分になる原因を特定:

  1. score_attack は Bellowing の打点を「場の基本エネを全捨て」前提 (70×6=420) で計算 → KO 圏と判断し ATTACK を選択
  2. しかし直後の SelectContext.DISCARD_ENERGY_CARD (どのエネを捨てるか) は 汎用 score_discard に流れ、エネは「keep = -2000」で 0 枚選択
  3. rank_options は score<0 を min_count まで捨てるため 0 枚 → 実打点 70×0 = 0 → 相手 HP 満タンのまま毎ターン同じ → max-steps ループ

score_attack の全捨て前提と discard 選択の温存が矛盾していた。 score_bellowing_discard を追加し DISCARD_ENERGY_CARD では全捨て (attack.bellowing.discard_score = 20000, rank_options が全選択) にして 前提と一致させた。このデッキの DISCARD_ENERGY_CARD は Bellowing のコスト 専用 (相手強制の捨札は相手の選択) なので安全。

検証: ミラー 40g で DISCARD_ENERGY_CARD 73/73 でエネ選択 (修正前 0)、 最長ターン 32 に収束 (修正前 47/53/69)。6 体プールで raging_bolt の unfinished は 実相手 5 種すべて 0、残るのは vs iono のみ (iono は凍結・自身のミラーで 21/22 停滞 = iono 側の問題、範囲外)。

教訓: 動的打点ワザは「打つ判断」と「コスト支払いの選択」が別コンテキストに 分かれる。両者の前提を一致させないと 0 打点空撃ちになる (attack を選ぶ score と discard/number を選ぶ score の整合性を必ず確認)。

追記 (2026-07-11): セットアップ速度の調査結論 — ロジックは概ね正常

vs dragapult 19 games の序盤トレースを定量化した結果、セットアップの 主要ロジックは正しく機能していることが判明 (当初「速度が主犯」と仮説を 立てたが、単一の修正可能バグは残っていない):

  • 手張り先: 85 回中 72 回 (85%) が Raging Bolt (ex/基本) に。誤爆は Kangaskhan 3 / Fezandipiti 3 等の少数で、多くは Bolt 不在時の消化的手張り
  • 先発: Ogerpon (エンジン) 65 / Raging Bolt ex 7 / Iron Leaves 6 — Ogerpon 先発が大半 (正しいプラン)。Fezandipiti 先発の事故手は稀
  • Ogerpon 特性: 発動できる局面で発動。3 エネ貯める前に退却して Bolt を 昇格させる (正しいエンジン運用) ので、まんようしぐれ未使用も設計通り
  • アカマツ (Crispin): 提示 7 回中 6 回プレイ (86%)。引けた時は撃つ

Bellowing 初発射は良い試合で自分の 3-4 ターン目。これはデッキの自然な 速度であり、dragapult (2-3 ターン目に KO 開始) への遅れはアーキタイプ 固有の速度不利。残る勝率差 (~5-12%) はこの 6 体メタでの遅い後手デッキの 天井に近く、ルールの微調整では 2 倍にはならない。

次アクション (優先順を更新)

  1. 再チューニング — 今日までの修正で attach / retreat / tier / bellowing / prize_race の各 params が実際に挙動へ効くようになった (以前は死んでいた)。 これらを回すのが最も費用対効果が高い
  2. ベンチ構成 / プライズ献上の抑制 — 場が ex だらけで相手のサイドレースが 速い。非 ex 基本 Raging Bolt を壁に使う判断は turn planner (フェーズ認識) 領域。中規模
  3. メタ再考 — raging_bolt がこの環境で構造的に不利なら、agent の投資先 としての優先度自体を ryo さんと再検討する余地

参考: iono_bellibolt の停滞 (範囲外だが要記録)

iono_bellibolt (凍結) は自身のミラーで 22 戦中 21 引き分け = agent 自体が 停滞する。raging_bolt の vs iono unfinished 8/11 はこの iono 側の問題。 凍結のため本タスクでは触れないが、提出プールに入れると全体の unfinished 率を 押し上げるため、ryo さん判断で configs/rule_agents.yaml の match_weight を 下げる / enabled: false にする検討余地がある。

教訓 (他 agent にも適用すべき)

  • ワザ/カード名でのマッチングは言語依存で壊れる。ID は構造 (カード → ワザリスト、コスト長) から解決する。raging_bolt 以外に該当 agent は無いことを grep で確認済み
  • 合法オプションの再合法性チェックは書かない
  • params.yaml のキーを main.py が本当に読んでいるかは grep 'PARAMS.get' main.py で機械的に検証できる (tuning 空振りの早期検出)