機械学習周りのプログラミング中心。 イベント情報
ポケモンバトルAI本電子書籍通販中

ゲーム木探索入門 part05 交互手番MCTSの実装【PokéAI】

前回は原始モンテカルロ法というもっとも単純な探索手法を試しました。原始モンテカルロ法では、ルート局面(行動を決定したい局面)でとれる行動それぞれについて、その行動をとった直後の局面からランダムにゲームを進めた際の勝率を比較して行動を決定します。この手法でもただランダムに行動を決定するよりはましですが、最初の行動以外すべてランダムであるため、大量の悪手を含んだゲーム進行の結果得られる勝率を元に行動を決定するため貧弱な手法といえます。勝率の推定を改善する手法として、モンテカルロ木探索(MCTS)があります。MCTSはもともと囲碁AIのために開発された手法です。囲碁ポケモンバトルの主な違いは、自分と相手が交互に行動を選択する、伏せられている情報がない、ランダム性がないという点です。よりポケモンバトルに近い性質のゲームへの応用も研究されており今後試しますが、今回は囲碁向けのMCTSアルゴリズムをできるだけ少ない変更でポケモンバトルに応用します。

アルゴリズム

MCTSの基本的な説明は囲碁関係の文献を参照してください(本にするときはポケモンバトルに即してちゃんと書きます…)。書籍だと囲碁ディープラーニングプログラミングがわかりやすいです。AlphaGoの文脈でMCTSが解説されることも多いですが、今回の実装に深層学習の要素はないので注意してください。 原始モンテカルロ法に引き続き、本来のポケモンバトルにはない仮定として、相手のパーティ構成がわかっている、ランダム性がないという条件でMCTSを実装します。 これらの仮定を置くことで囲碁にゲームの性質を近づけることができますが、囲碁では交互に行動を選択する一方で、ポケモンバトルでは同時に行動を選択するという違いについては何らかの対処をしないとMCTSを実装することができません。 この課題に対しては、自分が行動を選択し、次に相手が行動を選択するという近似を行います。シミュレータの状態と自分が選んだ行動の組み合わせを状態とみなし、この状態を入力として相手は行動を選ぶと考えます。

MCTS実装のコア部分を示します。完全なコードはこちら。 ポイントは、ノードが持つ状態としてシミュレータの状態のほかに、相手が選んだ行動を含ませる点です。相手が選んだ行動が入っているノードの子ノードは、相手と自分の行動の組み合わせでシミュレータを1ターン進めた状態を保持することになります。

// シミュレータの状態simに対し、プレイヤーsideidの行動を決定する
go(sim: Sim, sideid: SideID): string | null {
    const root = new MCTSNode(new PRNG(), sim, sideid, undefined);
    const playoutPolicy = new AIRandom2({ switchRatio: this.playoutSwitchRatio });

    // ルート局面から木構造をたどってプレイアウトを繰り返す
    for (let i = 0; i < this.playoutCount; i++) {
        let backupNodes: MCTSNode[] = [];
        let node = root;
        backupNodes.push(node);
        while ((!node.canAddChild()) && (!node.isTerminal)) {
            node = this.selectChild(node);
            backupNodes.push(node);
        }

        if (node.canAddChild()) {
            node = node.addRandomChild();
            backupNodes.push(node);
        }

        let winner: SideID;
        if (node.isTerminal) {
            winner = node.winnerIfTerminal!;
        } else {
            // プレイアウトする
            const copySim = node.gameState.clone();
            if (node.opponentMove !== undefined) {
                // 片側の行動だけ決まっている状態なので、もう片方を埋めて1ターン進める
                const leafChoice = playoutPolicy.go(copySim, node.sideid);
                const bothChoice = node.sideid === 'p1' ? [leafChoice, node.opponentMove] : [node.opponentMove, leafChoice];
                copySim.choose(bothChoice);
            }
            let { winner: playoutWinner } = playout(copySim, [playoutPolicy, playoutPolicy]);
            winner = playoutWinner || sideid;// 引き分けは珍しいので、とりあえずsideid側の勝ち扱い
        }

        for(let i = backupNodes.length-1; i >= 0; i--) {
            backupNodes[i].recordWin(winner);
        }
    }

    // ルート局面の子ノードで勝率最大のノードを選択
    let bestMove: string | null = null;
    let bestPct: number = -1.0;
    for (const [move, node] of root.children.entries()) {
        const pct = node.winningPct(sideid);
        if (pct > bestPct) {
            bestPct = pct;
            bestMove = move;
        }
    }

    return bestMove;
}

// MCTSのノードを表現するオブジェクト
class MCTSNode {
    winCounts: {[sideid in SideID]: number};
    numRollouts: number;
    children: Map<string|null, MCTSNode>;
    unvisitedMoves: (string|null)[];
    isTerminal: boolean;
    winnerIfTerminal?: SideID;
    constructor(public prng: any,
        public gameState: Sim, // シミュレータの状態
        public sideid: SideID, // このノードの手番プレイヤー
        // このシミュレータの状態に対し、すでに相手が行動を決定している場合その行動
        public opponentMove?: string | null
    ) {
        this.winCounts = {'p1': 0, 'p2': 0};
        this.numRollouts = 0;
        this.children = new Map();
        const isTerminal = this.gameState.getEnded();
        this.isTerminal = isTerminal;
        if (isTerminal) {
            this.winnerIfTerminal = this.gameState.getWinner();
            this.unvisitedMoves = [];
        } else {
            this.winnerIfTerminal = undefined;
            const choices = enumChoices(gameState.getRequest(sideid));
            if (choices.length > 0) {
                this.unvisitedMoves = choices.map((c) => c.key);
            } else {
                this.unvisitedMoves = [null]; // パスの1手だけを選択肢とみなす
            }
        }
        
    }

    // まだ子ノードを生成していない選択肢から1つランダムに選んで子ノードを作成
    addRandomChild(): MCTSNode {
        const unvisitedLength = this.unvisitedMoves.length;
        const index = unvisitedLength === 1 ? 0 : this.prng.next(unvisitedLength);
        const move = this.unvisitedMoves[index];
        this.unvisitedMoves.splice(index, 1);
        // moveを適用した後の状態を生成
        // シミュレータは決定論的に動作するという仮定
        let newNode: MCTSNode;
        const opponentSideId = invSideID(this.sideid);
        if (this.opponentMove !== undefined) {
            // 自分と相手の行動が揃ったので1ターン進める
            const copySim = this.gameState.clone();
            const bothChoice = this.sideid === 'p1' ? [move, this.opponentMove] : [this.opponentMove, move];
            copySim.choose(bothChoice); // シミュレータに行動を与えて進める
            newNode = new MCTSNode(this.prng, copySim, opponentSideId, undefined); // undefinedが相手の行動未定を表す
        } else {
            // シミュレータはまだ動かせない。相手の行動を決めるフェーズに進む
            newNode = new MCTSNode(this.prng, this.gameState, opponentSideId, move);
        }
        this.children.set(move, newNode);
        return newNode;
    }
}

