コンテンツにスキップ

Rule-Based Agents 再設計 — 設計問題・実装方針・戦略思考

作成日: 2026-07-05

先行資料: 2026-06-29-rule-agent-bootstrap.md

概要

src/pca/rule_agents/ は Belief-Guided Neural ISMCTS のブートストラップ・自己対戦相手プール・Elo 評価基準の 3 役を担う。現時点で 30 agent / 約 20k 行あり、以下の問題を抱える:

  • 1 デッキ 1 モノリスで重複と非対称チューニングが混在
  • スコアリング閾値はマジックナンバー、由来説明もチューニング機構もなし
  • アルゴリズムが「その 1 手のスコア独立評価」に閉じており、ターン全体の計画・相手読み・複数手の連携を表現できない
  • 勝率が 0.045 〜 0.676 の 15 倍差

本ドキュメントは:

  • notebook 由来 agent (10 種) を 基準ベースラインとして凍結 した上で、
  • 手書き agent (残り 20 種) を対象に 3 層アーキテクチャ + params.yaml + Trace + Golden test で再設計し、
  • 加えて アルゴリズムそのものを「ターン計画 + 相手読み + 局所探索」に拡張 する方針を示す。

制約: notebook 由来 agent は基準として凍結

notebooks/rule-ai-agent/ の Kaggle notebook を移植した agent は、Elo 評価と回帰基準として実装を保存する。ソースファイル冒頭に # Auto-extracted from notebooks/... を持つものが該当する。

凍結対象 (10 agent、touch 禁止)

agent_id 出典 notebook
alakazam_ex_not_psychic rule-based-not-psychic-alakazam-best-5th.ipynb
archaludon_ex_metal a-sample-archaludon-75-wr-vs-my-1300-starmie.ipynb
dragapult_ex_phantom_dive a-sample-rule-based-agent-dragapult-ex-deck.ipynb
dragapult_ex_phantom_dive_or_go_home phantom-dive-or-go-home-a-dragapult-ex-deck.ipynb
dragapult_ex_tempo dragapult-v3-tempo-ptcg-ai-battle-agent.ipynb
garchomp_ex_cynthia a-sample-cynthia-garchomp-ex-deck.ipynb
generic_advanced_notebook_heuristic pok-mon-tcg-advanced-heuristic-planning-agent.ipynb
generic_mirror_setup_speed mirror-setup-speed.ipynb
generic_multiply_agent multiply-agent-best-940-lb.ipynb
iono_bellibolt_ex_lightning a-sample-rule-based-agent-iono-s-deck.ipynb
mega_abomasnow_ex_water_grass a-sample-rule-based-agent-mega-abomasnow-ex-deck.ipynb
mega_lucario_ex_fighting_gong a-sample-rule-based-agent-mega-lucario-ex-deck.ipynb
mega_lucario_ex_v62 ptcg-mega-lucario-ex-v62.ipynb
mega_venusaur_ex_grass_sustain mega-venusaur-ex-deck-v2-rule-based-agent.ipynb
moltres_ex_removal japanese-ex-moltres-removal.ipynb
starmie_ex_archaludon_counter japanese-ex.ipynb
crustle_mysterious_rock_inn crustle-bot.ipynb

これらの main.pyバグ修正・パフォーマンス改善を含めて改変しない。理由:

  1. Elo 基準の再現性。Neural agent の改善は「凍結 rule agent との勝率差分」で測る。基準側を触ると過去との比較が壊れる。
  2. notebook 版の元コード互換。提出時に notebook そのままの挙動を再現できるようにする。
  3. 回帰検出。共有 heuristics.py を触ったとき、凍結 agent の勝率が動いていないか自動テストする。

改変可能な agent (手書き系 20 種)

