2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

弾幕シューティングゲームを Microsoft-Decision-1 にやらせてみた

2
Posted at

MicrosoftからもJevやOpenAI Decisions APIのような判断型AIモデルが出てきました。すでに解説記事も色々と出ていますが、今回はこの判断型AIモデルを使い「その場で判断」させて、弾幕シューティングゲームを遊ばせて検証してみました。

ちなみにゲームをさせるにはAIにゲームの情報を渡す口が必要になります。理想的にはゲームの画像データから判断なんでしょうが、残念ながらまだそれはできないので、自作のブラウザゲームを作りました。
このゲームに人間が目で見ているようなデータをJSONで返す口を作っています。

これはこれでGitHub Copilotで7エージェント並列に開発したものではあるのですが、今回詳細は割愛します。普通に遊べるのでご興味ある方は遊んでみてください。

AIによるプレイ結果

ブラウザで動く縦スクロールの弾幕STGを、AIがキーボード操作でプレイしています。左にゲーム画面、右にAIを稼働しているCLIという配置です。今回利用したデプロイのリクエスト上限は60回/分だったため、約1秒間隔でAPIを呼び出す構成にしました。返ってきた判断に従ってキーボードを操作しています。

結果、なんと1面をクリアできました。最初は前後移動しかせず、回避行動もほぼなくすぐに全滅していたAIが色々な工夫の末にここまでやることができました。感無量です^^

AIによる1面クリアの衝撃
※GIFにする際にフレームレートや色は少し落としました。

ゲーム時間は止めず、操作はキーボード入力だけで行っています。ただし、画像を見て判断するのではなく、画面上の対象の座標などをJSONで取得しています。さらに、観測履歴からの速度推定と、各移動プランの危険度予測を前処理として加えています。そのため、人間とまったく同じ観測条件ではありません。

Microsoft-Decision-1の基本的な使い方

Microsoft-Decision-1 は、文章を生成する LLM とは違い、「判断」だけを返すモデルです。
状況(state、テキストまたはJSON)と質問(questions)を渡すと、決まった型で答えと確率を返します。

質問タイプ 用途 返ってくるもの
noul Yes/No の判定 0〜1 の確率
choice 選択肢から1つ選ぶ 選んだ選択肢と、各選択肢の確率
score 段階評価 段階インデックスの確率加重平均と、各段階の確率。段階の中間値も取り得る

ゲーム操作は「どの行動を選ぶか」なので、今回は choice を使いました。

呼び出し方

Foundry でモデルをデプロイしたら、リソースの /providers/microsoft/v1/systemone に POST するだけです(model にはデプロイ名を指定)。

curl "$AZURE_ENDPOINT/providers/microsoft/v1/systemone" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AZURE_ENTRA_TOKEN" \
  -d '{
    "model": "<デプロイ名>",
    "state": "APIを呼ぶと毎回500エラーが返ってきます。",
    "questions": {
      "team": {
        "type": "choice",
        "instructions": "この問い合わせはどのチームが対応すべきですか?",
        "criteria": {
          "billing": "請求・支払い・返金に関する問い合わせ",
          "engineering": "バグ・エラー・障害に関する問い合わせ",
          "support": "使い方・アカウントに関する問い合わせ"
        }
      }
    }
  }'

返ってくるのはこんな形です(数値はイメージです)。自由文や理由の説明はなく、選んだ選択肢と、各選択肢の確率だけ。プログラムからとても扱いやすいです。

{
  "model": "microsoft-decision-1",
  "answers": {
    "team": {
      "type": "choice",
      "choice": "engineering",
      "probabilities": { "billing": 0.01, "engineering": 0.96, "support": 0.03 }
    }
  },
  "usage": { "input_tokens": 120, "output_tokens": 1 }
}

自由文を生成せず、型の決まった判断結果を返すので、プログラムから扱いやすいモデルです。基本的に応答も速いです。今回のゲームでは、1回の判断で中央値約0.5秒でした。

詳細については公式のドキュメントや解説ブログなんかをご参照ください。

工夫したところ

AIがきちんとゲームをプレイできるように工夫した点は大きく以下の2つです。

  1. 何をどう見せるか(Decision-1 に渡すデータの工夫)
  2. 何を優先させるか(判断基準の工夫)