今回のアルゴリズムにおける、自分が行動を選択し、次に相手が行動を選択するという近似には問題点があります。じゃんけんのようなゲームを考えるとわかりますが、相手の行動が決まっている状態のノードで自分の行動を決めると、相手の手に応じて都合のいい行動を選ぶ結果になり、行動を同時に選ぶ場合と比べ非対称な先手と後手が生じます。この問題を考慮したアルゴリズムを今後利用する予定です。

実験

MCTSと原始モンテカルロ法の強さを比較する実験を行います。

使用するパーティ等の条件は原始モンテカルロ法の時と同じです。

まずは、ランダム、原始モンテカルロ法とともに、MCTSのプレイアウト回数を変えて強さを比較しました。ランダムプレイアウト中に交代を選ぶ確率は10%です。MCTSの探索において未知の行動を探索するか既知の勝率の高い行動をより深く探索するかのバランスを決める温度パラメータは1.0としました。温度パラメータについては0.5, 1.0, 2.0で別途比較し、大きな差はありませんが1.0が最も強いという結果でした。比較結果を示します。

手法 プレイアウト回数 平均レート
ランダム - 1396
原始モンテカルロ法 100 1506
MCTS 30 1474
MCTS 100 1509
MCTS 300 1537
MCTS 1000 1579

計算時間はプレイアウト回数に比例します。また、原始モンテカルロ法MCTSで、プレイアウト回数が同じならほぼ同じ計算時間となります。MCTSにおいてプレイアウト回数を増やせば増やすほど強くなること、また同じプレイアウト回数では原始モンテカルロ法と同等以上の強さになることがわかりました。

次に、プレイアウト回数1000回での原始モンテカルロ法MCTSの比較を行いました。

手法 プレイアウト回数 平均レート
ランダム - 1424
原始モンテカルロ法 1000 1530
MCTS 1000 1546

やはりMCTSのほうが若干高いレートとなっています。

さらにプレイアウト回数を増加させたときに強くなるかどうか、MCTSのみで比較を行いました。

手法 プレイアウト回数 平均レート
MCTS 1000 1481
MCTS 3000 1519

プレイアウト回数3000回は、1000回の場合より強くなることがわかりました。計算に30時間を要したため、これ以上の回数を試すのは困難です。

今回は、ポケモンバトルを交互手番のゲームに近似してMCTSを実装しました。原始モンテカルロ法と比べ、同じプレイアウト回数(計算時間)でより強い可能性が高いことがわかりました。 木構造を探索する思考過程を可視化できないか検討しています。

ゲーム木探索入門 part04 原始モンテカルロ法【PokéAI】

ゲーム木探索を用いたポケモンバトルAIの最も単純なアルゴリズムとして、原始モンテカルロ法を試します。 前回から長らく期間が空いてしまいましたが、再開していきます。

アルゴリズム

ここでは原始モンテカルロ法(pure Monte Carlo search)を試します。

原始モンテカルロ法は、現在の局面からランダムに手を選んで1ターン局面を進め、そこからプレイアウトするという処理を何度も行います。そして、現在の局面で選べる手のうちで勝率が最も高かった選択肢を選ぶ手法です。プレイアウトとは、ランダムに選択肢を選んでゲームの決着がつくまで局面を進めることです。ポケモンでは相手も同時に手を選ぶことになりますが、相手の手はプレイアウトの一部として、ランダムに決めることにします。 なおこの手法の重要な注意点として、相手のパーティ構成がわかっているという仮定が入ります。探索中に、相手の行動をランダムに選んでバトルを進めて勝敗を得るというステップが入っているためです。探索過程で具体的な選択肢を列挙せずランダムに選ぶというブラックボックス状態であっても、例えば相手が一撃必殺技を持っているかどうかによって、こちらが積み技を使う選択肢と攻撃技で目の前の相手をすぐ倒そうとする選択肢の勝率が変わると考えられます。当面はこの仮定が入った状態のアルゴリズムを考えることとなります。

実際のコードからコア部分をコメント付きで掲載します。

export class AIMC extends AIBase {
    playoutCount: number; // 行動決定のためのプレイアウト回数
    // 局面を受け取り、最適な手を返す関数
    go(sim: Sim /* 現在局面を表すシミュレータ */, sideid: SideID /* 自分がプレイヤー1か2か */): string | null /* 最適な手 */ {
        const choices = this.enumChoices(sim.getRequest(sideid)); // 選択肢を列挙(技1, 技2, ...)
        const playoutPolicy = new AIRandom2(); // プレイアウト中の行動決定ポリシー(ここではランダム)
        if (choices.length > 0) {
            const playoutPerAction = Math.floor(this.playoutCount / choices.length); // 1つの選択肢に割り当てるプレイアウトの回数を、単純に全プレイアウト回数を選択肢の数で割って定める
            const winCounts = (new Array(choices.length)).fill(0); // 各選択肢を選んだあとの勝利回数を入れる配列
            for (let c = 0; c < choices.length; c++) { // 各選択肢ごとにループ
                for (let i = 0; i < playoutPerAction; i++) { // 選択肢1つに対する複数回のプレイアウト
                    const copySim = sim.clone(); // シミュレータをコピーし、実際の状態を動かさずに思考できるようにする
                    const opponentChoice = playoutPolicy.go(copySim, invSideID(sideid)); // 相手の行動をランダムに決める
                    const bothChoice = sideid === 'p1' ? [choices[c].key, opponentChoice] : [opponentChoice, choices[c].key];
                    copySim.choose(bothChoice); // 1ターン進める
                    const { winner } = playout(copySim, [playoutPolicy, playoutPolicy]); // プレイアウトして勝者を決める
                    if (winner === sideid) {
                        winCounts[c]++; // 自分が勝った回数を集計
                    }
                }
            }
            // 勝利回数最大の選択肢を、探索結果として返す
            const bestIdx = argsort(winCounts)[winCounts.length - 1];
            return choices[bestIdx].key;
        } else {
            return null;
        }
    }
}

