ボンバーマンのミニマックス


11

私はボンバーマンゲームのクローンを開発しており、さまざまなタイプのAIを実験しています。最初にA *で状態空間を検索することを使用しましたが、次にMinimaxアルゴリズムで別のアプローチを試したいと思います。私の問題は、私が見つけたすべてのミニマックスの記事が、プレーヤーが交代すると仮定したことです。しかし、ボンバーマンでは、すべてのプレイヤーが同時にいくつかのアクションを実行します。1つのゲームティックで可能なすべての状態を生成できると思いますが、4人のプレーヤーと5つの基本アクション(4移動と爆弾配置)を使用すると、ゲームツリーの最初のレベルで5 ^ 4状態になります。その値は、次のレベルごとに指数関数的に上昇します。何か不足していますか?それを実装する方法はありますか、またはまったく異なるアルゴリズムを使用する必要がありますか?提案をありがとう


1
これは少し外れたトピックですが、私がAIでやりたいことの1つは、AIの目標または個性を使用することです。それは買いだめのパワーアップ、攻撃的でない、復讐を求める、急ぐなどのようなものである可能性があります。そのような目標を使用すると、大まかにどの方向に移動すべきかを知ることができ、爆弾をドロップするのは、それが目標に進んだ場合のみです狩りをしているプレイヤーや破壊したいブロックにかなり近いです)。
ベンジャミンデンジャージョンソン

2
はい、いくつか不足していますが、それらを悪化させるので、指摘してくれて感謝しません。5つの基本的なアクションはありません。一部の正方形には5つの「動き」があります(4つの方向と静止)。他には3があります(2方向でブロックされているため)。平均は4ですが、実行中に爆弾投下できるので、平均して分岐係数は8です。高速パワーアップを持つ人は、より多くの動きに対応でき、分岐係数を効果的に押し上げます。
Peter Taylor

モンテカルロツリー検索を使用して、質問の回答を提供しました。
SDwarfs 2013年

ミニマックスは、ボンバーマンと同じくらい多くの選択肢がある状況では、まったく役に立ちません。移動が賢明であるかどうかを確認するのに十分な距離になる前に、検索機能を使い果たしてしまいます。
Loren Pechtel 2013年

回答:


8

爆撃機のようなリアルタイム戦略ゲームはAIに苦労しています。あなたはそれがインテリジェントであることを望みますが、同時にそれは完璧ではあり得ません。

AIが完璧だと、プレイヤーは苛立ちます。それらは常に失われるか、毎秒0.3フレームを取得するためです。

十分にインテリジェントではない場合、プレイヤーは退屈します。

私の推奨は、2つのAI関数を使用することです。1つはAIの移動先を決定し、もう1つは爆弾を投下するのに最適なタイミングを決定します。移動予測などを使用して、現在の場所に爆弾が投下された場合に危険になるスポットに向かって敵が移動しているかどうかを判断できます。

難易度に応じて、これらの関数を変更して難易度を改善または低減できます。


2
時間、欲求不満、退屈は問題ではありません。ボンバーマンでのさまざまなAIアプローチに関する学士論文を書いて、それらを比較しています。ですから、それが完璧であれば、より良いです。私は今、そのミニマックスで立ち往生しています
Billda 2013

1
ミニマックスアルゴリズムで遭遇する問題は、処理時間です。すべての敵の行動を追跡し、それらのプレイスタイルとカウンタープレイスタイルを決定する必要があります。既にご存知のようですが、リアルタイムゲームでは、ゲームの速度を落とさずにこれを行うのは非常に困難な作業です。プレイツリーを作成する代わりに、リアルタイムでアクションを決定する必要があります。おそらく、プレイするほど良くなる機械学習アルゴリズムを作成しますか?
UnderscoreZero 2013

4

お気づきのように、ボンバーマンはターンベースのゲームとしてシミュレートするには複雑すぎます。考えられる自分の決定に加えて、他のすべてのプレイヤーの考えられるすべての決定を外挿しても、うまくいきません。

その代わりに、より戦略的なアプローチを使用する必要があります。

ボンバーマンをプレイしている間、人間のプレイヤーはどのように意思決定を行うのですか?通常、プレーヤーは4つの基本的な優先順位に従う必要があります。

  1. 爆弾の爆発領域を避ける
  2. 他の人が爆発エリアを避けられないように爆弾を置く
  3. パワーアップを収集する
  4. 爆弾を置いて岩を爆破する