以下は本再設計の対象。既存の handwritten agent + city_top16 系列。

  • clefairy_ogerpon_fairy_zone, festival_lead_appletun, grafaiai_dark_spread, honchkrow_rocket_toolbox, raging_bolt_ogerpon
  • garchomp_ex_cynthia_city_top16, mega_sharpedo_ex_city_top16, n_zoroark_ex_city_top16, slowking_psychic_city_top16, terastal_box_jewel_search_city_top16
  • wugtrio_ex_tricolor_pump, generic_advanced_heuristic, generic_advanced_notebook_heuristic (このうち凍結指定を除く)

これらは自由にリファクタリング・archetype layer への切り出し・params.yaml 化する。

凍結境界を守るためのガード

  • src/pca/rule_agents/agents/<frozen>/main.py の先頭に # FROZEN: do not edit. See docs/research/2026-07-05-rule-agents-redesign.md を追加
  • tests/rule_agents/test_frozen_hash.py で SHA-256 をロックし、CI で改変検知
  • 共有 heuristics.py の関数シグネチャを変えるときは、凍結 agent の import が壊れないか import test で確認

デッキ固有動作の設定機構

Yes, デッキごとに個別に設定できる。3 層構造で "共通/族/固有" を分離し、固有部分は 2 つのレベル (宣言的 = params.yaml / 手続的 = main.py override) で表現する。

設定の 2 レベル

Level A: params.yaml (宣言的、tuning 可能)

  • スコア閾値、ボーナス、優先度など 数値 で表現できるものすべて
  • tuning runner から grid / CMA-ES で自動最適化可能
  • 例: "アカマツを手札に雷エネルギーがない時 22000 点"

Level B: main.py override (手続的、tuning 不可能)

  • 条件分岐が複雑でコードで書いた方が明快な "そのデッキだけの動き"
  • 100〜200 行を上限に据え、超えたら archetype layer に切り出す
  • 例: "Iron Crown ex を bench に 1 手目で置く、Rapid Vernier でメガフシギバナのデバフを無効化"

具体例: raging_bolt_ogerpon

# src/pca/rule_agents/agents/raging_bolt_ogerpon/params.yaml
version: 1
archetype_layers:
  - energy_engine # Ogerpon 系の共通エネ加速ロジック
  - mega_setup # メガガルーラ準備
  - draw_engine # メガガルーラのドロー特性

setup:
  active_priority:
    ogerpon: 100000 # T1 に確実に active (エンジン起動)
    raging_bolt_ex: 60000 # フォールバック (メインアタッカー)
    mega_kangaskhan_ex: 30000
    latias_ex: 20000

attach_energy:
  key_attacker_lightning_bonus: 5000
  key_attacker_grass_bonus: 2500
  engine_grass_bonus: 2000
  overcap_penalty: -1000

supporter:
  akamatsu_no_lightning_in_hand: 22000
  akamatsu_no_grass_in_hand: 20000
  akamatsu_generic: 9000
  boss_lethal: 50000

items:
  hyper_ball_empty_bench: 22000
  hyper_ball_generic: 15000
  glass_trumpet_no_basic_energy: 17000

opp_max_damage_overrides:
  dragapult: 240
# src/pca/rule_agents/agents/raging_bolt_ogerpon/main.py
# 100〜200 行のデッキ固有ロジックのみ

from ..._shared import RulePipeline, AgentContext

def deck_specific_bench_priority(ctx: AgentContext, card_id: int) -> tuple[int, str] | None:
    """Iron Crown ex を必ず bench に置く (Rapid Vernier でデバフ無効化)"""
    if card_id == P_TETSUNOIBARA_EX:
        return (19000, "bench Iron Crown ex (Rapid Vernier — play first)")
    return None

def register(pipeline: RulePipeline) -> None:
    pipeline.hooks.bench_priority.append(deck_specific_bench_priority)

RulePipeline は archetype layer + universal layer をコンポーズしたもので、フックポイント (bench_priority, attach_bonus, boss_target_selector など) で override を差し込める。

アルゴリズム自体の問題分析