実験

実験条件

プレイアウト回数(コード中playoutCount)を変えて強さを比較します。プレイアウト回数10, 30, 100, 300, 1000およびランダムに行動を選ぶアルゴリズムで合計6種類のポリシーを比較します。各ポリシーが10個のパーティを操作し、合計60プレイヤーでレーティングバトルを行います。各プレイヤーのレートをポリシーごとに平均してポリシーの強さとします。 プレイアウト中に使用する行動決定関数AIRandom2では、交代を選択する確率を10%に設定しました。 1プレイヤー当たり10回の対戦を行いました。

実験結果

実験結果を示します。

プレイアウト回数 平均レート
ランダム 1451
10 1415
30 1485
100 1507
300 1564
1000 1577

プレイアウト回数が増加するほど強くなることがわかりました。プレイアウト回数10の場合はランダムに行動するより弱いという結果になっています。この原因を考察します。ランダムの場合は交代確率を10%に設定しているのに対し、プレイアウトを行う場合は技を出す場合も交代する場合も選択肢として平等に扱っています。各選択肢を選んだ場合のプレイアウトの試行回数が少なすぎて選択肢の良し悪しがうまく推定できない(技の命中や相手の技選択に強く依存する)ため、効果の低い交代を選ぶ確率が上がっているのではないかと考えられます。

行動選択の考察

いくつかの実例をもとに、各行動の勝率がどのように見積もられたかを考察します。

下の例では、p2(プレイヤー2)がプレイアウト1000回の強いプレイヤーです(p1は10回)。技名の下および控えポケモン名の右の数値がプレイアウトにより得られた勝率を表します。この例では、おんがえしが勝率0.96で最も高く、探索結果として選ばれています。ばくれつパンチでも倒せるのは同じですが命中率が低いデメリットが勝率に反映されています。

f:id:select766:20210515180408p:plain
行動選択結果

下の例では、p1とp2両方がプレイアウト1000回のプレイヤーです。双方残りポケモン1体です。カビゴンのHPがほとんど減らず、ケンタロスのHPが減ることで勝率が変化していくことがわかります。ところで、ケンタロスのつのドリル(一撃必殺技)の最終ターンでの勝率が0.12となっています。命中率は30%なので勝率0.3になってほしいところですが、そうなっていません。これはシミュレータの乱数がバトル開始時に決定しており、同じ行動を選択すれば同じ結果になる仕様となっているためです。シミュレータの乱数の内部状態では、このターンでつのドリルを選択したら外れることになっているものと推測されます。この仕様もまた、技が当たるか当たらないかという乱数が間接的に探索アルゴリズムに漏れているということになります。

f:id:select766:20210515180914p:plain
行動選択結果

下の例では、p1とp2両方がプレイアウト1000回のプレイヤーです。双方ポケモン3体が生き残っている状態です。ブラッキーは体力満タンで無意味なねむるを選択してしまっています。バトルの決着がつくまでのターン数が多く、このターン以後の行動選択はすべてランダムであるためミスが生じる確率が高いので、ここで無駄な行動をしたとしてもあまり影響がないというのがこの選択の原因であると考えられます。

f:id:select766:20210515183438p:plain
行動選択結果

以上のように、バトル終盤であればある程度適切な行動選択ができていますが、序盤ではうまくいっていないということが確認できました。 次回は、より高度な探索手法を試そうと考えています。

第31回世界コンピュータ将棋選手権の参加記録(ねね将棋)

ねね将棋は2021年5月3日~5日にかけてオンラインで行われた第31回世界コンピュータ将棋選手権(コンピュータ将棋協会主催)に参加しました。 対局に付き合ってくださった方、運営の方、応援してくださった方に感謝申し上げます。 簡単に参加記録を残しておきます。

ソフトの内容

ねね将棋はいわゆるDL(Deep Learning)系のエンジンで、昨年までは独自に実装したMCTS探索部および、KPPT型の既存エンジンで生成した棋譜を用いた教師あり学習によるCNN型の評価関数モデルを用いていました。dlshogiと比べ評価関数の精度が決定的に劣る状況となっていたため、今回からdlshogiをベースとして用いて改良を加える方式に転換しました。

今回の改良点は、MCTSベースの探索で起きやすい読み抜けを減らすための、αβ法ベースの探索エンジンとの組み合わせでした。具体的な手法は過去の記事をご覧ください。

select766.hatenablog.com

今回の手法の都合上、GPUとCPUの性能の比率がちょうどよい範囲にある必要があります。自宅のPCがそれに合致するので、自宅のPCでソフトを動かすことにしました。クラウドだと、GPUの性能が強すぎてうまく動かないためです。GPUは3年ほど前に3万円で購入したGTX1060という安い機種です。

対局結果

一次予選

一次予選は二次予選シードを除く34チームが参加しました。

参加ソフト間の棋力差が大きいため、ほとんどの対局は一方的な内容になりました。

すいしょう戦では、8手目で大優勢になったものの詰めるまでには77手かかりました。すいしょうの評価関数は駒割りのみとのことですが、受けに底力を感じました。私が人力で指したら多分勝てません。

アルゴリズムや評価関数の性能差はあまり大きくないと思われるMiacis戦では、ねね将棋は2300nps程度、Miacisは数万nps以上出ているという状況でした。ハードの性能差が致命的な差でした。

結果は5勝3敗で11位となりました。 二次予選進出チームを決める際には勝ち数や直接対決の結果など複数の指標により序列をつけますが、AobaZeroとは全く同じ値となり優劣がつきませんでした。しかもねね将棋とAobaZeroがボーダーライン上でどちらか片方だけが進出可能という状況でした。ルール上の最終の条項である、シード順(前回大会での順位)による決定が行われ、シード順が1位だけ高かったねね将棋が二次予選に進出となりました。ボーダーライン上でこの規定が適用された例は初のようです。

二次予選

DaigorillaEX戦は、途中ねね将棋が優勢でしたが相手をうまく寄せることができず、逆転負けとなりました。YouTubeでのプロ棋士の先生の解説でも同様の見解でした。

f:id:select766:20210505174551p:plain
DaigorillaEX戦の評価値