最優先事項は「危険マップ」を作成することで実現できます。爆弾が置かれるとき、それによってカバーされるすべてのタイルは「危険」としてマークされるべきです。爆弾が爆発するのが早いほど(連鎖反応を念頭に置いてください)、危険度が高くなります。AIが危険度の高いフィールド上にあることに気づくと、AIは離れる必要があります。(何らかの理由で)パスをプロットする場合、危険レベルの高いフィールドは回避する必要があります(パスコストを人為的に追加することで実装できます)。

危険マップの計算をさらに強化して、AIを愚かな決定(別のプレイヤーが近くにいるときに脱出するのが難しい領域に入るなど)から保護することができます。

これにより、妥当な防御AIがすでに作成されているはずです。では、犯罪はどうですか?

AIが現時点でかなり安全であることを認識したら、攻撃的な作戦を計画する必要があります。爆弾自体を配置することによって、他のプレイヤーの周囲の危険マップをどのように増やすことができるかを検討する必要があります。爆弾を配置する場所を選択するときは、近くに移動する必要がないように、近い場所を優先する必要があります。また、作成された危険マップで合理的な避難経路が許可されていない場合は、爆弾の場所を無視する必要があります。


それをプレイする私の限られた経験は、通常、有能な対戦相手を殺すために複数の爆弾を配置しなければならないということです-戦略はこれを考慮する必要があります。私はAIとほぼ同じ戦略で対戦しましたが、追い詰められない限り、AIはあなたを殺すのにまったく効果がありません。
Loren Pechtel 2013年

4

1つのゲームティックで可能なすべての状態を生成できると思いますが、4人のプレーヤーと5つの基本アクション(4移動と爆弾配置)を使用すると、ゲームツリーの最初のレベルで5 ^ 4状態になります。

正しい!ゲームティックごとに、すべての5 ^ 4(または4方向に歩き、停止して「爆弾を置く」ことができるので6 ^ 4)アクションを検索する必要があります。ただし、プレイヤーがすでに移動することを決定している場合、移動が実行されるまでには時間がかかります(たとえば、10ゲームティック)。この期間中、可能性の数は減少します。

その値は、次のレベルごとに指数関数的に上昇します。何か不足していますか?それを実装する方法はありますか、またはまったく異なるアルゴリズムを使用する必要がありますか?

ハッシュテーブルを使用して、同じゲーム状態の「サブツリー」を1回だけ計算できます。プレーヤーAが上下に歩き、他のすべてのプレーヤーが「待機」している間、あなたは同じゲーム状態になります。「left-right」や「right-left」と同じです。また、「左上」と「左上」を移動すると、同じ状態になります。ハッシュテーブルを使用すると、すでに評価されているゲーム状態の計算スコアを「再利用」できます。これにより、成長速度が大幅に低下します。数学的には、それはあなたの指数関数的成長関数のベースを減らします。どれだけ複雑さを軽減するかを理解するために、プレーヤーが上/下/左/右/停止するだけの場合の、マップ上の到達可能な位置(=異なるゲーム状態)と比較した、1人のプレーヤーのみの可能な移動を見てみましょう。

深さ1:5つの移動、5つの異なる状態、この再帰のための5つの追加状態

深さ2:25の移動、13の異なる状態、この再帰のための8つの追加の状態

深さ3:6125移動、25の異なる状態、この再帰のための12の追加状態

それを視覚化するには、自分に答えてください。マップ上のどのフィールドに1回の移動、2つの移動、3つの移動で到達できますか。答えは、開始位置からの最大距離が1、2、または3のすべてのフィールドです。

HashTableを使用する場合、到達可能な各ゲーム状態(この例では深さ3の25)を1回評価するだけで済みます。一方、HashTableがない場合、それらを複数回評価する必要があります。つまり、深さレベル3では25ではなく6125評価になります。最適:HashTableエントリを計算したら、後のタイムステップで再利用できます...

さらに深く調査する価値のない、インクリメンタルディープニングおよびアルファベータプルーニングの「カット」サブツリーを使用することもできます。チェスの場合、これは検索されるノードの数を約1%に減らします。アルファベータ剪定の簡単な紹介は、ビデオとしてここにあります:http//www.teachingtree.co/cs/watch? concept_name = Alpha-beta + Pruning

さらなる研究の良いスタートはhttp://chessprogramming.wikispaces.com/Searchです。このページはチェスに関連していますが、検索と最適化のアルゴリズムはまったく同じです。

ゲームに適した別の(しかし複雑な)AIアルゴリズムは、「時間差学習」です。

よろしく

ステファン

PS:可能なゲームステートの数を減らすと(たとえば、マップのサイズが非常に小さい、プレイヤーごとに爆弾が1つだけで、他には何もない)、すべてのゲームステートの評価を事前に計算する機会があります。

-編集-