現行の rule agent は本質的に 1 手を独立にスコアリングする reactive scorer である。以下の 6 つの構造的欠陥を持つ。

1. スコア合成が「校正されていない加算」

SCORE_BOSS_LETHAL=50000SCORE_HYPER_BALL_SEARCH=18000 は "ボスの方が優先" を表現しているだけで、比率 (50000/18000 ≈ 2.78) には何の意味もない。閾値 A + 閾値 B の加算合成が起きたとき、結果は無意味な相対関係になる。

現行: score = base + bonus1 + bonus2 + ...
問題: bonus 同士が競合しても補正されない、絶対値の大小に意味がない

対策案:

  • スコアを 順序 (rank) として設計。tier (0〜9) + tier 内優先度 で表現
  • または 確率としての policy 出力: softmax 温度でスケール、tuning で校正

2. Look-ahead ゼロ

各 option を その瞬間の盤面 だけで評価しており、"この手を打った後にどうなるか" を見ていない。

症状:

  • 攻撃準備の完了を「次ターンにあと 1 エネで殴れる」で判断できない
  • boss で引きずった相手を 今ターン中に KO できるか を計算しない → 引きずった意味がない引きずり
  • 進化 → 攻撃の 2 手連鎖を単一 option 評価では表現できない

対策案:

  • 1〜3 手先の determinized look-ahead (簡易 CABT sim を rule 側でも持つ)
  • または turn plan: ターン開始時に "今ターンの目的" を決定 → 各 option を "計画達成に寄与するか" で評価

3. 独立評価: 手の組み合わせを表現できない

「エネ張り + 攻撃」「サーチ + プレイ」の 2 手を同時に考慮できない。

症状:

  • エネ張りを別の pokemon にしてしまい、直後の攻撃で必要エネが足りない
  • Hyper Ball で 2 枚トラッシュを "その 2 枚で失うもの" を評価せず選ぶ

対策案:

  • plan-and-execute: ターン開始で "今ターンにやりたい 3 手" を立て、以降の option 評価はその文脈で判定
  • または SelectContext ごとに 同ターン内で既に選ばれた option を context に注入

4. 相手モデル不在

自分の手のスコアリングにしか関心がなく、相手が次に何をしそうか を推論しない。

症状:

  • Boss の指令の狙撃先選定を "prize × energy × stage" の静的スコアだけで決める → 相手の脅威度は動的
  • 自分のベンチ狙撃されそうな pokemon を判別できない → 場に出すべきでない場面で出す
  • 相手の手札枚数からアンフェアスタンプの成功率を推定しない

対策案:

  • 軽量 opponent modeling: 相手 archetype + 相手 discard から "次ターンの想定打点分布" を軽く推論
  • Neural 側の BeliefNet を rule agent からも呼べる薄いラッパを提供 (Phase 2 以降)

5. Resource budget が暗黙

1 ターン = supporter 1 / energy attach 1 / retreat 1 (基本) の予算を 各 SelectContext が独立に消費 しており、"今ターンあと何を残しているか" が rule 側の思考に入っていない。

症状:

  • supporter を序盤に軽い理由で切り、後半にボスを使えない
  • retreat を無駄に切って攻撃準備が崩れる

対策案:

  • ターン開始時に TurnBudget = {supporter: 1, attach: 1, retreat_used: false} を追跡
  • 各 option を "budget 消費に見合う価値か" で評価

6. ターン計画の欠如

現在は 状況反応 (SelectContext ごとに独立スコアリング) に閉じており、ターン単位のゴール (今ターン何を達成したいか) の概念がない。

症状:

  • 序盤に「盤面展開」「エネルギー加速」「サーチで手札の質を上げる」のどれを優先するかがコード上で表現されない
  • 対戦のフェーズ (setup / trade / endgame) 認識がない