TMOQ戦は、相手の定跡を抜けたところで後手の相手の評価値が100程度となり、定跡を使わないことによるデメリットが生じました。

Apery戦は優勢でしたが、寄せきることができず320手に達して引き分けとなりました。

寄せ≒終盤がよくないというのは自前で評価関数を学習させていた時代から感じていた課題で、DL系の弱点に思えます。細い詰み筋を見つけられないというよりは、評価値が一定以上になると緩慢な手を指すような印象を受けています。

習甦戦では、途中まで拮抗していたように見えましたが120手目付近で一気に評価値が悪くなり負けました。読み抜けがあったのではないかと考えられます。今回のコンセプトを達成しきれない残念な結果となりました。

f:id:select766:20210505174804p:plain
習甦戦の評価値

結果は3勝5敗1分で23位となりました。

反省

手法上の都合があるとはいえ、絶対的に性能が低いハードでは勝てませんでした。勝てる相手、勝てない相手がはっきりしていて、自分の改良によって勝敗が変化するような場面がなかったのではないかと思います。容易に両立する手段は思いつきませんが、何らかの手段で強力なハードを使うべきでした。また、定跡を入れておくべきでした。昨年は自宅のPCで開発して本番だけ強力なクラウドを利用するという形でしたので、自宅のPCで事前に探索を行ってもあまり効果がないと考えていました。しかし今回は本番も自宅のPCを使いましたので、事前に長時間の探索を行って定跡を作っておくことの効果が高かったはずです。これは見落としていました。

今後

以下の内容を試してみたいと思っています。

  • ニューラルネットワークの圧縮
    • 画像認識の分野で、ほぼ同じ精度を保ったまま計算量を減らす手法が多数提案されています。npsを上げることに貢献できる可能性があります。
    • ただ現状dlshogiの探索部は、強力なGPUを使った場合にCPU側がネックになるとのことなので、圧縮に成功しても直ちに大きな成果はないかもしれません。
  • 探索アルゴリズムの改良
    • 読み抜け対処
    • 並列度の向上
      • バッチサイズを増やした時の棋力低下を補えないか
    • イデア勝負なところが大きいので、思いつけばやるという感じになります。

強力なGPUを買うかどうかは悩ましいですね。ビットコインの関係で値上がりしているというのもありますが、電源を増強したり空調を気にしたりとGPU本体以外のコストがかなりかかってしまいます。クラウド上で比較的安価にGPUが使えるというGoogle Colabは深層学習フレームワークのサンプルを動かした程度しか経験がないので、有料版を試してみたいです。

来年もできる限り参加したいと思っています。オフラインでの開催ができることを願うばかりです。

世界コンピュータ将棋選手権(第31回)のねね将棋の手法リンク

世界コンピュータ将棋選手権に関連してこのサイトへ来られた方のために、今回(第31回=2021年5月)に用いた手法へのリンクを掲載します。

select766.hatenablog.com

select766.hatenablog.com

select766.hatenablog.com

select766.hatenablog.com

select766.hatenablog.com

select766.hatenablog.com

そのほか、コンピュータ将棋カテゴリに過去の記事があります。

汎用行動選択モデルの3vs3対応 part11 行動の強化学習とパーティ生成の交互実行(結果の評価)【PokéAI】

前回、パーティ生成と学習の反復により、各反復で生成されるパーティが定性的に改善されていることを確認しました。 今回は、各反復での学習結果で上位100パーティを抽出し、反復10回分のパーティを混合した1,000パーティでレーティングバトルさせます。反復間での強さの違いを確認するため、パーティが抽出された反復ごとにレートの平均を計算し、下図に示します。

f:id:select766:20210424161314p:plain
各反復で強かったパーティを混合して対戦させてレートを付与し、反復ごとにレートの平均を算出した結果

反復3までは強さが向上していますが、そのあとは若干弱くなってしまっています。現状の学習手法では、計算時間を延ばしても強さの向上に限界があることがわかりました。

レートトップ10のパーティを示します。

レート パーティ
1821 カビゴン,LV55,かいりき,のしかかり,じしん,ソーラービーム
ミルタンク,LV50,のしかかり,どろかけ,じしん,かいりき
メガニウム,LV50,のしかかり,ソーラービーム,かいりき,どろかけ
1797 カビゴン,LV55,のしかかり,じしん,ソーラービーム,かえんほうしゃ
ギャロップ,LV50,つのドリル,ふみつけ,かえんほうしゃ,のしかかり
ケンタロス,LV50,ふみつけ,かえんほうしゃ,のしかかり,つのドリル
1789 カビゴン,LV55,のしかかり,じしん,かいりき,いわくだき
プテラ,LV50,じしん,スピードスター,いわくだき,はがねのつばさ
グライガー,LV50,じしん,かいりき,スピードスター,いわくだき
1784 カビゴン,LV55,じしん,ちきゅうなげ,のしかかり,どろかけ
ニョロボン,LV50,ちきゅうなげ,どろかけ,ハイドロポンプ,のしかかり
サワムラー,LV50,かいりき,ロケットずつき,どろかけ,のしかかり
1769 カビゴン,LV55,かえんほうしゃ,サイコキネシス,のしかかり,じしん
サンダー,LV50,ドリルくちばし,10まんボルト,どろかけ,かみなり
ドククラゲ,LV50,ふぶき,なみのり,ハイドロポンプ,ヘドロばくだん
1768 カビゴン,LV50,じしん,のしかかり,かみなり,すてみタックル
ドードリオ,LV55,そらをとぶ,すてみタックル,のしかかり,ゴッドバード
ガルーラ,LV50,かみなり,じしん,のしかかり,すてみタックル
1767 カビゴン,LV55,じしん,かみなりパンチ,どろかけ,のしかかり
ミルタンク,LV50,かみなりパンチ,のしかかり,どろかけ,じしん
エアームド,LV50,どろかけ,ゴッドバード,はがねのつばさ,そらをとぶ
1767 バンギラス,LV55,げんしのちから,10まんボルト,じしん,かえんほうしゃ
ギャラドス,LV50,10まんボルト,たきのぼり,のしかかり,かえんほうしゃ
ガルーラ,LV50,のしかかり,10まんボルト,じしん,かえんほうしゃ
1765 カビゴン,LV55,おんがえし,サイコキネシス,かえんほうしゃ,ばくれつパンチ
ヤドラン,LV50,ばくれつパンチ,かえんほうしゃ,サイコキネシス,おんがえし
ムウマ,LV50,おんがえし,サイケこうせん,ずつき,サイコキネシス
1764 カビゴン,LV55,おんがえし,じしん,ロケットずつき,どくどく
ハッサム,LV50,おんがえし,どくどく,はがねのつばさ,ロケットずつき
スターミー,LV50,どくどく,ロケットずつき,たきのぼり,おんがえし

