Google I/O のパズルの Gemini ボーナス ワールドを解放する

3月 13, 2025
Jay Chang Senior Product Marketing Manager Developer Activations

コードをクラッキングする


今年の Google I/O のパズルでは、光と色をプリズムを通して操作することで、ゲームの世界のセクターを解放することができます。コア ゲームループのほかに、新しいレベルも追加されています。このボーナスの世界では、Gemini API が生成した謎を解かねばなりません。このブログでは、その仕組みをどのように開発したかについて説明します。

Gemini との連携: クリエイティブでスケーラブルなソリューション

隠されたタイルはマップに動的に配置され、Gemini モデルが独特な謎を生成します。プレーヤーはその謎を解き、タイルを見つけなければなりません。その目的は、AI が作成した新たなレベルのパズルをプレーヤーに試してもらうことを通して、エンゲージメントを向上させることです。

秘密のタイルの場所とそれに対応する謎を手動で 100 個ハードコーディングするのではなく、AI を使って機能を拡張したことで、難易度の高い独特のものを作ることができました。

ソリューション: 動的に謎を生成する

Gemini の強みを活かすために、アルゴリズムの精度と AI のクリエイティビティを組み合わせたソリューションを考案しました。まず、バックエンド アルゴリズムでマップに隠しタイルを配置します。続いて、ゲームのルールに基づいて 3 つの簡単なヒントで場所を説明してもらうために、Gemini API のプロンプトを生成します。こうすることで、ゲームの枠組みですべての謎を論理的に解けることが保証されます。その後、Gemini を使って、アルゴリズムで生成した答えを巧妙な謎に変換します。


アルゴリズムによるプロンプト生成

ゲームのルールに基づき、プログラマティックにゲームボードの「秘密の場所」を決定します。これを Gemini のプロンプトに使います。こうすることで、謎に対する答えが常に存在し、解決できることが保証されます。

// Gemini トークンの新しい隠し場所を探し、手がかりとなる文字列を生成する
  getHiddenLocation() {
    const geminiCluePositions = GameWorld.getCluePositions() // 「手がかり」のタイルに指定する場所を返す。レベルをデザインするときには、重要なタイルにタグを付ける。通常はプレーヤーが移動できないタイル。
 
 
    // レベルのすべてのタイルの位置を取得する。場所は単純な XY 座標
    const secretLocations = GameWorld.getAllTilePositions()
      // 手がかりの場所に隣接しないタイルを削除する...
      .filter((tileA) => geminiCluePositions.some((tileB) => GameWorld.isNextTo(tileA, tileB)))
      // 空でないタイルなど、無効な場所を削除する
      .filter(({gridX, gridY}) => GameWorld.isValidGeminiPosition(gridX, gridY))
 
 
    // 隠し場所をランダムに選択する
    const randomPosition = secretLocations[Math.floor(Math.random() * secretLocations.length)]
 
 
    const randomTile = Gameworld.getTileByPosition(randomPosition)
 
 
    // 隠し場所が決まったので、手がかりの文字列を生成する
    const riddleClues = GameWorld.generateGeminiRiddleClues(tilePosition)
 
 
    return {
      position: randomPosition,
      clues: riddleClues,
    }
  }

このアルゴリズムからの出力は、次のような単純なテキストになります。

1. 壁のすぐ下。

2. レインボー ノードから厳密に 2 タイル離れている。

3. 最初のセクターに含まれる。


Gemini で謎を生成する

次に、Gemini API を使い、一貫したプロンプト生成構造に基づいて、秘密のタイルの位置を曖昧な形で伝える謎を作成します。Gemini へのプロンプトとして、必要なコンテキストと制約を渡すことで、楽しく難易度の高い謎を作成できます。しかも、一貫したフォーマットになっているため、フロントエンド アプリケーションでユーザーに表示することができます。

