コンテンツにスキップ

ルールエージェントのアルゴリズム調査 — 最良パターンの特定 (2026-07-15)

「どのアルゴリズムが最も強いか」を、(1) 全プール総当たりの実測ランキング、(2) 同一デッキ・別実装の自然実験、(3) 上位実装の解剖、で特定した記録。

1. 実測ランキング (26 enabled agents、2600 games、seed 20260715)

順位 agent 勝率(all) unfin 特徴
1 garchomp_ex_cynthia 65.3% 5.6% _win_plan (プラン駆動)
2 starmie_ex_archaludon_counter 64.9% 7.9% planned_*_attacks 5箇所参照
3 archaludon_ex_metal 64.8% 2.8% 同上 (同 notebook)
4 mega_lucario_ex_multiply_agent 64.7% 3.4% 汎用 AttackPlan + (死んだ)beam search
5 garchomp_ex_cynthia_city_top16 62.4% 6.4%
6 mega_lucario_ex_advanced_notebook_heuristic 61.4% 2.7% 汎用
7 crustle_mysterious_rock_inn 60.3% 1.1% 139行 (デッキパワーの証左)
25 raging_bolt_ogerpon 11.8% 4.2% reactive scorer
26 slowking_psychic_city_top16 3.7% 10.1% reactive scorer

注意: プール勝率はデッキパワーと操縦品質の積。crustle が 139 行で 60% を出す通り、ランキング単体ではアルゴリズムの優劣を語れない。

2. 決定的証拠: 同一デッキ・別実装の自然実験

デッキ 実装 アルゴリズム 勝率
mega_lucario multiply_agent プラン駆動 (汎用) 64.7%
mega_lucario advanced_notebook_heuristic 汎用ヒューリスティック 61.4%
mega_lucario fighting_gong reactive scorer (デッキ専用) 50.7%
alakazam mirror_setup_speed (汎用) セットアップ速度重視 59.4%
alakazam not_psychic (デッキ専用) reactive scorer 57.4%
garchomp cynthia (_win_plan あり) プラン駆動 65.3%
garchomp city_top16 reactive 寄り 62.4%

同一デッキで、プラン駆動の汎用実装が専用 reactive scorer を +14pt 上回る (multiply vs fighting_gong)。デッキ知識の量ではなくアルゴリズム構造が効いている。

3. 最良アルゴリズム = 「プラン先行スコアリング」

上位 4 実装 (garchomp/starmie/archaludon/multiply) が全て共有するパターン:

毎意思決定の冒頭:
  plan = ベストな (attacker, attack, target, 不足リソース) を列挙で確定
         - 仮定込み: 「進化したら」「あと1枚手張りしたら」撃てる攻撃も候補
         - target は Boss で釣れるベンチも含む (プライズ価値×確殺で採点)
各サブスコアラー:
  RETREAT   → plan.attacker がベンチなら高スコア (プランの実現)
  ATTACH    → plan.needs_energy の対象なら高スコア
  BOSS 対象 → plan.target と一致なら高スコア
  ATTACK    → plan.attack_index と一致なら高スコア

multiply_agent の _plan_attack汎用 (デッキ非依存) で約 60 行。reactive scorer が原理的に持てない「意思決定間の一貫性」を、グローバルなプラン 1 個の共有だけで実現している。raging_bolt で診断した失敗 (0 打点 Bellowing ループ、{L}{F} の分散、Crispin と手張りの不整合) は全て「プラン不在によるサブ決定間の矛盾」であり、このパターンの欠如が原因。

4. 発見: 公式先読み API が存在し、しかも死んでいる

multiply_agent (Kaggle LB 940 の notebook) は本来 cg.apisearch_begin/search_step (エンジン公式のターン内シミュレーション) によるbeam search (幅3、深さ4、1.5s 予算) を併用する設計。しかし:

  • ローカル obs にも search_begin_input は供給されている (cg/game.py:15)
  • notebook は旧 API (search_begin(文字列)) 向けで、現行シグネチャ (search_begin(Observation, デッキ/サイド/手札の予測...)) と不一致
  • except Exception: return None に握り潰され発火 0 回 (プローブで確認: 41 MAIN 決定で fired=0)