やはりカビゴンが極めて強いという結果になりました。前回考察したように、控えのポケモンについては課題が残ります。

トップのパーティの行動を確認しました。

カビゴン,LV55,かいりき,のしかかり,じしん,ソーラービーム
ミルタンク,LV50,のしかかり,どろかけ,じしん,かいりき
メガニウム,LV50,のしかかり,ソーラービーム,かいりき,どろかけ

基本的に、カビゴンが圧倒的に強いためごり押しになります。タイプ相性によってのしかかりとじしんを使い分ける単純な戦法でした。1vs1ではドンファンカビゴンに有利でしたが、ドンファンメガニウムソーラービームで倒されてしまうので補完ができていました。

カビゴンゴローニャモルフォンのパーティとの対戦では、相手に対し有利なポケモンへの交換を繰り返しつつダメージを蓄積するサイクル戦のようにも見える戦略が見えました。

結局のところ、補助技を使ったトリッキーな戦略というのは現れず、カビゴンの攻撃技で大暴れするのが最適解という結果でした。交代により不利な相手への対策も少しはできるようになったといえます。しかし、交代を繰り返すばかりで無駄にダメージを蓄積してしまうループに陥る場合もあり、交代という選択肢がないほうが良いように思われる状況もありました。

3vs3のまとめ

汎用行動選択モデルを用いた1vs1バトルの手法を拡張し、3vs3バトルへ対応させました。そのために、交代を含めた入出力の拡張、バトルが長くなることに対応した補助報酬の提案などを行いました。これにより、3vs3バトルでのパーティ生成、行動の強化学習の一連の流れを実現することができました。得られた結果はカビゴンの攻撃技を連打するのが最強で、一部の状況では交代を有効活用するサイクル戦のような戦略を確認することができました。補助技の活用はまだ成功しておらず、より長期的な戦略をとれるような学習手法を確立することが今後の課題です。

このシリーズはいったん終了として、ゲーム木探索系の手法を進めようと思います。PokéAIの第4巻の物理本を発行できる機会があったら、第2版として3vs3バトルについて追記したいと考えています。

汎用行動選択モデルの3vs3対応 part10 行動の強化学習とパーティ生成の交互実行(学習経過の評価)【PokéAI】

3vs3バトルでの強化学習とパーティ生成の実装が完成したので、1vs1バトルの時と同様、これらを交互に動作させて強いパーティとその適切な運用法を学習します。

実験条件

アルゴリズム自体は1vs1の時と変わりません。

select766.hatenablog.com

  • 反復回数 10
  • パーティ数
    • Q関数を用いて生成するパーティ数 871
    • ランダム生成するパーティ数 129
  • パーティの生成条件
  • 類似パーティ生成抑制のペナルティ 0.1
  • 強化学習
    • 探索: epsilon-greedy
    • ランダム行動する確率epsilon: 0.3
    • epsilon decay: 2.0×10−6
    • 最小のepsilon: 0.01
    • 報酬割引率: 0.95
    • バッチサイズ: 32
    • 最初に学習するまでのステップ数(replay bufferのサンプル数): 500
    • Nサンプル収集するたびにoptimize: 1
    • N optimizeごとにtarget networkのアップデート: 100
    • replay bufferサイズ: 100,000
    • optimizer (Adam)の学習率: 0.00001
    • バトル数: 100,000
    • 補助報酬

各反復の結果

各反復ではパーティが1,000個生成されます。そして、そのパーティを用いて学習したモデルを用いてレーティングバトルを行い、上位10パーティを示します。また、上位100パーティに含まれるポケモン・技の分布(それぞれ出現回数トップ10)を示します。全1,000パーティでの分布では、多様性を確保したパーティ生成の影響で、ほとんどのポケモンが覚える技(かげぶんしん、どくどく等)が常に上位を占めて傾向が出ませんでした。

反復0

レート パーティ
1975 カビゴン,LV50,かみなりパンチ,のしかかり,すてみタックル,ふぶき
シャワーズ,LV50,のしかかり,ふぶき,バブルこうせん,ずつき
エレブー,LV55,おんがえし,サイコキネシス,スピードスター,はかいこうせん
1969 バクフーン,LV50,すてみタックル,かえんほうしゃ,じしん,ほのおのパンチ
オドシシ,LV50,ずつき,どろかけ,じしん,おんがえし
カビゴン,LV55,のしかかり,はかいこうせん,ちきゅうなげ,いわくだき
1941 ケンタロス,LV50,でんじほう,つのドリル,とっしん,おんがえし
ゴルダック,LV50,かいりき,のしかかり,れいとうビーム,なみのり
ルージュラ,LV55,れいとうビーム,おんがえし,ロケットずつき,とっしん
1939 バンギラス,LV55,じしん,どろかけ,いわくだき,げんしのちから
ムウマ,LV50,10まんボルト,かみなり,スピードスター,でんじほう
シャワーズ,LV50,どくどく,はかいこうせん,かげぶんしん,なみのり
1930 カイリュー,LV55,おんがえし,ロケットずつき,れいとうビーム,だいもんじ
シャワーズ,LV50,たきのぼり,どろかけ,とっしん,ロケットずつき
ゲンガー,LV50,のしかかり,ナイトヘッド,サイコキネシス,ほのおのパンチ
1899 ジュゴン,LV50,ロケットずつき,ずつき,つのドリル,れいとうビーム
カビゴン,LV55,すなあらし,ソーラービーム,のしかかり,でんじほう
ハピナス,LV50,かえんほうしゃ,すてみタックル,とっしん,どくどく
1881 ハピナス,LV50,バブルこうせん,10まんボルト,だいもんじ,かえんほうしゃ
シャワーズ,LV50,れいとうビーム,とっしん,ロケットずつき,すてみタックル
バクフーン,LV55,スピードスター,ばくれつパンチ,かみなりパンチ,だいもんじ
1878 ドンファン,LV50,すなあらし,すてみタックル,ころがる,じしん
ランターン,LV50,でんじほう,なみのり,ハイドロポンプ,おんがえし
スイクン,LV55,バブルこうせん,れいとうビーム,どくどく,はかいこうせん
1870 シャワーズ,LV55,ふぶき,とっしん,スピードスター,なみのり
フーディン,LV50,ちきゅうなげ,サイコキネシス,ほのおのパンチ,れいとうパンチ
マンタイン,LV50,おんがえし,ふぶき,どろかけ,なみのり
1845 プテラ,LV50,いわくだき,とっしん,そらをとぶ,おんがえし
ラプラス,LV55,ふぶき,いわくだき,なみのり,ずつき
ライコウ,LV50,すなあらし,かみなり,でんじほう,はかいこうせん