対策案:

  • ターン開始時に TurnGoal を選ぶ:
  • SETUP — ベンチ展開・進化下ろし・エネ加速
  • PRESSURE — 攻撃を通しつつ盤面維持
  • LETHAL — 今ターン KO でサイド確定
  • STALL — 対面不利で耐える
  • 各 SelectContext のスコアリングを TurnGoal で条件付ける

これら 6 点は アルゴリズムを "reactive scorer" から "planner + reactive scorer" に拡張 する形で解消できる。以下の 3 層アーキテクチャ にこれを組み込む。

ポケカ戦略を AI に実装する方法論

ここが本節の一番重要な部分。「デッキごとの立ち回り」を どうやって発見・形式化・コード化するか の作業手順を明文化する。

手順

1. 立ち回り知識の収集
   ↓
2. Game Plan 形式化 (turn goal + kill path + threat map)
   ↓
3. パラメータ空間の切り出し (tunable vs fixed)
   ↓
4. archetype layer への配置 or deck-specific 実装
   ↓
5. Golden fixture で "この局面でこの手" を固定
   ↓
6. 自己対戦でチューニング
   ↓
7. Trace レビューでミスを修正

Step 1: 立ち回り知識の収集

入力: 大会入賞レシピ・海外 YouTube 解説・Kaggle notebook のコメント・ポケカ公式ブログ

成果物 (src/pca/rule_agents/agents/<agent_id>/strategy.md) — 対応する機械可読版 strategy.yaml を並置:

# raging_bolt_ogerpon strategy note

## 勝ち筋

- T1: Ogerpon active + Raging Bolt bench + 草エネ手張り
- T2: Ogerpon エネ加速 → Raging Bolt に手張り + アカマツで雷を追加
- T3: Raging Bolt にエネ 3+ → Burst Lightning で KO

## 脅威への対応

- Dragapult: ベンチ狙撃 → Ogerpon 保護のため Latias skyline で pivot
- Mega Lucario: 高打点 → 先攻優位、Iron Crown で built-in draw 妨害

## リソースの優先順位

- Akamatsu > その他 supporter (エネ加速のため)
- Hyper Ball は 1 枚温存 (終盤の Iron Crown サーチ用)

## Bench Composition (理想)

- Active: Ogerpon
- Bench 1: Raging Bolt ex
- Bench 2: Iron Crown ex (Rapid Vernier)
- Bench 3: Mega Kangaskhan ex (draw engine)
- Bench 4: Latias ex (free retreat)

重要: この strategy note を書けない agent は作らない。言語化できない立ち回りはコードにできない

Step 2: Game Plan の形式化

strategy note を TurnGoal state machine に落とす:

# src/pca/rule_agents/agents/raging_bolt_ogerpon/game_plan.yaml
turn_goals:
  - name: SETUP
    condition: "turn <= 2 or active_energy < 2"
    priorities:
      - fill_bench_with_key_pokemon
      - accelerate_energy_via_ogerpon
      - search_key_cards
  - name: PRESSURE
    condition: "turn >= 3 and active_energy >= 2 and can_attack_next_turn"
    priorities:
      - attach_to_main_attacker
      - draw_via_kangaskhan
      - preserve_supporter_for_boss
  - name: LETHAL
    condition: "can_ko_active_this_turn or (opp_prize == 1 and can_boss_target)"
    priorities:
      - boss_target_selection
      - burst_lightning_energy_maximization
      - attack

threat_responses:
  dragapult:
    - keep_ogerpon_low_hp_off_bench
    - prefer_latias_pivot
  mega_lucario:
    - iron_crown_priority_high
    - conserve_hyper_ball

成果物: agent ごとの game_plan.yamlturn goal を決めるルール + archetype 別 threat response を宣言的に持つ。

Step 3: パラメータ空間の切り出し

game plan の中で 数値化できる部分は全て params.yaml に:

  • スコア閾値
  • 優先度の順序
  • 条件式の threshold (active_energy < 22)

手続的な部分だけを main.py に残す

Step 4: archetype vs deck-specific の判断