ミニマックス計算のオフライン計算結果を使用して、神経ネットワークをトレーニングすることもできます。または、それらを使用して、手動で実装した戦略を評価/比較することもできます。たとえば、提案された「パーソナリティ」のいくつかと、どの戦略が適切であるかを検出するいくつかのヒューリスティックを実装できます。したがって、状況(ゲームの状態など)を「分類」する必要があります。これはニューラルネットワークでも処理できます。ニューラルネットワークをトレーニングして、現在コーディングされている戦略のうちどれが現在の状況で最適に機能しているかを予測し、実行します。これにより、実際のゲームで非常に優れたリアルタイムの決定が得られます。オフラインでの計算にかかる時間はそれほど重要ではないため(ゲームの前にあるため)、それ以外の場合に実現できる低深度の検索よりもはるかに優れています。

-編集#2-

1秒ごとにベストムーブのみを再計算する場合は、より高いレベルのプレーニングを試みることもできます。それはどういう意味ですか?あなたはあなたが1秒間に何回動くことができるか知っています。そのため、到達可能な位置のリストを作成できます(たとえば、これが1秒間に3移動である場合、到達可能な位置は25になります)。次に、「位置xに移動して爆弾を配置する」のように計画できます。他の人が提案したように、ルーティングアルゴリズムに使用される「危険」マップを作成できます(位置xに移動する方法?どのパスを優先する必要があります[ほとんどの場合、可能なバリエーションがいくつかあります])。これは、巨大なHashTableと比較してメモリ消費は少ないですが、最適な結果は得られません。ただし、メモリ使用量が少ないため、キャッシング効果のために高速になる可能性があります(L1 / L2メモリキャッシュのより適切な使用)。

さらに:失うことになるバリエーションを選別するために、1人のプレーヤーの動きのみを含む事前検索を行うことができます。したがって、他のすべてのプレーヤーをゲームから除外します...各プレーヤーが失うことなく選択できる組み合わせを保存します。失う動きのみがある場合は、プレーヤーが最も長く生き続ける動きの組み合わせを探します。この種のツリー構造を格納/処理するには、次のようなインデックスポインターを含む配列を使用する必要があります。

class Gamestate {
  int value;
  int bestmove;
  int moves[5];
};

#define MAX 1000000
Gamestate[MAX] tree;

int rootindex = 0;
int nextfree = 1;

各状態には評価「値」があり、移動時に「ツリー」内の配列インデックスを格納することにより、移動時に次のゲーム状態にリンクします(0 =停止、1 =上、2 =右、3 =下、4 =左)。 ]からmoves [4]に。ツリーを再帰的に構築するには、次のようにします。

const int dx[5] = { 0,  0, 1, 0, -1 };
const int dy[5] = { 0, -1, 0, 1,  0 };

int search(int x, int y, int current_state, int depth_left) {
  // TODO: simulate bombs here...
  if (died) return RESULT_DEAD;

  if (depth_left == 0) {
    return estimate_result();
  }

  int bestresult = RESULT_DEAD;

  for(int m=0; m<5; ++m) {
    int nx = x + dx[m];
    int ny = y + dy[m];
    if (m == 0 || is_map_free(nx,ny)) {
      int newstateindex = nextfree;
      tree[current_state].move[m] = newstateindex ;
      ++nextfree;

      if (newstateindex >= MAX) { 
        // ERROR-MESSAGE!!!
      }

      do_move(m, &undodata);
      int result = search(nx, ny, newstateindex, depth_left-1);
      undo_move(undodata);

      if (result == RESULT_DEAD) {
        tree[current_state].move[m] = -1; // cut subtree...
      }

      if (result > bestresult) {
        bestresult = result;
        tree[current_state].bestmove = m;
      }
    }
  }

  return bestresult;
}

動的にメモリを割り当てるのは本当に遅いので、この種のツリー構造ははるかに高速です!しかし、検索ツリーの保存も非常に遅くなります...したがって、これはよりインスピレーションになります。


0

誰もが交代することを想像するのに役立ちますか?

技術的には、基礎となるシステムでは実際に実行されますが、物事は交互に配置され、重複しているため、同時に実行されているように見えます。

また、アニメーションのフレームごとに AIを実行する必要がないことも覚えておいてください。成功したカジュアルゲームの多くは、AIアルゴリズムを毎秒1回程度しか実行せず、AIが制御するキャラクターに、どこに行くべきか、何をすべきかに関する情報を提供し、その情報を使用してAIキャラクターを制御します他のフレームで。


アニメーションのフレームごとではなく、毎秒AIを計算しています。私の環境は毎秒すべてのプレーヤーのアクションを収集し、新しい更新された状態を送信します。
Billda 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.