パーティはランダムですが、強いポケモン・技の傾向が見て取れます。

要素 出現回数
シャワーズ 11
カビゴン 10
ランターン 9
ライコウ 9
サンダー 9
ケンタロス 7
ムウマ 7
ゲンガー 7
ハピナス 6
ヌオー 6
要素 出現回数
おんがえし 83
のしかかり 58
どくどく 58
はかいこうせん 56
かげぶんしん 54
ずつき 53
とっしん 53
スピードスター 43
どろかけ 41
すてみタックル 40

パーティはランダム生成ですが、上位100パーティを抽出すればカビゴン、サンダーなどの出現回数が大きくなりました。技については、AIが現状使いこなせていないものの覚えるポケモンが多いどくどく、かげぶんしんが上位に来ています。

反復1

レート パーティ
1922 カビゴン,LV55,のしかかり,ずつき,じしん,かみなり
サンダース,LV50,スピードスター,かみなり,ずつき,のしかかり
ムウマ,LV50,サイケこうせん,ずつき,かみなり,スピードスター
1903 ファイヤー,LV50,どろかけ,はがねのつばさ,スピードスター,だいもんじ
ライコウ,LV55,いあいぎり,10まんボルト,どろかけ,かいりき
サイドン,LV50,じしん,ふみつけ,だいもんじ,10まんボルト
1877 カビゴン,LV55,かえんほうしゃ,サイコキネシス,のしかかり,じしん
サンダー,LV50,ドリルくちばし,10まんボルト,どろかけ,かみなり
ドククラゲ,LV50,ふぶき,なみのり,ハイドロポンプ,ヘドロばくだん
1874 ガルーラ,LV55,だいもんじ,のしかかり,じしん,いわくだき
ケンタロス,LV50,いわくだき,のしかかり,じしん,だいもんじ
プテラ,LV50,ゴッドバード,だいもんじ,じしん,いわくだき
1866 カビゴン,LV55,じしん,かみなりパンチ,どろかけ,のしかかり
ミルタンク,LV50,かみなりパンチ,のしかかり,どろかけ,じしん
エアームド,LV50,どろかけ,ゴッドバード,はがねのつばさ,そらをとぶ
1854 プテラ,LV50,じしん,げんしのちから,はかいこうせん,ゴッドバード
フシギバナ,LV50,はっぱカッター,げんしのちから,はかいこうせん,やどりぎのタネ
ライコウ,LV55,はかいこうせん,10まんボルト,でんじほう,かみなり
1852 カビゴン,LV55,のしかかり,バブルこうせん,でんじほう,ロケットずつき
ペルシアン,LV50,バブルこうせん,でんじほう,ロケットずつき,のしかかり
フシギバナ,LV50,やどりぎのタネ,ロケットずつき,どくのこな,はっぱカッター
1839 サンダー,LV50,とっしん,ドリルくちばし,おんがえし,10まんボルト
ケンタロス,LV55,とっしん,おんがえし,10まんボルト,れいとうビーム
ランターン,LV50,10まんボルト,れいとうビーム,おんがえし,とっしん
1835 カイリュー,LV55,10まんボルト,かえんほうしゃ,ハイドロポンプ,のしかかり
サイドン,LV50,10まんボルト,かえんほうしゃ,おんがえし,のしかかり
カビゴン,LV50,かえんほうしゃ,おんがえし,10まんボルト,のしかかり
1823 カビゴン,LV55,のしかかり,サイコキネシス,だいもんじ,ばくれつパンチ
スリーパー,LV50,ばくれつパンチ,サイコキネシス,れいとうパンチ,でんじほう
ベトベトン,LV50,れいとうパンチ,だいもんじ,ばくれつパンチ,でんじほう

学習結果では、カビゴン、サンダーの優位性がよく現れる状況になりました。

要素 出現回数
カビゴン 37
サンダー 21
ライコウ 15
バンギラス 15
プテラ 8
サンダース 6
ガルーラ 6
ベトベトン 6
ヌオー 6
キングドラ 6
要素 出現回数
のしかかり 92
10まんボルト 85
おんがえし 64
じしん 63
ずつき 49
でんじほう 46
どろかけ 42
はかいこうせん 38
とっしん 37
どくどく 37

カビゴン、サンダーが1位、2位となり、その主力技となるのしかかり、10まんボルトが技の上位に来ました。

反復2

レート パーティ
1926 カビゴン,LV55,ふぶき,なみのり,かみなり,のしかかり
ヤドキング,LV50,なみのり,ふぶき,バブルこうせん,どくどく
スターミー,LV50,ふぶき,どくどく,バブルこうせん,かみなり
1900 カビゴン,LV55,かいりき,のしかかり,じしん,ソーラービーム
ミルタンク,LV50,のしかかり,どろかけ,じしん,かいりき
メガニウム,LV50,のしかかり,ソーラービーム,かいりき,どろかけ
1886 カビゴン,LV55,はかいこうせん,どくどく,れいとうビーム,おんがえし
メガニウム,LV50,どくどく,はかいこうせん,おんがえし,やどりぎのタネ
パルシェン,LV50,おんがえし,はかいこうせん,どくどく,れいとうビーム
1885 カビゴン,LV50,なみのり,のしかかり,じしん,ロケットずつき
ヌオー,LV50,はかいこうせん,のしかかり,なみのり,じしん
ファイヤー,LV55,そらをとぶ,はがねのつばさ,はかいこうせん,だいもんじ
1883 カビゴン,LV55,のしかかり,じしん,だいもんじ,はかいこうせん
エンテイ,LV50,はかいこうせん,ふみつけ,だいもんじ,おんがえし
キュウコン,LV50,だいもんじ,のしかかり,おんがえし,はかいこうせん
1870 カビゴン,LV55,じしん,ちきゅうなげ,のしかかり,どろかけ
ニョロボン,LV50,ちきゅうなげ,どろかけ,ハイドロポンプ,のしかかり
サワムラー,LV50,かいりき,ロケットずつき,どろかけ,のしかかり
1868 カビゴン,LV50,ばくれつパンチ,すてみタックル,のしかかり,れいとうビーム
カメックス,LV50,れいとうビーム,どくどく,すてみタックル,ばくれつパンチ
ガルーラ,LV55,どくどく,のしかかり,れいとうビーム,すてみタックル
1867 カビゴン,LV55,じしん,はかいこうせん,れいとうビーム,おんがえし
イノムー,LV50,はかいこうせん,じしん,おんがえし,れいとうビーム
ブラッキー,LV50,ロケットずつき,おんがえし,いあいぎり,はかいこうせん
1858 カビゴン,LV50,れいとうビーム,かいりき,じしん,のしかかり
サンドパン,LV50,のしかかり,どくどく,かいりき,じしん
ジュゴン,LV55,どくどく,かいりき,のしかかり,れいとうビーム
1858 カビゴン,LV50,のしかかり,いわくだき,れいとうビーム,ロケットずつき
スイクン,LV55,たきのぼり,おんがえし,いあいぎり,ふぶき
ハッサム,LV50,おんがえし,ロケットずつき,スピードスター,いあいぎり