何をどう見せるか

状況を少し加工する

ゲームから見えている情報(自機・敵・弾・アイテムの座標、残機・ボム数など)がJSONで飛んでくるので、それを少し加工し、少し前の観測と見比べて弾や敵の速度を推定し、「どちらから近づいてくるか」をAIがすぐに判断できるようにしました。
少しズルっぽいですが、今回は画像ではなく座標データを入力するため、観測履歴から速度を推定する処理をプログラム側に持たせた形です。Microsoft-Decision-1には、その推定結果を判断材料として渡しています。

また、弾幕系のゲームなので弾の数が非常に多くなります。そこで、各弾のデータを配列にして、コンパクトにAIへ渡します。

// 値はイメージです
bulletColumns:  ['id', 'x', 'y', 'vxPxPerSecond', 'vyPxPerSecond', 'displayRadius'],
enemyBullets: [
  [101, 412, 230, -20, 180, 6],
  [102, 380, 260,  15, 190, 6],
  // ...
],

移動の判断を1つの選択肢にまとめる

最初は「方向」「速度」「押す時間」をバラバラの質問にしていましたが、組み合わせがちぐはぐになりがちでした。
そこで、停止+8方向×(低速/通常)=17通りの飛行プランを用意し、その中から1つ選ばせる形にしました。

export const FLIGHTS = {
  stay: { movement: 'stay', speed: 'slow', movementMs: 150 },
  // 8方向それぞれに「精密(低速250ms)」と「移動(通常450ms)」
  left_precision: { movement: 'left', speed: 'slow',   movementMs: 250 },
  left_travel:    { movement: 'left', speed: 'normal', movementMs: 450 },
  // ... up / down / up_left / down_right など
};

各プランに対して"予想される結果"を添える

弾の一覧だけ渡して「よけて」と言っても、判断型AIモデルが頭の中で全弾の未来位置を計算するのは大変です。
そこで、17プランそれぞれについて「その通り動いたら弾や敵にどこまで近づきそうか」を事前に判断材料として添えるようにしました。

// 値はイメージです
flights: {
  stay:           { minimumBulletSeparationPx: 18.2, predictedNearPasses: 2, itemDistancePx: 140 },
  left_travel:   { minimumBulletSeparationPx: 72.5, predictedNearPasses: 0, itemDistancePx: 210 },
  up_left_travel:{ minimumBulletSeparationPx: 55.0, predictedNearPasses: 0, itemDistancePx: 60, bossAlignmentPx: 12 },
  // ...
}

ここでは、各移動プランで弾や敵にどこまで近づくかの予測をプログラム側で計算しています。Decision-1には、その結果を踏まえて、危険度やアイテム回収、攻撃位置などを考慮した行動を選ばせています。つまり、予測処理と判断モデルを組み合わせた構成です。

何を優先させるか

Microsoft-Decision-1 の instructions と criteria には、何を大事にするかを文章で書けます。ここが人間の「プレイ方針」にあたります。

優先順位を明確に記載する

試行錯誤の末、最終的にこの順番に落ち着きました。

弾をよける(生存) → 安全に取れるアイテム → ボス → 通常の敵

flight: {
  type: 'choice',
  instructions: [
    'FIRST avoid edgeOverflowPx > 0, predictedNearPasses and predictedEnemyNearPasses. ...',
    'Among similarly safe routes, collect powerups ..., then align fire with the boss ..., then ordinary enemies ...',
    'Do not select stay when it has a close pass and another route is clearer.',
  ].join(' '),
  criteria: { /* 17プランそれぞれの説明 */ },
},

「止まっていて危ないなら、もっと空いているルートを選べ」と明記したのは、AIがひたすら停止し続ける現象への対策です。明記しないと保守的になりがちでした。

ボムは「本当に逃げ道がないときだけ」

初回試行時に、AIは開始時にいきなり全部のボムを使い切りました。(敵がいなくてもw)
そこでボムを使う条件、温存する条件を具体的にしています。ただしここはあまりうまくいっていません。プレイ動画を見ていただいてもボムは使っていないことが分かるかと思います。積極的に使えというとすぐに使い切るし、条件をつけるとなかなか使ってくれません。ここはチューニングのし甲斐があるところかなと思いました。