つまり現ランキングの multiply は「プランのみ」の実力で、beam search を現行 API に修繕すればさらに一段上がる余地がある。selfplay では相手デッキが既知なので、教師用途ならオラクル決定化 (真のデッキを予測値として渡す) が使える。

5. 提言

  1. 共有プランナー層 pca/rule_agents/planner.py を新設し、multiply の _plan_attack を一般化 (attacker×attack×target 列挙、進化/手張り仮定、デッキ固有打点は combat.py と同じ base_damage_fn 注入)。弱い agent (raging_bolt 11.8% / slowking 3.7% / zoroark 12.8% / terastal 12.9%) をプラン参照に改修する
  2. beam search の修繕 (search_begin を現行 API で呼ぶ薄いラッパ + オラクル/簡易決定化)。プランの次の一段。ターン内全探索は ISMCTS (ML 側) と役割が被るため、rule agent には軽量 beam に留める
  3. ランキング下位の city_top16 系 (slowking 3.7% 等) は教師データ品質を下げるため、プランナー改修か match_weight 削減の判断対象

追記 (2026-07-19): 提言 1 を実装 — パイロットで +1.5pt (有意)

src/pca/rule_agents/planner.py を新設 (plan_turn_attack / TurnAttackPlan、設計: rule-agent-planner.md) し、raging_bolt_ogerpon に 6 フックで配線 (RETREAT 昇格 / TO_ACTIVE 昇格先 / ATTACH / エネ付与先 / Boss プレイ・対象 / ATTACK 一致)。

A/B (1500g/側 × 2seed、vs メタ5 pin): 3.87% → 5.40% (+1.53pt, z=2.00)、avg サイド 0.71 → 0.90 (+27%)。garchomp 1.0→4.7% / abomasnow 6.0→10.3%。

スモークで重要な反例を検出: 退却フックだけ配線すると退却→意図しない昇格→また退却のスラッシュで攻撃回数が 2.8→0.7 回/game に崩壊。昇格 (TO_ACTIVE) をプランに従わせ、退却を「プランが KO or Active に打点プラン無し」でゲートして解決。multiply が昇格選択もプランで縛っていた理由がこれ。

追記 (2026-07-20〜23): 横展開 2 例目 (slowking) — +1.89pt (高度に有意)

プール最弱の slowking_psychic_city_top16 (3.7%) にプランナーを配線。導入前に raging_bolt と同型のバグを発見: score_attack が attack_id を区別しておらず、Yadoking の主力ワザ「Super Psy Bolt」(120 dmg, cost 3) と 0 ダメージ効果ワザ「Seek Inspiration」(デッキトップを捨てそのポケモンの1ワザを借りる、cost 2) が同スコアで並び、30game スモークで Super Psy Bolt は 54 回中 1 回しか撃たれず Seek Inspiration が 89% を占めていた。さらに retreat の昇格閾値が min_energy=2 で実コスト (3) より 1 少なく、2 エネで妥協昇格していた。

対応 (raging_bolt の _attack_of パターンを踏襲):

  1. attack_id を構造的に解決 (_attack_of、コスト長→名前フォールバック)
  2. score_attack を attack_id で区別、Seek Inspiration を下位 tier に
  3. 昇格閾値を実コスト (3) に修正
  4. プランナー 6 フック配線 (raging_bolt と同一パターン)

条件付き技 (Ononokusu の Bring Down the Axe = 相手が特殊エネ所持なら即死) と spread 技 (Kyurem の Trifrost = 相手 3 体へ 110) は単体 KO モデルに合わないため base_damage_fn で意図的に 0 を返しプランナーの対象外にし、既存の固定スコアパスをそのまま残した (安全側)。