// タイルの場所からプロンプトを作成する。常に 3 つのルールを次の順番で出力する。
    // 手がかり 1. 秘密の場所に隣接するタイルの種類
    // 手がかり 2. 秘密の場所が含まれるセクター
    // 手がかり 3. 秘密の場所に一番近いカラーノードと、厳密に何タイル離れているか
  generateGeminiRiddleClues(tilePosition) {
    const adjacentTiles = GameWorld.getAdjacentTiles(tilePosition) // Get the left, right, top and bottom neighboring tiles
    const locationSector = GameWorld.getTileSector(tilePosition) // タイルの「セクター」を取得する。レベル デザイナーは、レベルをセクター、つまりいくつかの「塊」に分割している。
 
 
    const nodeTiles = GameWorld.getAllNodeTiles() // レベルのすべての「ノード」タイルを取得する
 
 
    // 手がかり 1
    const randomAdjacentTile = adjacentTiles[Math.floor(Math.random() * adjacentTiles.length)]
    const direction = GameWorld.getDirection(randomAdjacentTile, tilePosition)
    const randomTileType = randomAdjacentTile.type
    const firstClue = `Directly ${direction} a ${randomTileType} tile` // 例: 「壁のタイルのすぐ上」
 
 
    // 手がかり 2
    const secondClue = `In sector ${locationSector}` // 例: 「セクター 3 に含まれる」
 
 
    // 手がかり 3
    const closestNode = nodeTiles.reduce((closest, node) => {
      const distance = GameWorld.getDistance(node.position, tilePosition)
      if (distance < closest.distance) {
        return {node, distance}
      }
      return closest
    }, {node: null, distance: Infinity})
 
 
    const thirdClue = Exactly ${distance} tiles away from a ${closestNode.node.color} node`
 
 
    const clues = `1. ${firstClue}. 2. ${secondClue}. 3. ${thirdClue}.`
 
 
    return clues
  }

この場合、生成される謎は次のようになります。

とても高い壁のすぐ下、

レインボー ノードから 2 タイル離れた場所。

私がいるのは、最初のセクター。

これを解いて、勝利のトークンを手にしよう。


謎を出す理由

謎は本質的にある程度の曖昧さを含んでおり、不思議だからこそ楽しいものです。そのため、ときどき AI が生成する出力に微妙な内容や予期しない変化が含まれることもあっても、それを受け入れることができました。さらに、謎はプレーヤーの推理力を刺激し、ゲームのルールに関する知識を活用してクリエイティブな発想を求め、ボードのレイアウトを分析して隠されたタイルを探すことを促します。


システム指示で LLM が生成する出力の一貫性を確保する

AI との共同作業には、独自の課題が伴います。一番重要な点は、AI には「幻覚」を起こしたり、与えられたルールから逸脱したりする傾向があることです。このリスクを軽減するため、プロンプトをプログラマティックに生成し、プロンプトのシステム指示に例と JSON 出力の定義を含めました。

**重要な指示:**
        - 指定の形式に厳密に従った JSON オブジェクト**のみ**で応答してください。
        - 説明、コードブロック、追加テキストは含め**ない**でください。
        - JSON を 3 つのバックティックで囲んだり、マークダウン形式にしたりし**ない**でください。
        - JSON 内のすべての文字列を適切にエスケープしてください。
        - 改行(`\\n`)、タブ(`\\t`)、引用符(`\\"`)など、文字列内の特殊文字をエスケープしてください。
        - 単一引用符は使わないでください。すべての JSON キーや文字列の値で二重引用符を使ってください。
        - 必ず有効でパースできる JSON にしてください。

私たちは、人間の推理力にも注目しました。プレーヤーは謎めいた手がかりを解釈したり、解読したりすることが得意です。AI による出力には一貫性がない場合もありますが、論理的な推理が求められる謎を作ることで、プレーヤーがそれを克服できるようにしています。最終的に重要だったのは、AI が生成したコンテンツと人間の創意工夫との適切なバランスを見つけることでした。


Gemini API を使ってアプリを作ろう

今年は、Gemini API を搭載した最初の Google I/O パズルというマイルストーンを実現できました。私たちの設計チームとエンジニアリング チームにとって、これは単なる機能の組み込みではなく、AI とのコラボレーションを実現する新時代に向けて徹底的に考え抜く取り組みであり、単に機能を開発することを超えて、インタラクティブな体験への新しいアプローチを開拓する試みでした。皆さんがプロジェクトに Gemini API を導入することを検討する際には、次の 3 つの重要な教訓を参考にして、アプローチを決定するとよいでしょう。

  • クリエイティビティ: これまでにない方法でプロダクトの AI を活用し、ダイナミックなコンテンツ生成、拡張性、自動化を実現する。

  • デザイン: 効果的なプロンプトを記述して試し、Google AI Studio でプロトタイプを作成して、さまざまな Gemini モデルや機能で結果をテストする。

  • 実装: 詳細なシステム指示を作成し、モデルに求められる応答例を使って出力形式を定義して、アプリケーションが解釈できるように構造化された一貫性のある出力にする。


AI は、ユーザーのアプリやゲームへの関わり方を変え、新しいエキサイティングなユーザー エクスペリエンスへの扉を開きます。

5 月 20~21 日の Google I/O にオンラインで参加しましょう。Mountain View の Shoreline Amphitheatre から、今年のエキサイティングな発表をライブ配信します。Gemini を試して、ユーザーに役立つ楽しい体験を生み出す可能性を探ってみてください。可能性は無限大です。