ルールエージェントのアルゴリズム調査 — 最良パターンの特定 (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.api の search_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. 提言¶
- 共有プランナー層
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%) をプラン参照に改修する - beam search の修繕 (search_begin を現行 API で呼ぶ薄いラッパ + オラクル/簡易決定化)。プランの次の一段。ターン内全探索は ISMCTS (ML 側) と役割が被るため、rule agent には軽量 beam に留める
- ランキング下位の 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 パターンを踏襲):
- attack_id を構造的に解決 (
_attack_of、コスト長→名前フォールバック) - score_attack を attack_id で区別、Seek Inspiration を下位 tier に
- 昇格閾値を実コスト (3) に修正
- プランナー 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 つの落とし穴を踏んだ:
- サブアタッカー 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 も合法なのに選ばれていた。
- エンジン特有の罠 (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 漏れ)