A/B (2750g/側 × 3seed、vs メタ5 pin): 3.53% → 5.42% (+1.89pt, z=3.40)、avg サイド 0.16→0.33 (2 倍)、unfinished -33%。dragapult/garchomp/archaludon/ abomasnow は全改善、lucario のみ僅かに悪化 (11.3%→9.6%、許容範囲)。

2 例連続で有意な改善 (raging_bolt +1.5pt z=2.00、slowking +1.89pt z=3.40)。プランナー配線の再現性が高いことを示す。

追記 (2026-07-23〜24): 横展開 3 例目 (zoroark) — +1.31pt (有意)、罠 2 種

n_zoroark_ex_city_top16 (5.0%) にプランナーを配線。slowking より複雑な 2 つの落とし穴を踏んだ:

  1. サブアタッカー 3 体の attack_id 未区別: Zekrom (Shred 70 dmg vs Rampaging Thunder 250 dmg)、Hihidaruma (Back Draft 動的 vs Flamebody Cannon 90+スプレッド)、Reshiram (Powerful Rage 動的 vs Virtuous Flame 170) が各々同スコアで、30game スモークで Shred が 19 回中 12 回 (63%) は Rampaging Thunder も合法なのに選ばれていた。
  2. エンジン特有の罠 (score_attack を active.id で分岐すると踏み外す): Zoroark ex の唯一のワザ「Night Joker」はベンチ N ポケモンの技をコピーする。トレースで判明: エンジンはコピー候補を「コピーされた技の attack_id を持つ ATTACK オプション」として個別に提示し、board の active は Zoroark のまま。初回実装は active.id == P_ZOROARK_EX_N で分岐し aid を無視していたため、Shred が選ばれても Rampaging Thunder が選ばれても同じ「ベンチ最良技」の一律スコアを返し、上記 1 のバグを再生産していた。修正: score_attack を aid in _N_LINEAGE_ATTACKS (attack_id 優先) で分岐する構造に統一 — 「Zekrom 本体が攻撃」も「Zoroark 経由でコピー」も同じ経路で正しく評価される。動的技 (Back Draft = 相手 discard の基本エネ×30、Powerful Rage = コピー元 Reshiram 自身のダメカン×20) はコピー元をベンチから探して計算 (zoroark_raw_damage)。

さらに実装中に CardType の import 漏れ (NameError) を作り込み、 choose_options の広い except に握り潰されて Back Draft の全選択肢が静かに無効化されていた。ruff check で検出したが、最初の A/B 計測はこのバグが入った状態で走らせてしまっていたため、修正後に該当 seed を取り直した:

delta z
バグ入り版 +1.09pt 1.77 (有意水準未達)
修正後 +1.31pt 2.10 (有意)

最終 A/B (2750g/側×3seed): 5.02%→6.33%、avg サイド 0.25→0.64 (2.6倍)。dragapult で unfinished が 12→37 に増加 (許容範囲、他相手はほぼ 0 のまま)。

3 例全てで有意な改善 (raging_bolt z=2.00、slowking z=3.40、zoroark z=2.10)。プランナー配線は再現性の高い改善パターンと言える。

教訓

  • アルゴリズム比較は同一デッキ・別実装で行う (プール勝率はデッキパワーと交絡)
  • notebook 由来コードの外部 API 呼び出しはシグネチャ互換を必ず検証 (握り潰し except で 1 年気づかない)。凍結 agent でも「機能が死んでいる」ことはある
  • reactive scorer の限界は「プラン 1 個のグローバル共有」という小さな構造で大きく破れる (+14pt)。フルプランナーや検索を持ち出す前にまずこれ
  • score_attack は active.id ではなく attackId で分岐する。コピー技 (Night Joker 等) はコピー先の attack_id を持つ選択肢を、board の active を変えずに提示するエンジン仕様があり、active.id 分岐は罠になる (zoroark)
  • 例外を握り潰す except Exception の中で新しい動的計算を書いたら、ruff check を実装直後に走らせる。 テストが緑でも NameError は選択肢の score を静かに -999999 にするだけで、A/B が「動いているように見えて実は死んだコードパス」のまま完走してしまう (zoroark の CardType 漏れ)