カビゴン無双になりました。特に気になるのは、パーティの先頭にカビゴンが集中している点です。次の章で考察します。

要素 出現回数
カビゴン 56
サンダー 15
ガルーラ 10
カメックス 9
スターミー 8
メガニウム 6
ヌオー 6
ファイヤー 6
イノムー 6
バンギラス 6
要素 出現回数
のしかかり 104
じしん 93
れいとうビーム 62
おんがえし 57
すてみタックル 51
はかいこうせん 44
かいりき 42
とっしん 35
どろかけ 33
ばくれつパンチ 33

出現頻度がカビゴンに強く偏っています。

反復9 (最終)

レート パーティ
1906 カビゴン,LV50,ほのおのパンチ,でんじほう,じしん,おんがえし
ミルタンク,LV55,おんがえし,じしん,でんじほう,ほのおのパンチ
デンリュウ,LV50,でんじほう,ばくれつパンチ,おんがえし,ほのおのパンチ
1883 カビゴン,LV55,のしかかり,バブルこうせん,でんじほう,ちきゅうなげ
マンタイン,LV50,スピードスター,れいとうビーム,たきのぼり,ハイドロポンプ
カメックス,LV50,ハイドロポンプ,たきのぼり,れいとうビーム,ちきゅうなげ
1876 カビゴン,LV55,ソーラービーム,だいもんじ,れいとうパンチ,のしかかり
キングラー,LV50,ふみつけ,のしかかり,どくどく,どろかけ
ゴローニャ,LV50,だいもんじ,どくどく,どろかけ,のしかかり
1861 カビゴン,LV50,ほのおのパンチ,なみのり,どくどく,のしかかり
ヤドキング,LV55,どくどく,ふみつけ,なみのり,のしかかり
ケンタロス,LV50,ふみつけ,なみのり,どくどく,のしかかり
1859 カビゴン,LV55,すなあらし,じしん,ちきゅうなげ,のしかかり
ドンファン,LV50,じしん,つのでつく,のしかかり,すなあらし
ヘラクロス,LV50,つのでつく,いあいぎり,ちきゅうなげ,じしん
1857 カビゴン,LV55,だいもんじ,はかいこうせん,のしかかり,じしん
ガルーラ,LV50,はかいこうせん,じしん,どくどく,だいもんじ
マグカルゴ,LV50,だいもんじ,はかいこうせん,どくどく,じしん
1856 スイクン,LV50,なみのり,れいとうビーム,おんがえし,かげぶんしん
ドンファン,LV55,つのでつく,かげぶんしん,じしん,おんがえし
ケンタロス,LV50,じしん,つのでつく,かげぶんしん,おんがえし
1831 カビゴン,LV50,ばくれつパンチ,ふぶき,かえんほうしゃ,のしかかり
プクリン,LV55,のしかかり,かえんほうしゃ,ふぶき,ばくれつパンチ
ニョロボン,LV50,ばくれつパンチ,いわくだき,ふぶき,のしかかり
1830 カビゴン,LV50,ロケットずつき,のしかかり,かみなりパンチ,じしん
ブースター,LV50,ロケットずつき,どろかけ,のしかかり,でんじほう
スリーパー,LV55,ロケットずつき,のしかかり,でんじほう,かみなりパンチ
1829 カビゴン,LV55,ばくれつパンチ,バブルこうせん,かみなりパンチ,のしかかり
ニョロボン,LV50,のしかかり,バブルこうせん,ばくれつパンチ,れいとうビーム
パルシェン,LV50,れいとうビーム,バブルこうせん,スピードスター,ふぶき

対戦結果ではカビゴンがやはり無双している状況でした。1vs1バトルでカビゴンへの対策として現れたドンファンが、3vs3バトルではカビゴンと同じパーティに組み込まれ、上位に食い込んでいます。

要素 出現回数
カビゴン 49
ライコウ 14
サンダー 13
バンギラス 13
ドンファン 8
ガルーラ 7
バクフーン 6
プテラ 6
カメックス 5
サイドン 5
要素 出現回数
のしかかり 93
じしん 91
おんがえし 62
どろかけ 53
10まんボルト 47
どくどく 40
れいとうビーム 38
はかいこうせん 38
いわくだき 37
すてみタックル 37

カビゴンが圧倒的上位のまま収束したという状況になりました。

パーティの先頭にカビゴンが多い理由の考察

反復9のトップ100パーティにおいて、出現回数上位5ポケモンについてそれがパーティ先頭に現れた回数、先頭以外に現れた回数を抽出しました。

要素 パーティ先頭での出現回数 パーティ先頭以外での出現回数
カビゴン 48 1
バンギラス 13 0
サンダー 11 2
ライコウ 10 4
スターミー 5 0