「他のデッキでも使えるロジックか?」でふるい分ける:

  • archetype layer 行き: Ogerpon エネ加速、Rocket supporter chain、Stage2 rush、bench spread
  • deck-specific 行き: Iron Crown 早出し、フェアリーゾーン弱点付与、"このカード特有の効果" 全般

判断に迷ったら deck-specific から始めて、2 デッキ目で同じロジックが出たら archetype に昇格 させる。

手順5: 基準となるテストデータ

strategy note の各 kill path から代表局面を fixture 化:

tests/rule_agents/golden/raging_bolt_ogerpon/
  t1_setup_ogerpon_active.json/expected.yaml
  t2_akamatsu_for_lightning.json/expected.yaml
  t3_burst_lightning_ko.json/expected.yaml
  vs_dragapult_pivot_latias.json/expected.yaml

手書きで良い。5〜10 fixture / agent で十分。selfplay から自動収集した trace を種にする。

Step 6: 自己対戦チューニング

tuning runner で params.yaml を最適化:

uv run python -m pca.rule_agents.tuning \
  --agent raging_bolt_ogerpon \
  --search grid \
  --params-space configs/rule_agents/tuning/raging_bolt_space.yaml \
  --opponents starmie_ex_archaludon_counter,archaludon_ex_metal,dragapult_ex_phantom_dive_or_go_home \
  --games-per-config 200 \
  --parallel 8

対戦相手には 凍結 agent を含める。Elo 基準が動かないように。

Step 7: Trace レビュー

--rule-explain N で対戦の trace を吐き、負け試合を目視で:

  • どのターンでミスをしたか
  • なぜそのスコアになったか
  • game_plan のどの条件が誤って発火したか

を確認して strategy note に反映 → コード修正 → 再チューニング。

私 (Claude) と ryo さんの役割分担

手順 ryo さん
Step 1: 知識収集 ★★★ 主導 (ポケカ知識) 補助 (英語情報の翻訳、公式データ整理)
Step 2: Game Plan ★★ 監修 ★ 形式化 (yaml 化)
Step 3: パラメータ切り出し ★★★ 主導
Step 4: archetype 分類 ★ アドバイス ★★ 実装
Step 5: Golden fixture ★★ 期待挙動を指定 ★★ 実装
Step 6: チューニング ★★★ 主導
Step 7: Trace レビュー ★★★ 主導 (ミス判定) ★★ ミスの根本原因分析

3 層アーキテクチャの提案

Universal layer (deck 非依存 — 全 agent 適用)
  ├─ setup: hand の basic pokemon を active に
  ├─ deck-out 回避
  ├─ END_TURN は最後 (score=0)
  ├─ 攻撃可能なら必ず攻撃 (無理攻めは archetype でペナルティ)
  ├─ 空ベンチ safety (種切れ回避)
  └─ 火傷/麻痺での retreat 判定

Archetype layer (デッキ族 — 有効化 flag で選択)
  ├─ energy_engine       Ogerpon 系
  ├─ evolve_rush         Stage2 早出し
  ├─ spread_damage       ベンチダメカン蓄積
  ├─ rocket_toolbox      Rocket supporter chain
  ├─ counter_swap        Starmie / Wugtrio 系
  ├─ mega_setup          Mega ex 進化準備
  └─ draw_engine         Kangaskhan / Meowth 特性

Deck-specific layer (100〜200 行の main.py override)
  └─ カード固有の特殊効果 (Iron Crown, フェアリーゾーン 等)

--- 上記の上に、ターン計画層を追加 ---

Turn planner (ターン開始時に走る)
  ├─ TurnGoal 選択 (SETUP / PRESSURE / LETHAL / STALL)
  ├─ TurnBudget 初期化 (supporter/attach/retreat 予算)
  └─ ThreatMap 更新 (相手 archetype 判定 + 想定打点)

