O quebra-cabeça do Google I/O deste ano desafia os jogadores a manipularem a luz e as cores através de prismas para desbloquear setores no mundo do jogo. Além do loop de jogo central, uma nova dimensão foi adicionada à jogabilidade: mundos de bônus ocultos por trás de enigmas gerados com a API Gemini. Este blog fornece uma análise de como nós criamos tudo isso!
Os blocos ocultos são posicionados dinamicamente no mapa, enquanto os modelos Gemini geram enigmas exclusivos para os jogadores resolverem a fim de encontrá-los. O objetivo? Aumentar o engajamento, incentivando os jogadores a explorarem novas dimensões do quebra-cabeça criado com a IA.
Em vez de codificar manualmente centenas de possíveis localizações de blocos secretos e os enigmas correspondentes, usamos a IA para nos ajudar a escalonar o recurso de uma maneira desafiadora e única.
Para aproveitar os pontos fortes do Gemini, desenvolvemos uma solução que combinava precisão algorítmica com criatividade habilitada por IA. Um algoritmo de back-end posicionou os blocos ocultos no mapa e gerou um prompt para a API Gemini com base nas regras do jogo, descrevendo a localização com três instruções simples. Isso garantiu que cada enigma tivesse uma solução lógica dentro do framework do jogo. Usamos o Gemini para transformar a resposta gerada de forma algorítmica em um enigma inteligente.
Com base nas regras do jogo, determinamos programaticamente uma "localização secreta" no tabuleiro do jogo que foi usada como um prompt para o Gemini. Isso garantiu que a resposta a todos os enigmas fosse sempre válida e solucionável.
// Finds a new hiding spot for the Gemini token and generates a clue string
getHiddenLocation() {
const geminiCluePositions = GameWorld.getCluePositions() // Returns positions that are designated as a "Clue" tile. We tag important tiles when designing a level. These are generally tiles that are not movable by the player.
// We get all the tiles positions in the level, a position is a simple XY coordinate
const secretLocations = GameWorld.getAllTilePositions()
// we remove tiles that are not adjacent to a clue position...
.filter((tileA) => geminiCluePositions.some((tileB) => GameWorld.isNextTo(tileA, tileB)))
// we remove invalid positions, such as tiles that are not empty
.filter(({gridX, gridY}) => GameWorld.isValidGeminiPosition(gridX, gridY))
// we randomly choose a hiding spot
const randomPosition = secretLocations[Math.floor(Math.random() * secretLocations.length)]
const randomTile = Gameworld.getTileByPosition(randomPosition)
// now that we have a hiding spot, we generate a clue string
const riddleClues = GameWorld.generateGeminiRiddleClues(tilePosition)
return {
position: randomPosition,
clues: riddleClues,
}
}
A saída do algoritmo era um texto simples, semelhante aos seguintes (traduções fornecidas para fins de compreensão):
1. Diretamente sob uma parede.
2. Exatamente a dois blocos de distância de um nó de arco-íris.
3. No primeiro setor.
Com uma estrutura consistente para que o prompt fosse gerado, recorremos à API Gemini para criar um enigma que descrevesse de forma críptica a localização do bloco secreto. Ao fornecer ao Gemini um prompt com o contexto necessário e as restrições, conseguimos criar enigmas atraentes e desafiadores, que eram consistentemente formatados para que o aplicativo de front-end pudesse exibi-los aos usuários.
// Build a prompt based on the tile position. We always output 3 rules in this order:
// Clue 1. The type of one adjacent tile to the secret location
// Clue 2. The sector which contains the secret location
// Clue 3. The closest color node to the secret location, and exactly how many tiles away it is.
generateGeminiRiddleClues(tilePosition) {
const adjacentTiles = GameWorld.getAdjacentTiles(tilePosition) // Get the left, right, top and bottom neighboring tiles
const locationSector = GameWorld.getTileSector(tilePosition) // get the "sector" of the tile. Levels are divided in sectors or 'chunks' by the level designer.
const nodeTiles = GameWorld.getAllNodeTiles() // get every 'Node' tile in the level
// clue 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` // e.g. "Directly above a wall tile"
// clue 2
const secondClue = `In sector ${locationSector}` // e.g. "In sector 3"
// clue 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
}
O enigma resultante foi o seguinte (tradução fornecida para fins de compreensão):
Diretamente abaixo de uma parede alta eu estou,
Conte dois blocos a partir de um nó de arco-íris, e você me achou.
Dentro do primeiro setor, minha posição você verá,
Resolva este enigma, e o token você ganhará.
Os enigmas são inerentemente crípticos e divertidos, e um certo grau de ambiguidade já é esperado. Isso nos deu espaço para as ocasionais pistas falsas ou mudanças inesperadas de frases que podem surgir na saída gerada pela IA. Além disso, os enigmas estimulam as habilidades de raciocínio dos jogadores, incentivando-os a pensar de maneira criativa e aplicar seus conhecimentos das regras do jogo, analisando o layout do tabuleiro enquanto procuram pelo bloco oculto.
O trabalho com a IA tem seu próprio conjunto de desafios. Um dos maiores deles é a tendência da IA de "alucinar", ou se desviar das regras fornecidas. Mitigamos esse risco gerando um prompt programaticamente e fornecendo exemplos e uma saída em JSON definida nas instruções de sistema para o prompt:
**Important Instructions:**
- Respond **only** with the JSON object in the exact format specified.
- Do **not** include any explanations, code blocks, or additional text.
- Do **not** enclose the JSON in triple backticks or any markdown formatting.
- Ensure all strings in the JSON are properly escaped.
- Escape special characters like newlines (`\\n`), tabs (`\\t`), and quotation marks (`\\"`) within strings.
- Do not use single quotes; use double quotes for all JSON keys and string values.
- Ensure the JSON is valid and parsable.
Também utilizamos a capacidade humana de raciocínio. Os jogadores são hábeis em interpretar e decifrar pistas crípticas. Ao criar enigmas que exigiam dedução lógica, capacitamos os jogadores a superarem quaisquer inconsistências potenciais na saída da IA. No final das contas, era uma questão de encontrar o equilíbrio certo entre o conteúdo gerado pela IA e a engenhosidade humana.
Este ano, atingimos um marco: o primeiro quebra-cabeça do Google I/O feito com a API Gemini. Para nossas equipes de design e engenharia, essa foi mais do que uma simples integração. Foi uma exploração cuidadosa de uma nova era de criação colaborativa com a IA. Não estávamos simplesmente criando um recurso; fomos pioneiros em uma nova abordagem para experiências interativas. Ao considerar o uso da API Gemini em seus projetos, lembre-se destas três lições principais para determinar a abordagem:
A IA está mudando a forma como os usuários interagem com nossos apps e jogos, abrindo portas para experiências de usuário novas e empolgantes.
Junte-se a nós on-line no Google I/O, em 20 e 21 de maio, e veja os incríveis anúncios deste ano, que serão transmitidos ao vivo do Shoreline Amphitheatre, em Mountain View. Incentivamos você a experimentar o Gemini e explorar seu potencial para criar experiências mais úteis e divertidas para os usuários. As possibilidades são infinitas!