Usando JavaCV para Jogar Dinossauro VS Cactus

Recentemente implementei, como parte de meus estudos de visão computacional, um bot que joga o famoso game do Dinossauro x Cactus. O Objetivo era conseguir encontrar os personagens do jogo na tela do navegador e a partir disso ser capaz de tomar decisões, como realizar um salto, não fazer nada ou reiniciar o jogo.

Nesse post não tento discutir sobre melhores técnicas, ou melhor forma de implementação, e sim mostrar como consegui resolver esse problema.

Para executar essa missão foi necessário combinar alguma técnicas de RPA simples como envio de comando de tecla e captura de tela, visão computácional para detecção de objetos do jogo. Além disso alguns calculos simples e lógica procedural para tomada de decisão.

Detalhes

  1. Capturando a tela

Nesse primeiro ponto usei um JFrame para servir de delimitador dinâmico de área de captura, e uma segunda tela que sera responsável por acompanhar o que está sendo capturado e realizar todo o controle do jogo. O processo está exibido nas três capturas de tela abaixo.

preparando a área de captura de tela
área de captura definida pelo Jframe
captura refletida na tela de controle

Dentro do pacote de ferramentas disponíveis na API JavaSE existe o toolkit e a classe Robot, que sempre me ajudaram em minhas pequenas tarefas de RPA. Para este projeto foram usados na captura de uma área da tela do computador e no envio de comandos.

Captura de tela a partir das coordenadas previamente definidas

2. Enviando comandos

O envio de comando é realizado através de uma instância de Robot, onde temos comandos de tecla para baixo (dinossauro corre abaixado), tecla para cima (salto) e tecla de espaço (apertar o botão de reiniciar jogo). Dependendo do sistema operacional em uso será necessário permitir o envio de comandos na primeira execução.

Abstração para enviar comandos de tecla.

3. Detectando objetos na tela

A detecção é realizada através da biblioteca JavaCV, que é uma abstração em cima do OpenCV, e que considerei simples de integrar aos meus projetos Java desktop e Android pois possui suporte ao Gradle.

A principal feature para esta tarefa foi o Match Template, que basicamente busca por uma imagem dentro de outra, devolvendo seu posicionamento e um nível de pontuação (Score) para a detecção. Com essa funcionalidade foi possível procurar os personagens do jogo e a partir disso tomar as decisões, eviar comandos etc.

Os templates são as imagens que iremos buscar dentro da tela do jogo e aparecem dividios em 3 grupos: Dinossauro, cactus e botão de reset. Abaixo temos a lista dos templates usados. Destacando que o cactus grande teve melhor resultado deixando apenas o caule, usar apenas a cabeça do dinossauro evitou ambiguidades com o botão de reset e com os cactus, e o botão reset teve o branco sobrescrito com transperente.

Da esqueda para direita: Cactus grande, Cactus pequeno, Dinossauro, Botão reset

O processo de leitura dos templates assim como das capturas de tela passam por ler a imagem e depois transformá-la em escala de cinza. O JavaCV trabalha com matrizes representando a imagem em análise, a classe Mat é a abstração do JavaCV pra esse fim.

Carregando imagem de um template
Convertendo imagem para tons de cinza
Capturando imagem da tela e transformando em Mat.

Para realizar a detecção passamos o Mat correspondente ao template, o cenário do jogo, e um valor de corte para aceitar a detecção ou não. Depois de chamar o matchTemplate, chamamos o minMaxLoc para avaliar os resultados da detecção. Dentro do minVal e maxVal estarão as coordenadas do valor mais baixo e do mais alto respectivamente, assim como o valor de score. Caso o valor de score capturado através de maxVal.get(0) seja aceitável retornaremos as coordenadas do retangulo onde ocorreu a detecção. Além disso pintamos um retangulo em cima da área detectada e adicionamos um texto com o nome do template e valor de score encontrado.

realizando match template

4. Tomando decisão baseado nas detecções

A cada 50ms repete-se o processo de captura da tela do jogo, detecção dos templates e tomada de decisão. Quando é detectado o template do reset significa que ocorreu uma colisão e precisamos reiniciar o jogo, isso é feito chamando o RobotControl no metodo sendSpaceKey().

A detecção do dinossauro é vital para que possamos tomar decisão quanto ao que fazer em relação aos cactus. Por isso esse é o primeiro template a ser validado na tela.

Um cálculo simples baseado na distancia do dinossauro aos cactus e na velocidade de aproximação indica o momento em que devemos chamar RobotControl no método jump() que executará o salto. Ainda é validado se o dinossauro está se aproximando de um único cactus ou de um bloco de cactus. A implementação considera que devemos saltar mais distante quando nos aproximamos de um unico cactus e saltar o mais próximo possível quando nos aproximamos de um bloco de cactus.

5. Execuções

Em geral o bot teve uma boa resposta nas detecções conseguindo trabalhar com a tela do navegador, para tamanhos de 100% a 133% sem perda da qualidade. No entando o calculo da distância teve melhor resultado em 133%. No navegador Chrome as detecções ocorreram corretamente mas o cálculo da distância mostrou-se pouco robusto. Mesmo assim o objetivo da POC foi alcançado e firefox a execução ocorreu sem maiores dificuldades.

O uso da tela de delimitação da área de captura ajudou a elevar a performance. As operações de seguimentação por cor, bordas, morfológicas etc, podem melhorar as detecções mas no primeiro instante aumentaram o tempo de execução diminuindo a capacidade de resposta do bot. Assim por conta dos resultados já alcançados e por conta de performance essas operações não foram realizadas e apenas a conversão para tons de cinza com o matchTemplate foram realizadas.

A velocidade de execução e o tempo de reação foi aceitável, o dinossouro conseguiu executar saltos e alcançar valores de pontuação máxima de 1300 pontos. Abaixo temos um print de uma execução acima dos 1000 pontos.

Print de uma das melhores execuções

Resumo

  1. O uso do matchTemplate do JavaCV permitiiu implementar uma primeira versão com boa qualidade de detecção e reação, para o jogo do Dinossauro Vs cactus.
  2. A quantidade de dados coletados e percepção de momento em que ocorreu a derrota, permite estender a POC a um treinamento por reforço que poderia substituir as rotinas de decisão implementadas de forma procedural.
  3. Em alguns momentos a detecção falhava e por isso o dinossauro se chocava com os cactus. Isso indica necessidade de melhorias no processo, as primeiras conclusões apontam para falha por número alto de cactus na tela. Isso pode ser otimizado aumentando a quantidade máxima de detecções feitas na tela. Entretanto não foi realizada uma investigação mais criteriosa para o problema.
  4. Este projeto me ajudou a entender mais sobre a bilbioteca JavaCV e suas funcionalidades.

Referências

Tutorial MatchTemplate

Usando a classe Robot referência