Reactive scorer (各 SelectContext で走る)
  └─ 各 option を "TurnGoal 達成寄与 + Budget 使用効率" で採点

これで アルゴリズム自体の問題 の 6 点に対応:

問題 対応
1. スコアが校正されていない tier + 内部順位で表現、softmax で確率化
2. Look-ahead ゼロ Turn planner で 1〜3 手先を想定
3. 独立評価 TurnBudget で "既消費/残予算" を共有
4. 相手モデル不在 ThreatMap で archetype ベースの脅威度を持つ
5. Resource budget 暗黙 TurnBudget を明示
6. ターン計画欠如 TurnGoal state machine

パラメータチューニング機構

(前セクション 具体例: raging_bolt_ogerpon 参照)

探索空間の書き方

# configs/rule_agents/tuning/raging_bolt_space.yaml
attach_energy.key_attacker_lightning_bonus:
  type: choice
  values: [3000, 4000, 5000, 6000, 8000]

supporter.akamatsu_no_lightning_in_hand:
  type: choice
  values: [18000, 20000, 22000, 25000]

items.hyper_ball_generic:
  type: continuous
  low: 10000
  high: 20000

出力

  • data/rule_tuning/<agent>/history.csv — 全 config × 対戦相手の勝率
  • data/rule_tuning/<agent>/top_k.yaml — 勝率トップ K
  • data/rule_tuning/<agent>/best.yaml — 最良 params (書き戻し候補)

対戦相手セットの選び方

  • 凍結 agent を必ず含める (Elo 基準連続性)
  • 環境 Tier1 相当を 3〜5 種
  • 同 archetype 同士も含める (ミラー適応性)

説明性 (Trace)

追加する型

@dataclass(frozen=True)
class TraceEntry:
    option_index: int
    score: float
    reason: str
    layer: str          # "universal" | "archetype:energy_engine" | "deck"
    tags: tuple[str, ...] = ()

@dataclass(frozen=True)
class RuleDecision:
    selected: list[int]
    scores: list[float]
    target_policy: list[float] | None = None
    trace: list[TraceEntry] = ()
    turn_goal: str | None = None       # ターン計画層追加後
    turn_budget: dict[str, int] | None = None
    meta: dict[str, Any] = ...

凍結 agent は空 trace を返す (後方互換)。改変可能 agent のみ trace を埋める。

selfplay 出力

uv run python -m pca.training.selfplay \
  --rule-explain 20 \
  --rule-explain-output data/rule_trace/2026-07-05.jsonl

分析ツール

uv run python -m pca.rule_agents.explain \
  --trace data/rule_trace/2026-07-05.jsonl \
  --agent mega_venusaur_ex_grass_sustain \
  --show-turn-ends

例えば mega_venusaur が 0.045 に沈んでいる原因 (回復ループで END が来ない、等) を trace で追える。凍結対象なので修正はできないが、なぜ壊れているかを言語化してドキュメント化できる (src/pca/rule_agents/agents/mega_venusaur_ex_grass_sustain/known-issues.md)。

Golden Replay テスト

テストデータ

tests/rule_agents/golden/raging_bolt_ogerpon/
  t1_no_lightning_in_hand.json           # 観測 snapshot
  t1_no_lightning_in_hand.expected.yaml
# expected.yaml
context: PLAY_SUPPORTER
must_include_option_card: アカマツ
forbidden_option_cards: [ボスの指令]
min_score: 15000
turn_goal: SETUP # ターン計画層追加後

継続的インテグレーション

uv run pytest tests/rule_agents/ -v

凍結 agent 用の hash guard

# tests/rule_agents/test_frozen_hash.py
FROZEN_AGENTS = {
    "starmie_ex_archaludon_counter": "sha256:abcd...",
    "archaludon_ex_metal": "sha256:1234...",
    # ...
}

