ポケモン対戦を強化学習で学ぶ入門記事
- はじめに
ポケモン対戦を強化学習で扱うことは十分に可能で、現在いちばん実用的な構成は Pokémon Showdown を対戦シミュレータとして使い、Python から poke-env で操作し、Gymnasium 互換の環境として学習器につなぐ 形です。poke-env の公式ドキュメントもこの用途を前提にしており、Gymnasium インターフェース、報酬設計、観測設計、対戦評価の例まで公開しています。さらに、学習用途では公開サーバではなくローカルの Showdown サーバを使うことが推奨されています。Python は 3.10 以上、Showdown サーバは Node.js 環境で起動できます。 
この記事では、最初から順に、
強化学習とは何か
なぜポケモン対戦が難しいのか
どんなソフトウェア構成にするのか
状態・行動・報酬をどう定義するのか
どのアルゴリズムから始めるべきか
どこで詰まりやすいのか
まで、実装を意識して詳しく整理します。
⸻
- まず、強化学習とは何か
強化学習は、エージェントが環境とやり取りしながら、「どの行動を選ぶと長期的な得点が高くなるか」を学ぶ枠組みです。基本要素は次の4つです。
状態 s
行動 a
報酬 r
方策 π(a|s)
流れは単純です。
1. エージェントが状態 s_t を観測する
2. 行動 a_t を選ぶ
3. 環境が次の状態 s_(t+1) に進む
4. 報酬 r_t を返す
5. その報酬が大きくなるように方策を更新する
ポケモン対戦に当てはめると、
状態
= 盤面の情報、HP、技、交代先、場の状態など
行動
= 技を選ぶ、交代する、特殊行動を選ぶ
報酬
= 勝敗、与ダメージ、相手撃破、自分の被害など
方策
= その盤面で何をするかを決める規則
になります。
⸻
- なぜポケモン対戦は強化学習の題材として面白いのか
ポケモン対戦は、単純なゲームよりかなり難しいです。難しい理由は大きく5つあります。
3.1 長期戦略が必要
1ターン先だけではなく、数ターン先の交代、削り、詰め筋を考える必要があります。たとえば「今は不利交換でも相手の高速アタッカーを削る価値がある」という判断は、短期報酬だけでは見えにくいです。
3.2 部分観測である
相手の全情報は見えていません。ルールにもよりますが、少なくとも対戦の途中では、相手の技構成、持ち物、努力値配分、性格などが完全には分からないことが多いです。つまり、将棋のような完全情報ゲームではなく、見えていない情報を推定しながら戦う必要があります。
3.3 行動が単純に見えて実は複雑
表面上は「4つの技から選ぶか、交代するか」に見えますが、実際には
タイプ相性
命中率
追加効果
先制技
素早さ関係
状態異常
天候
フィールド
ステルスロックなどの設置技
テラスタルや世代固有要素
が絡みます。1手の価値が盤面文脈で大きく変わります。
3.4 報酬が遅い
最終的な勝敗は試合の最後にしか決まりません。勝利だけを報酬にすると、序盤の行動が良かったのか悪かったのかが学習しづらくなります。
3.5 相手も意思決定する
対戦相手も行動を選ぶので、環境が固定ではありません。自己対戦を使うと、学習中に相手の強さも変わっていきます。これが学習を難しくします。
このため、ポケモン対戦は強化学習の教材として非常に豊かですが、同時にかなり手強い実験環境でもあります。
⸻
- 実装の全体構成
実用上の基本構成は次のとおりです。
RLアルゴリズム
↓
Gymnasium互換環境
↓
poke-env
↓
Pokémon Showdown
↓
対戦結果・盤面情報
ここでの役割分担は明確です。
Pokémon Showdown
対戦ルールを正しく処理するシミュレータです。Showdown は公式リポジトリで、対戦サイト、JavaScript ライブラリ、シミュレーション機能、コマンドラインツールなどを含むと説明されています。 
poke-env
Python から Showdown を扱うためのインターフェースです。プレイヤークラス、対戦オブジェクト、Gymnasium ラッパー、報酬・観測のカスタム例が用意されています。 
強化学習ライブラリ
PPO や DQN などのアルゴリズムを実装する部分です。Stable-Baselines3 は PyTorch ベースの実装群で、PPO を含む複数アルゴリズムを提供しています。PPO は A2C と TRPO の考え方を組み合わせ、更新時に新旧方策が離れすぎないよう clipping を使うと公式ドキュメントにあります。 
⸻
- なぜローカルサーバが必要なのか
poke-env の公式ドキュメントでは、公開サーバにも接続できますが、学習用途では private server の利用が望ましいと明記されています。理由は性能とレート制限です。さらに、ローカルサーバを使う場合、Player はデフォルトで localhost 向け設定を使えます。 
つまり、学習中に何千、何万戦も回したいなら、公開サーバに依存するのは現実的ではありません。研究や開発ではローカル Showdown を立てる前提で考えた方がよいです。
⸻
- 環境構築
6.1 Python 側
poke-env の導入要件は Python 3.10 以上です。インストールは pip install poke-env で可能です。Gymnasium や SB3 も合わせて入れます。 
pip install poke-env gymnasium stable-baselines3
6.2 Showdown 側
poke-env の Getting Started では、Showdown サーバ構築の基本手順として次の流れが示されています。Node.js v10+ を入れ、リポジトリを clone し、npm install し、設定ファイルをコピーし、node pokemon-showdown start --no-security で起動します。--no-security はレート制限や認証要件を外し、AI学習をしやすくしますが、重要なセキュリティ機能も無効化するので注意が必要です。 
git clone https://github.com/smogon/pokemon-showdown.git
cd pokemon-showdown
npm install
cp config/config-example.js config/config.js
node pokemon-showdown start --no-security
⸻
- どのルールから始めるべきか
最初の題材としては、シングルバトル、できれば ランダムバトル系 が扱いやすいです。理由はチーム構築を省けるからです。
もし最初から通常の 6vs6 構築戦をやると、問題は二段になります。
1. チームをどう作るか
2. そのチームをどう動かすか
これは一気に難しくなります。まずは「盤面の意思決定」に集中した方がよいです。したがって、
ランダムバトルで盤面操作だけ学ぶ
↓
固定チームで学ぶ
↓
複数チームに一般化する
↓
チーム構築も含めて考える
という順番が自然です。
⸻
- 強化学習で最重要なのは状態・行動・報酬の設計
ポケモン対戦を RL で扱うとき、難所はモデルそのものより、環境設計です。ここが雑だと、どんな高度なアルゴリズムを使っても強くなりません。
⸻
- 状態表現をどう作るか
ニューラルネットワークは「盤面」をそのまま理解できません。そこで、対戦の情報を固定長の数値ベクトルに変換します。これを埋め込み、あるいは観測ベクトルと考えます。
9.1 最小構成の状態
最初の最小構成なら、次のような特徴量から始められます。
自分の場のポケモンのHP比
相手の場のポケモンのHP比
自分の利用可能な技の威力
各技の相手へのタイプ相性倍率
自分の残り匹数
相手の残り匹数
例として、
[自分HP比, 相手HP比, 技1威力, 技2威力, 技3威力, 技4威力,
技1倍率, 技2倍率, 技3倍率, 技4倍率,
自分残数, 相手残数]
のような形です。
9.2 もう少し実用的な状態
実際にはこれだけでは弱いです。少なくとも次を入れたくなります。
自分の状態異常
相手の状態異常
能力ランク上昇・下降
天候
フィールド
ステルスロックやまきびし
交代可能な控えの情報
先手を取れるかの近似
残りPPの情報
テラスタル可能かどうか
9.3 観測設計の考え方
観測設計で大事なのは、「勝敗に効く情報を落とさない」ことです。
たとえば、相手がほのおタイプでこちらのみず技が使えるなら、その技は価値が高いです。しかし観測ベクトルにタイプ相性を入れていないと、モデルはその重要性を学ぶまで遠回りします。
逆に、あまりに情報を詰め込みすぎると、学習が遅くなることもあります。最初は
場の2体
使える技
タイプ相性
残数
簡単な場の状態
くらいから始め、必要に応じて増やすのがよいです。
⸻
- 行動空間をどう定義するか
10.1 基本形
シングルなら、ターンごとの行動はだいたい次です。
技1
技2
技3
技4
交代先1
交代先2
交代先3
交代先4
交代先5
最大 9 個前後の離散行動にできます。
10.2 行動マスクが必要
ただし、毎ターンすべてが有効ではありません。
瀕死の控えには交代できない
技が封じられている場合がある
アンコールやこだわりで制約される場合がある
PP が切れている場合がある
テラスタルは1回しか使えない
したがって、無効行動を選ばせない仕組みが重要です。これが action mask です。
もし行動マスクを入れずに「無効なら適当に代替行動にする」設計にすると、学習信号が濁りやすくなります。エージェントは「自分が何を選んだら実際に何が起きたか」を安定して学べなくなるからです。
⸻
- 報酬設計
報酬はかなり重要です。
11.1 勝敗だけでは足りない
いちばん自然な報酬は
勝ち +1
負け -1
です。しかしこれだけだと、試合の最後まで信号が来ません。序盤の良い行動も悪い行動も、最後にまとめてしか評価されません。
11.2 補助報酬を足す
そこで、実務では中間報酬を加えます。たとえば、
相手に与えたHPダメージに応じて加点
自分がHPを失ったら減点
相手を倒したら加点
自分が倒れたら減点
勝利時に大きく加点
です。
一例を数式で書くと、
r_t
= w1 * (相手HP減少量)
- w2 * (自分HP減少量)
- w3 * (相手瀕死数の増加)
- w4 * (自分瀕死数の増加)
- w5 * (勝利なら1、敗北なら-1)
のようになります。
11.3 shaping の注意点
中間報酬を入れすぎると、副作用もあります。
たとえば「与ダメージだけ高評価」にすると、勝ち筋よりも無意味な高火力連打を好むかもしれません。
「生存重視」にすると、負け筋でも無意味に粘るかもしれません。
したがって、最終勝利をいちばん重くしつつ、学習補助として軽い shaping を加えるのが一般的です。
⸻
- poke-env で観測と報酬を定義する
poke-env には Gymnasium ラッパーの RL 例があり、観測と報酬を定義してカスタムクラスを作る流れが示されています。公式 Examples でも、Reinforcement learning with the Gymnasium wrapper の節で、observations と rewards を定義し、環境を作り、学習し、評価する流れが用意されています。 
典型的な骨格は次のようになります。
import numpy as np
from poke_env.player import Gen9EnvSinglePlayer
class SimpleRLPlayer(Gen9EnvSinglePlayer):
def calc_reward(self, last_battle, current_battle):
return self.reward_computing_helper(
current_battle,
fainted_value=0.2,
hp_value=0.1,
victory_value=1.0
)
def embed_battle(self, battle):
moves_base_power = -np.ones(4, dtype=np.float32)
moves_multiplier = np.ones(4, dtype=np.float32)
for i, move in enumerate(battle.available_moves[:4]):
moves_base_power[i] = move.base_power / 100.0
if battle.opponent_active_pokemon is not None:
moves_multiplier[i] = move.type.damage_multiplier(
battle.opponent_active_pokemon.type_1,
battle.opponent_active_pokemon.type_2
)
remaining_team = len([m for m in battle.team.values() if not m.fainted]) / 6.0
remaining_opp = len([m for m in battle.opponent_team.values() if not m.fainted]) / 6.0
return np.concatenate([
moves_base_power,
moves_multiplier,
np.array([remaining_team, remaining_opp], dtype=np.float32)
])
この例はかなり簡略化していますが、考え方は分かりやすいです。
calc_reward
= 報酬関数
embed_battle
= 盤面を数値ベクトルに変換する関数
⸻
- どの強化学習アルゴリズムを使うか
13.1 初手の候補は PPO か DQN
離散行動問題なので、DQN 系でも始められます。ただし、実務では PPO の方が扱いやすい場合が多いです。
Stable-Baselines3 の公式ドキュメントでは、PPO は clipping を使って大きすぎる方策更新を防ぎ、さらに advantages の正規化や value function clipping などの実装上の工夫が入っていると説明されています。安定に学習させやすいのが長所です。 
13.2 どう選ぶか
DQN が向く場面
行動空間が単純な離散で、状態も固定ベクトルで、まず小さく試したいとき
PPO が向く場面
少し複雑な観測で、安定性を重視したいとき
最初のおすすめは PPO です。
⸻
- 学習コードの最小例
poke-env で作った環境を SB3 に渡すと、形としては次のようになります。
from stable_baselines3 import PPO
env = SimpleRLPlayer(battle_format="gen9randombattle")
model = PPO(
"MlpPolicy",
env,
verbose=1,
)
model.learn(total_timesteps=100_000)
ここでは MLP、つまり全結合ネットワークを使っています。最初はこれで十分です。画像入力ではないので、CNN から始める必要はありません。
⸻
- 学習の進め方
15.1 いきなり本番相手に出さない
最初の目標は、「強い人間に勝つ」ではありません。順番はこうです。
ランダム行動相手に安定して勝つ
↓
単純ルールベース相手に勝つ
↓
過去の自分に勝つ
↓
複数相手への平均勝率を上げる
↓
必要なら人間やラダーへ
15.2 カリキュラム学習
最初から強い相手だけにすると、ほとんど負け続けて学習が進みにくいです。そこで、弱い相手から順に難易度を上げるカリキュラム学習が有効です。
たとえば、
第1段階
ランダムプレイヤー
第2段階
最大火力を選びがちな単純 AI
第3段階
状態異常や交代も使うルールベース AI
第4段階
自己対戦
という流れです。
⸻
- 自己対戦の考え方
自己対戦は、現在のエージェントや過去のエージェントを相手にして学習する方法です。対戦ゲームでは非常に強力です。
ただし注意点もあります。
相手が自分だけだと、偏った戦略に閉じる
特定の戦い方だけに異常に強くなる
同系統メタにだけ最適化される
そのため、
ランダム AI
ルールベース AI
最新モデル
少し前のモデル
かなり前のモデル
を混ぜる方が安定しやすいです。
⸻
- 評価をどうするか
poke-env の Examples には evaluate_player や cross_evaluate を使った評価例があります。単発の勝率ではなく、複数エージェントを相互に戦わせて比較する発想が重要です。 
評価では少なくとも次を見ます。
平均勝率
先手・後手やマッチアップによる偏り
試合長
降参率
無効行動率
タイプ相性が有利なときの勝率
不利対面からの巻き返し率
単に 100 戦で 60 勝した、だけでは不十分です。相手分布が変わると性能が崩れることが多いからです。
⸻
- 初学者がはまりやすい失敗
18.1 状態が貧弱すぎる
技威力しか見ていないと、タイプ相性、状態異常、交代の価値をうまく学べません。
その結果、「高威力技を押すだけ」の AI になりやすいです。
18.2 報酬が偏っている
与ダメージだけに報酬を振ると、勝率ではなく瞬間火力だけを追いやすいです。
18.3 行動マスクがない
無効行動を多発すると、学習がかなり不安定になります。
18.4 相手が単純すぎる
ランダム相手に強くなっても、交代や積み技を使う相手には崩れやすいです。
18.5 いきなり観測を盛りすぎる
最初から全ポケモン情報、全履歴、全状態を詰め込むと、ネットワークが重くなり、デバッグもしにくくなります。
⸻
- 実用的な発展方向
19.1 履歴を使う
ポケモン対戦は部分観測なので、現在盤面だけでは足りないことがあります。過去数ターンの行動履歴を入れると改善する場合があります。
19.2 相手の未確定情報を推定する
相手の持ち物や型を確率分布として持つ設計にすると、より実戦的になります。
19.3 盤面表現を構造化する
単純なベクトルではなく、
場のポケモン
控え
相手の公開済み情報
場の効果
を別々にエンコードして最後に結合する方が自然なこともあります。
19.4 階層化する
「今ターン何をするか」と「どの勝ち筋を目指すか」は別レベルの意思決定です。
上位方策で方針を選び、下位方策で具体行動を選ぶ階層型も考えられます。
⸻
- 最初に作るべき最小プロジェクト
最短で始めるなら、次の仕様がよいです。
ルール
= Gen 9 Random Battle のシングル
観測
= 技威力、タイプ倍率、残数、簡単な HP 情報
行動
= 4技 + 交代
報酬
= 勝敗 + 軽い HP shaping
相手
= RandomPlayer と MaxDamage 風ルールベース
アルゴリズム
= PPO
評価
= 100 戦ごとの勝率推移
この構成なら、実装量を抑えつつ、RL の基礎が全部入ります。
⸻
- 実際の開発順序
おすすめの順序を、かなり現実的に書くとこうです。
第1段階
Showdown ローカルサーバを起動する。
poke-env の RandomPlayer 同士を戦わせる。
ここで通信が通ることを確認する。 poke-env には built-in agents や quickstart があり、対戦を作って結果を確認する流れも公式に用意されています。 
第2段階
最大火力を選ぶだけのルールベース AI を作る。
choose_move を override し、battle.available_moves から最大威力技を選ぶ。こうした built-in / custom player の考え方は quickstart にもあります。 
第3段階
embed_battle と calc_reward を定義して RL 環境にする。
第4段階
PPO で学習し、ランダム AI に対する勝率を測る。
第5段階
状態を強化する。
タイプ相性、場の状態、状態異常、テラスタル可否などを追加する。
第6段階
自己対戦や混合相手学習へ進む。
⸻
- 技術的に見たこのテーマの本質
ポケモン対戦 RL の本質は、「強化学習アルゴリズム選び」より、問題設定の切り方にあります。
同じ PPO を使っても、
観測が良い
行動マスクがある
報酬が自然
相手分布が適切
なら強くなります。
逆に、
観測が弱い
無効行動が多い
報酬が偏る
相手が単調
だと、学習器だけ高級でも伸びません。
つまり、このテーマは「RL を回す」より、「対戦ゲームを RL に翻訳する設計力」が重要です。
⸻
- まとめ
ポケモン対戦を強化学習で扱うなら、現在の標準構成は Pokémon Showdown + poke-env + Gymnasium + SB3 です。poke-env は Gymnasium ラッパー、報酬・観測のカスタマイズ、対戦評価の例を備えており、学習用途ではローカル Showdown サーバの利用が公式に推奨されています。PPO は SB3 で使える代表的アルゴリズムで、方策更新を clipping で安定化する設計です。