はじめに
普段からポケモンカードを含めたカードゲームが好きで、AIエージェントを使ってポケモンカードを対戦させる大会があることを知り、興味を持ちました。
AIエージェントの開発経験はなかったため、最初はかなり難しそうだと感じていました。
しかし、実際には公式のサンプルエージェントが用意されており、まずはサンプルを動かして対戦を見るところから始めることができました。
この記事では、公式のルールベースAIを動かし、実際の対戦から気になった挙動を見つけ、ポケカの知識を使って改善するまでに行ったことをまとめます。
PTCG AI Battle Challengeに参加してみる
今回参加したのは、**Pokémon Trading Card Game AI Battle Challenge(ポケカABC)**のシミュレーション部門です。
開発したAIエージェントをKaggleへ提出すると、他の参加者のエージェントとの対戦が自動で行われます。
今回は公式で公開されていたメガルカリオexデッキのルールベースエージェントをベースにしました。
最初はコードを変更せず、公式サンプルをそのまま提出しました。
Episodeを確認すると、
- ポケモンを場に出す
- エネルギーを付ける
- 進化する
- トレーナーズを使う
- ポケモンを入れ替える
- ワザを使って攻撃する
といった一連のプレイを自動で行っています。
「サンプルだけでもこんなにちゃんとポケカをプレイするんだ」
というのが最初の感想でした。
対戦を見て気になったこと
一方で、普段ポケカを遊んでいる視点で試合を見ていると、いくつか気になる判断もありました。
- ダメージを増やすカードを不要な場面でも使用する
- 特性などによってダメージを受けないポケモンにも攻撃する
-
3-1-2のようなサイドレースまでは考慮されていない
今回はこの中から、特に挙動が分かりやすかったパワープロテインの使用判断を改善してみることにしました。
パワープロテインとは
パワープロテイン(英語名:Premium Power Pro)は、この番、自分の闘ポケモンが相手のバトルポケモンへ与えるワザのダメージを30増やすグッズです。
そのため、
- そのターンに攻撃できない
- パワープロテインがなくても既に相手を倒せる
- パワープロテインを使っても相手を倒せない
といった場面では、使わずに温存した方がよい場合があります。
しかし公式サンプルの対戦では、そのような場面でもパワープロテインを使用することがありました。
ルールベースAIはどう行動を決めているのか
原因を調べるため、公式サンプルのコードを読みました。
最初はObservationやselect、OptionTypeなどが何を表しているのかも分かりませんでしたが、コードを追っていくと大まかな流れはシンプルでした。
現在のゲーム状態
↓
現在選べる行動を取得
↓
各行動にscoreを付ける
↓
scoreが高い順に並べる
↓
最もscoreが高い行動を選択
イメージとしては、以下のような処理です。
scores = []
for o in select.option:
score = 0
# 行動の種類や現在の盤面に応じてscoreを変更
scores.append(score)
最後にscoreが高い順へ並べ、最も評価の高い行動を返します。
desc_indices = [
i for i, _ in sorted(
enumerate(scores),
key=lambda x: x[1],
reverse=True
)
]
return desc_indices[:select.maxCount]
つまり今回のルールベースAIは、
現在選べる行動を、人間が設定したルールとscoreによって評価し、一番高いものを選択する
という仕組みでした。
攻撃ではAttackPlanを先に作る
攻撃については、さらにAttackPlanという攻撃計画を先に作っています。
大まかには、
自分のポケモン
×
使えるワザ
×
相手のポケモン
↓
各組み合わせを評価
↓
最も評価の高い攻撃をAttackPlanへ保存
という流れです。
AttackPlanには、
- どのポケモンで攻撃するか
- どの相手を狙うか
- どのワザを使うか
- 攻撃後に相手のHPがどの程度残るか
- エネルギーを追加する必要があるか
などが保存されます。
ここまで読んだことで、
先に攻撃プランを作り、そのプランを実現するためにエネルギーやトレーナーズなどの行動を選んでいる
という構造が見えてきました。
パワープロテインの無駄打ちの原因を調べる
パワープロテインの処理を確認すると、攻撃可能な場合には高いscoreが設定されていました。
また、特定の状況では、
パワープロテイン : 3050
ゼイユ : 3000
となるケースもありました。
ルールベースAIはscoreが高い行動を優先するため、この場合はゼイユより先にパワープロテインを使用します。
Episodeを見たときに感じた、
「なぜ今パワープロテインを使うんだろう?」
という疑問とコードがここでつながりました。
そこで今回は、
「使用できるか」ではなく「今使用する意味があるか」
を判断させるように変更しました。
パワープロテインを必要なときだけ使うようにする
実際に自分がポケカをプレイするとしたらどう判断するかを整理し、以下のルールにしました。
| 状況 | パワープロテインの判断 |
|---|---|
| 攻撃できない | 基本使わない |
| そのまま既にKOできる | 使わない |
| 全部使ってもKOできない | 使わない |
| パワープロテイン込みならKOできる | 必要枚数だけ使う |
| ゼイユを使う予定 | ゼイユより先に使う |
| リーリエの決心を使う予定 | 基本使わない |
| リーリエ前でもパワープロテインでKO可能 | 必要枚数だけ使う |
使用枚数を記録する
まず、このターンに何枚パワープロテインを使用したかを記録する変数を追加しました。
# パワープロテインの使用枚数を記録
power_pro_used = 0
新しいターンになった場合は0へ戻します。
global power_pro_used
if pre_turn != state.turn:
# 新しいターンになったら使用枚数をリセット
power_pro_used = 0
実際にパワープロテインが選択された場合は使用枚数を増やします。
if o.type == OptionType.PLAY:
card = get_card(obs, AreaType.HAND, o.index, my_index)
if card.id == Premium_Power_Pro:
# パワープロテインを使用したので1枚追加
power_pro_used += 1
KOに必要な枚数を判断する
AttackPlanには、予定している攻撃後に相手のHPがどの程度残るかが保存されています。
そこから、既に使用したパワープロテイン分のダメージを差し引きます。
remaining_hp = plan.remain_hp - (power_pro_used * 30)
例えば予定攻撃後の残りHPが50の場合、
0枚使用 → 残り50
1枚使用 → 残り20
2枚使用 → 残り-10(KO)
となります。
これによって、2枚でKOできる場合に3枚目まで無駄に使うことを防げます。
サポートによって使用判断を変える
さらに、手札にあるサポートの中から、次にどのカードを使いそうかを判定しました。
# ボスの指令を使用しそうか
will_use_boss = (
not state.supporterPlayed
and hand_counts[Boss_Orders] > 0
and plan.target >= 1
)
# リーリエの決心を使用しそうか
will_use_lillie = (
not state.supporterPlayed
and hand_counts[Lillie_Determination] > 0
and not will_use_boss
)
# ゼイユを使用しそうか
will_use_carmine = (
not state.supporterPlayed
and hand_counts[Carmine] > 0
and not will_use_boss
and not will_use_lillie
)
その上で、パワープロテインの使用条件を変更しました。
remaining_hp = plan.remain_hp - (power_pro_used * 30)
# リーリエの決心を使用する予定の場合
if will_use_lillie:
# 現在攻撃可能で、
# そのままでは倒せないが、
# 手札のパワープロテインを使えば倒せる場合のみ使用する
if (
can_attack
and remaining_hp > 0
and remaining_hp <= hand_counts[Premium_Power_Pro] * 30
):
score = 5000
else:
score = -1
# ゼイユを使用する予定の場合
elif will_use_carmine:
# ゼイユで手札から捨てられる前に使用する
score = 5000
# 攻撃できない場合は使用しない
elif not can_attack:
score = -1
# 予定している攻撃だけで既に倒せる場合は使用しない
elif remaining_hp <= 0:
score = -1
# 残っているパワープロテインを使えば倒せる場合は使用する
elif remaining_hp <= hand_counts[Premium_Power_Pro] * 30:
score = 5000
# 全て使用しても倒せない場合は使用しない
else:
score = -1
単純にscoreの数字だけを変更するのではなく、現在の盤面や、その後に使用するサポートによって判断を変えるようにしました。
改善後のEpisodeを確認する
修正版を再度Kaggleへ提出し、いくつかのEpisodeを確認しました。
不要な場合はパワープロテインを温存
パワープロテインを使ってもKOにつながらない場面では、無駄に使用せず、手札に残したままターンを返すようになりました。
リーリエの決心では基本的に使わない
リーリエの決心を使用する場合、パワープロテインがKOに必要でなければ先に使用しません。
実際のログでも、
Played Lillie's Determination.
Premium Power Pro moved from Hand to Deck.
Premium Power Pro moved from Hand to Deck.
Deck was shuffled.
となっており、パワープロテインを無駄に使用せず、そのまま山札へ戻していることを確認できました。
ゼイユの場合は先に使う
一方、ゼイユは手札をトラッシュするため、ゼイユを使用する場合は先にパワープロテインを使用するようにしました。
手札にパワープロテインとゼイユがある状態から、
まずパワープロテインを使用し、
その後にゼイユを使用する流れを確認できました。
今回指定した条件が、実際の対戦でも意図した通りに反映されていることを確認できました。
自分で考えた条件分岐によってAIのプレイが実際に変わるのを見ることができたのは、かなり面白かったです。
改善したらスコアは上がったのか
挙動が改善されたため、レーティングも大きく上がるのではないかと思いました。
しかし、実際には想像していたほど大きな変化はありませんでした。
執筆時点では、
| Submission | Score |
|---|---|
| Version 1 | 625.6 |
| PowerPro Improvement | 624.1 |
となっていました。
提出後も自動対戦によってScoreは変動するため単純な比較はできませんが、
人間から見て明らかな無駄行動を1つ減らしても、それだけでゲーム全体の勝率が大きく上がるわけではない
ということも分かりました。
ポケモンカードでは、
- 攻撃対象
- エネルギー管理
- サイドレース
- カード固有効果
- 次のターンの盤面
など、1試合の中に非常に多くの判断があります。
1つの局所的な判断を改善するだけでは大きく勝率が変わらないところも、ゲームAIの難しい部分だと感じました。
やってみて感じたこと
参加する前は、**「AIエージェントを作る」**と聞くだけで、かなり参入難易度が高いと思っていました。
しかし、公式サンプルやシミュレーターが用意されていたため、実際には「まず動かしてみる」ところまでのハードルは想像より低かったです。
そして今回特に面白かったのが、ポケカの知識がそのままAIの改善に使えたことです。
今回行ったことを振り返ると、
対戦を見る
↓
ポケカの知識から違和感を見つける
↓
コードから原因を探す
↓
判断ルールを変更する
↓
もう一度対戦を見る
というサイクルでした。
コードだけを見ていたら、単にscore = 5000という数値を見ても特に違和感を持たなかったかもしれません。
しかし実際の対戦を見たことで、
「そのパワープロテインは今使わなくてもいいのでは?」
と気付き、コードの調査と改善につなげることができました。
始めてみると思っていた以上に面白く、もっと早く取り組んでいれば、
- サイドレースの考慮
- ダメージ無効などカード固有効果への対応
- 他の判断ロジックの改善
- デッキレシピそのものの変更
なども試してみたかったです。
今後やってみたいこと
今回は、
この状況なら +5000
この状況なら -1
のように、人間が条件や重みを考えて設定しました。
実際にルールベースAIを触ったことで、次に興味を持ったのが、
「この重み付け自体をAIに学習させられないか」
ということです。
例えば、
- KOできる価値
- 取れるサイド枚数
- 場に残るエネルギー
- 手札の消費
- 次のターンの盤面
などの重みを、人間が固定値として設定するのではなく、大量の対戦結果から勝率が高くなる方向へ調整できれば、さらに面白そうです。
自己対戦や強化学習などを使えば、
AI同士で大量に対戦
↓
勝敗を記録
↓
行動の評価を更新
↓
再び対戦
といった形で改善していくことも考えられます。
今回の参加を通して、
AIの知識だけでなく、対象となるゲームの知識もエージェント開発では大きな武器になる
ことを実感できました。
次に同じような機会があれば、難しそうだからと様子を見るのではなく、まず動かすところから早めに挑戦してみたいと思います。