def test_frozen_agent_main_py_unchanged():
    for agent_id, expected_sha in FROZEN_AGENTS.items():
        path = Path(f"src/pca/rule_agents/agents/{agent_id}/main.py")
        assert sha256(path.read_bytes()) == expected_sha, (
            f"{agent_id} is frozen. See docs/research/2026-07-05-rule-agents-redesign.md"
        )

3 つの利用プリセット

プリセット policy_target option 選択 用途
teacher (default) selected_one_hot argmax 学習 policy 生成
pool score_softmax softmax(T=0.5) selfplay 対戦相手プール
baseline selected_one_hot argmax Elo 評価 (固定)

Agent 整理計画 (凍結境界を守る)

凍結: 17 agent (改変禁止)

前掲の notebook 由来リスト。全て main.py にロック。

改変可能・積極改善: 8〜13 agent

  • 手書き系 5 種 (raging_bolt_ogerpon, clefairy_ogerpon_fairy_zone, festival_lead_appletun, grafaiai_dark_spread, honchkrow_rocket_toolbox)
  • city_top16 系 5 種 (garchomp_ex_cynthia_city_top16, mega_sharpedo_ex_city_top16, n_zoroark_ex_city_top16, slowking_psychic_city_top16, terastal_box_jewel_search_city_top16)
  • その他 (wugtrio_ex_tricolor_pump, generic_advanced_heuristic など)

削除・統合はしない

凍結 agent との比較性を保つため、当面 agent 数を減らさない。Elo baseline pool を痩せさせない ことを優先。

段階的移行フェーズ

Phase 1 (即時、1 週間)

  • [x] 本ドキュメント作成
  • [x] RuleDecision.trace フィールド追加 (base.py、後方互換のため default 空)
  • [x] --rule-explain フラグを selfplay に追加
  • [x] tests/rule_agents/test_frozen_hash.py 追加 (凍結 hash lock)
  • [x] src/pca/rule_agents/agents/_template/ scaffold (README + strategy.md + strategy.yaml + params.yaml + known-issues.md)

Phase 2 (1〜2 週) — pilot: raging_bolt_ogerpon

  • [x] raging_bolt_ogerpon/strategy.md を書く (ryo さん監修は継続)
  • [x] params.yaml schema 定義、pilot agent を params 化 (setup / supporter / items / attack tier / prize_race)
  • [x] tuning runner (grid + optuna) 実装
  • [ ] pilot で tuning 1 ラウンド、best params を書き戻す (次アクション、 --confirm-top-k 3 推奨)
  • [ ] golden fixture 5 個