主要なポケモンは圧倒的にパーティ先頭に集中しています。なお、パーティ先頭以外だけで出現回数の順位を確認すると、ドンファンの8回が最大となりポケモン間での偏りが小さいことがわかりました。 この結果は、カビゴンバンギラス・サンダーを並べたパーティが生成されていないということでもあり最良のパーティ構築ができていない可能性を示唆しています。 パーティ生成のアルゴリズムは、バトル開始時のQ関数の出力の全行動に対する最大値を最大とするようなパーティを生成するというものでした。Q関数の入力として自分のパーティが反映される部分を展開すると、Q(先頭ポケモン, 技1)、Q(先頭ポケモン, 技2)、…、Q(控えポケモン1に交代, その技すべて)、Q(控えポケモン2に交代, その技すべて)という6つの値のうちの最大値、すなわち最善手をとったときの勝率を取ることになります。バトル開始時にいきなり交代が最善手になる状況は不利であり、先頭に天敵の少ない強力なポケモンが配置されていればほとんど起こらないと考えられます。この場合、控えポケモンは何であっても最大値に影響しないことになります。Q関数を用いた手法は、控えポケモンの反映が弱くなるという問題を抱えていることがわかりました。この問題の解決は将来の課題です。

この記事では、反復的な学習で生成されたパーティの確認と、パーティ生成手法の問題点を確認しました。次回は、反復により強さが向上したことを確認し、最強のパーティの戦略を観察します。

汎用行動選択モデルの3vs3対応 part09 Q関数を用いたパーティ生成【PokéAI】

1vs1バトルの時と同様に、強化学習の結果得られる、行動ごとの価値の期待値を表すQ関数を用いて強力なパーティを生成することを試みます。

手法

手法は1vs1バトルの時のものを応用し、3vs3対応のための変更を行います。

select766.hatenablog.com

3vs3対応の変更点は、(1)隣接パーティの生成部分と、(2)類似したパーティの生成を避けるためのペナルティ部分です。(1)については、10%の確率で、パーティ内のポケモン1匹を別のポケモンに変更し、そのポケモンの技はランダムに初期化します。なお、レベル配分はLV55,50,50となっており、変更前のレベルを維持します。最初のパーティ生成時に、どのポケモンがLV55になるかはランダムです。残りの90%は、ポケモン1匹を選択してその技1つをランダムに変更します。 (2)については、パーティ間の類似度測定の関数において、1vs1のときはパーティを表す特徴量=P,M,PM,MMを用いていました。3vs3ではポケモンのペアの類似度が加わり、P,M,PM,MM,PPの5種類の特徴量を用いた内積になります。パーティが完全一致したときの類似度は、3+12+12+18+3=48となり、1vs1の時の最大値15より大きくなります。そのため、ペナルティの強さ \lambdaを再度調整し、 \lambda=0.1を用いることとしました。

この手法を用いる場合、バトル開始時の時点で自分のパーティの強さがQ関数に現れる必要があります。以前提案した、強化学習中の対戦相手を同じぐらいの強さの相手から選ぶ手法を用いると、バトル開始時点では勝つか負けるか五分五分という状況ばかりを学習するため、パーティ自体の強さがQ関数に反映されません。実際には、最強クラスのパーティは自分より弱い相手との対戦のほうが多くなるため若干は強さがQ関数に現れますが、実際のデータで確認したところ対戦相手をランダムに選択する場合と比べ、パーティ間でのQ関数の値の分散は小さめでした。その問題もあり、対戦相手の選択手法は用いていません。

実験

過去の強化学習で得られたモデル(id: 78e)で生成されたパーティをランダムに10個抽出して示します。

ゲンガー,LV50,ギガドレイン,ばくれつパンチ,かみなり,ほのおのパンチ
デンリュウ,LV55,かいりき,ばくれつパンチ,ほのおのパンチ,かみなり
ラッタ,LV50,かみなり,バブルこうせん,かいりき,ふぶき
---
リングマ,LV50,おんがえし,どろかけ,スピードスター,どくどく
カモネギ,LV55,スピードスター,おんがえし,どろかけ,どくどく
スイクン,LV50,どくどく,おんがえし,スピードスター,どろかけ
---
リングマ,LV50,ずつき,れいとうパンチ,おんがえし,ほのおのパンチ
ゴルダック,LV50,おんがえし,ずつき,ハイドロポンプ,ロケットずつき
キングドラ,LV55,ずつき,ハイドロポンプ,おんがえし,ロケットずつき
---
リングマ,LV50,おんがえし,かみなりパンチ,ちきゅうなげ,ずつき
ハピナス,LV50,ずつき,おんがえし,ソーラービーム,ちきゅうなげ
スリーパー,LV55,かみなりパンチ,ちきゅうなげ,おんがえし,ずつき
---
ルージュラ,LV55,どくどく,サイコキネシス,れいとうビーム,おんがえし
ナッシー,LV50,サイコキネシス,やどりぎのタネ,どくどく,おんがえし
ムウマ,LV50,おんがえし,サイケこうせん,サイコキネシス,どくどく
---
ルージュラ,LV50,サイコキネシス,とっしん,れいとうビーム,どろかけ
エアームド,LV50,いあいぎり,スピードスター,おんがえし,ドリルくちばし
カブトプス,LV55,ちきゅうなげ,とっしん,れいとうビーム,いあいぎり
---
ルージュラ,LV55,れいとうビーム,ロケットずつき,ずつき,サイコキネシス
ハピナス,LV50,ふぶき,ロケットずつき,れいとうビーム,ずつき
ニドクイン,LV50,ずつき,ロケットずつき,ふぶき,れいとうビーム
---
リングマ,LV55,スピードスター,おんがえし,いあいぎり,ころがる
フォレトス,LV50,ころがる,おんがえし,ギガドレイン,スピードスター
エアームド,LV50,スピードスター,いあいぎり,おんがえし,ゴッドバード
---
エンテイ,LV50,かげぶんしん,かえんほうしゃ,どくどく,はかいこうせん
マルマイン,LV55,10まんボルト,かげぶんしん,はかいこうせん,どくどく
パルシェン,LV50,はかいこうせん,どくどく,かげぶんしん,ふぶき
---
リングマ,LV50,ころがる,おんがえし,ちきゅうなげ,ずつき
ベロリンガ,LV50,れいとうビーム,ちきゅうなげ,ころがる,ずつき
サイドン,LV55,ころがる,ちきゅうなげ,ずつき,れいとうビーム

ある程度強いパーティが生成できていると考えられます。カビゴンやサンダーが出ていないのは、ランダムに生成したパーティ1000個で学習しているため、それらのポケモンの強さを認識できていないものと考えられます。

次回は、パーティ生成と強化学習を交互に行い、強いパーティと強い戦略の組み合わせを学習することを目指します。