bomb: {
  type: 'choice',
  instructions: 'Bombs are scarce defensive resources. ... If enemyBullets and hostile targets are absent, always choose save.',
  criteria: {
    use:  'Spend one bomb only for clearly visible imminent collision with no reasonable movement escape.',
    save: 'Preserve bombs when no current imminent danger is established, movement can escape, ...',
  },
},

試行錯誤の履歴

最初は人間がAIのプレイを見てコメントし、それをGitHub Copilotが反映して再プレイするループを回しました。5回目からは面倒くさくなったので、プレイログなどをGitHub Copilotに渡し、判断基準や前処理コードを改善させるループに切り替えました。プレイ中の行動選択はDecision-1、結果の分析と修正はGitHub Copilotが担当しています。

段階 コメント・変更内容 結果・対応
初回 「いきなりボムを全部使ったり、前後移動しかしない」 判断基準を追加
2回目 「底で左右しかしない。アイテムも取らない」 左右には動くようになったが、画面下に張り付き
3回目 「弾を回避しなくなった…回避が最優先!」 アイテムに向かって突っ込んで被弾
4回目 (回避最優先を明記) 50回すべて「停止」を選択して撃沈
5〜7回目 弾の速度推定・押す時間の工夫など 動くようにはなったが、成績は上下
8〜9回目 「17プラン+予測値」方式に全面見直し ついに1面のボスを撃破!

興味深かったのは、今回の構成では、文章で「回避最優先」と書くだけでは十分に改善しなかったことです。動かないことが回避になると考えたのかわかりませんが、なぜか停止し続けてしまいました。
結局のところ今回の試行で特に手応えがあったのは、言葉を足すことより、17プランの選択肢ごとに予測値を添えて渡す構成に変えたことでした。複数の変更を加えているため、それぞれの効果を切り分けたわけではありませんが、この構成で1面クリアまで到達できました。少しズルっぽいですが、人間も予測してますしね。

まとめ

  • Microsoft-Decision-1 は「選ぶ」ことに特化したモデル。状況と質問を渡すと、選択結果や確率が返ってくるので、プログラムから扱いやすいです。今回のゲームでは、約1秒間隔で呼び出す制御に組み込みました
  • うまく使うコツは「見せ方」。生データを丸投げするより、選択肢ごとに「選んだらどうなるか」を添えると判断が安定しました
  • 判断基準は言葉で書ける。優先順位やボムの使いどころなど、人間のプレイ方針をそのまま instructions / criteria に落とし込めます。ただし、文章だけで解決しない問題もありました
  • 検証結果は Stage 1 クリア!。約1秒に1回の判断では、熟練プレイヤーのような細かい回避はまだ難しいのが正直なところですが、ひとまず見れるレベルにはなったかなと

モデルの追加学習は行わず、プロンプト、入力データ、予測処理、操作方法を工夫して「弾幕の中を動き回ってボスを倒す」ところまで来たのは、なかなか面白い結果でした。

業務用途としては、ドキュメントの分類や問い合わせの振り分けなど、「いくつかの選択肢から状況に応じて1つ選ぶ」場面に使えそうです。汎用LLMの代用品というより、分類・ルーティング・ゲート判定など、選択肢や判断基準が定義された処理に組み込むモデルとして捉えると、使いどころが色々ありそうです。
あとはフィジカルAIなどでのリアルタイム性のある判断にも使えるかもしれません。といっても応答を含めると1~2秒かかるのでその範囲で使えるものがあれば、というところでしょうか。なお、どうしても精度は汎用LLMに比べて落ちるので、使いどころは注意深く検討する必要はありそうです。

それでは最後に、ゲームの最終ステージのAIプレイ動画を添えて本記事を終わりたいと思います。最終ステージ、AIは瞬殺でしたが、皆さんはいかがですか?

最終面のAIプレイ動画

(約1秒間隔の判断と約500msの応答待ちでは、今回の構成で最終面の弾幕を攻略するのは到底無理でしたw)

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?