Phase 3 (2〜4 週)

  • [x] 共有 combat 層 (combat.py): リーサル判定 / ko_plan / 被リーサル推定 / プライズレース / 同ターン文脈。詳細: docs/architecture/rule-agent-combat.md
  • [ ] archetype layer 実装 (layers/archetypes/*.py)
  • [ ] pilot を layer 上に載せ替え、main.py 200 行以下
  • [ ] TurnGoal / TurnBudget / ThreatMap の型と Turn planner 実装 (prize_race がフェーズ判断の材料として先行実装済み)
  • [ ] pilot に turn planner を統合

Phase 4 (4〜8 週)

  • [ ] 残 handwritten agent 4 種を順次移行 (strategy-note → params → layer 移行 → golden test)
  • [ ] city_top16 系 5 種を移行
  • [ ] wugtrio_ex_tricolor_pump を tuning で修復試行 (凍結対象外)

Phase 5 (継続)

  • [x] Optuna (TPE + MedianPruner) 統合 完了 (2026-07-06)。詳細: docs/architecture/rule-agent-tuning-optuna.md
  • [x] Selfplay evaluator 実装完了 (subprocess ベース、PCA_PARAMS_OVERRIDE_DIR 経由)
  • [x] Tuning 信頼性改善 完了 (2026-07-09): baseline warm-start / seed 固定 / 確認ラン (ADR 0004) / match_weight 加重 objective
  • [ ] CMA-ES / Hyperband への切り替え (Optuna sampler / pruner 差し替えで対応可)
  • [ ] games 数 multi-fidelity 化 (Wilson 区間での早期打ち切り)
  • [ ] 並列 config 実行 (multiprocessing.Pool)
  • [ ] BeliefNet 相当の軽量 opponent modeling を rule 側にも提供 (opponent_can_ko_me が静的打点版の先行実装)
  • [ ] 新環境デッキ対応

ディレクトリレイアウト (提案)

src/pca/rule_agents/
  base.py                        # TraceEntry, RuleDecision 拡張
  policy.py                      # unchanged
  registry.py                    # unchanged
  ported.py                      # unchanged
  generic.py                     # unchanged
  heuristics.py                  # 縮小 (observation helpers のみ)
  params.py                      # NEW
  planner/
    __init__.py
    turn_goal.py                 # NEW: TurnGoal state machine
    turn_budget.py               # NEW
    threat_map.py                # NEW
  layers/
    universal.py                 # NEW
    archetypes/
      energy_engine.py
      evolve_rush.py
      spread_damage.py
      rocket_toolbox.py
      counter_swap.py
      mega_setup.py
      draw_engine.py
  tuning/
    __main__.py
    grid.py
    space.py
  explain/
    __main__.py
    trace.py
  agents/
    <frozen_deck>/               # 凍結: main.py touch 禁止
      main.py                    # # FROZEN header
      agent.py
      deck.csv
    <mutable_deck>/              # 改変可能
      main.py                    # 100〜200 行 override
      agent.py                   # archetype layer 宣言
      deck.csv
      strategy.md                # NEW: 立ち回り解説 (人間可読)
      strategy.yaml              # NEW: 立ち回り (機械可読)
      params.yaml                # NEW: 数値パラメータ (auto-tuned)
    _template/                   # NEW: 新規追加時のコピー元 (登録対象外)

tests/rule_agents/
  test_frozen_hash.py            # NEW
  test_golden.py
  golden/<agent>/*.json,yaml

configs/rule_agents/tuning/
  <deck>_space.yaml

data/rule_tuning/                # gitignore
data/rule_trace/                 # gitignore

よくある質問

Q. なぜ notebook 由来 agent を凍結するのか?
A. Elo 基準の再現性と回帰検出のため。改善は "凍結 agent との勝率差分" で測る。基準側を触ると過去比較が崩れる。

Q. 凍結 agent にバグがあっても直せないのか?
A. main.py は触らない。代わりに src/pca/rule_agents/agents/<agent>/known-issues.md に記録し、Neural 側の trace や上位 agent の派生でカバーする方向。

Q. デッキごとの動作を個別設定できるか?
A. Yes、2 レベルで:

  • 数値化できる部分は params.yaml (tuning 可能)
  • 手続的な部分は main.py (100〜200 行の override)

Q. アルゴリズムの根本問題 (look-ahead ゼロ、相手モデル不在等) は本当に rule ベースで解決できるか?
A. 完全解決は無理。しかし Turn planner + TurnBudget + ThreatMap で 今より 2〜3 段階の改善 はできる。それ以上は Neural ISMCTS 側に任せる。rule agent の役割は "ある水準以上の相手 + 教師" であって "最強" ではない。

Q. Trace を全 agent に付けるコストは?
A. option ごとに TraceEntry を 1 個作るだけ。1 ゲーム 100 decisions × option 平均 20 個 = 2000 entry ≒ 数百 KB。selfplay 全数に付けても問題ないサイズ。

Q. ryo さんは何を提供すればいい?
A. 主に 3 つ:

  1. 各デッキの strategy note (立ち回り + kill path + threat map)
  2. Trace レビュー時のミス判定 (負け試合を見て「ここが違う」と指摘)
  3. fixture の expected 挙動 (「この局面ではこれを選ぶべき」)

コードは私が書く。ポケカ知識に関わる意思決定だけ ryo さん依存。

参考