← Voltar

Como funciona a IA que aprende a jogar Flappy Bird

Neste projeto, demonstramos como uma inteligência artificial aprende a jogar Flappy Bird utilizando redes neurais combinadas com algoritmos evolutivos (NEAT).

Durante várias gerações, os pássaros virtuais aprendem a saltar na hora certa, evitando obstáculos, até que se tornem capazes de voar por longos períodos sem colidir. Todo o processo envolve evolução progressiva, seleção natural simulada e adaptação baseada em desempenho.

Vídeo — Comunicação entre o jogo e o algoritmo NEAT.

Estrutura geral do sistema

O sistema é composto por duas partes principais que trabalham em conjunto:

  • O jogo Flappy Bird: Implementado em Python com a biblioteca pygame. Ele gerencia toda a mecânica do jogo, incluindo movimentação do pássaro, geração de canos, colisões e pontuação.
    • O pássaro segue regras de física simplificadas: gravidade constante, impulso ao pular e rotação da imagem baseada no movimento vertical.
    • O chão e o plano de fundo são renderizados continuamente, criando a sensação de movimento infinito.
    • Os canos são gerados com alturas aleatórias e espaçamento progressivo, aumentando a dificuldade conforme a pontuação cresce.
    • Colisões são detectadas com precisão usando máscaras de pixel (pygame.mask), garantindo que o pássaro só “morre” quando realmente toca os obstáculos.
    • Sons de pulo e pontuação proporcionam feedback auditivo, tornando a experiência mais imersiva.
  • A IA que joga automaticamente: Cada pássaro é controlado por uma rede neural criada e treinada pelo algoritmo NEAT. A IA decide quando pular com base em entradas do ambiente.
    • Cada geração possui dezenas ou centenas de pássaros, representando indivíduos distintos na população do NEAT.
    • Os inputs da rede neural incluem: posição vertical do pássaro e distância até a parte superior e inferior do próximo cano.
    • A saída da rede neural indica se o pássaro deve saltar ou permanecer no curso atual.
    • O NEAT ajusta tanto os pesos quanto a topologia da rede ao longo de várias gerações, maximizando a aptidão (fitness), baseada em distância percorrida e pontuação acumulada.
    • Indivíduos com melhor desempenho sobrevivem e se reproduzem, permitindo evolução progressiva e adaptação ao ambiente.

O objetivo final é que a IA aprenda a voar o mais longe possível sem colidir, enquanto o jogo simula física realista e aumenta a dificuldade gradualmente.

O código define parâmetros dinâmicos para controlar dificuldade e evolução, como:

  • Velocidade dos canos que aumenta conforme a pontuação (Pipe.SPEED).
  • Espaçamento entre os canos ajustado dinamicamente para desafios mais difíceis à medida que o jogo avança.
  • Sistema de pontuação e fitness que influencia diretamente a evolução da rede neural.

Inputs da Rede Neural

Para tomar decisões, cada rede neural precisa “enxergar” o ambiente ao redor. Os inputs funcionam como sensores do pássaro, fornecendo informações essenciais para que a IA escolha a ação adequada a cada instante.

  • Altura do pássaro (bird.y) — posição vertical atual do pássaro na tela. Permite avaliar se ele está alto ou baixo em relação ao próximo obstáculo.
  • Distância até o topo do próximo cano (abs(bird.y - pipe.height)) — mede o espaço vertical entre o pássaro e a parte superior do cano. Indica se precisa subir para evitar colisão.
  • Distância até a base do próximo cano (abs(bird.y - pipe.bottom_pos)) — mede a distância até a extremidade inferior do cano, ajudando a prevenir quedas ou saltos desnecessários.

Esses três inputs fornecem informações detalhadas sobre:

  • Posição relativa do pássaro em relação ao obstáculo seguinte.
  • Necessidade de saltar ou manter a trajetória atual.
  • Como ajustar o movimento vertical para percorrer a maior distância possível.

A rede neural processa esses valores e gera uma saída, que determina se o pássaro deve pular. Com a evolução, a IA aprende padrões complexos, antecipando obstáculos e reagindo rapidamente a mudanças de altura.

Diagrama ilustrando os três inputs usados pela rede (altura do pássaro e distâncias superior e inferior do cano).

Figura 2 — Diagrama dos três inputs usados pela rede neural (altura do pássaro e distâncias ao cano).

Estrutura da Rede Neural

A rede neural é organizada em camadas que processam os inputs e decidem a ação do pássaro:

  • Camada de entrada: recebe os 3 inputs do jogo, que funcionam como sensores do ambiente.
  • Camadas ocultas: presentes ou não dependendo da configuração do NEAT. Permitem à rede aprender padrões complexos, como prever movimentos futuros ou calcular trajetórias seguras.
  • Camada de saída: possui um único neurônio que gera um valor contínuo, interpretado como:
    • Output maior que 0.5 → Pular
    • Output ≤ 0.5 → Não pular

Como a decisão é calculada:

Cada input é multiplicado por seu peso correspondente, somado a um bias e processado por uma função de ativação (tanh). Sem o bias, a saída seria sempre zero se todas as entradas forem zero (dependendo da função de ativação). O bias desloca a função para cima ou para baixo, dando mais flexibilidade à rede para aprender padrões. O resultado contínuo é convertido em ação:

input_y = 30
input_cano_sup = 4
input_cano_inf = -7
pesos = [0.5, 2, 1.3]
bias = -5

soma = 30*0.5 + 4*2 + (-7*1.3) + (-5) = 8.9
output = tanh(soma) ≈ 0.999

Se output > 0.5 → PULA
Se output ≤ 0.5 → NÃO PULA

O NEAT permite que a topologia da rede evolua: adicionar ou remover nós e conexões, ajustando pesos e biases para aumentar a eficiência e maximizar a pontuação.

Resumindo, a rede neural transforma informações do ambiente em decisões concretas (pular ou não) e sua estrutura evolui automaticamente para melhorar a performance da IA ao longo do tempo.

Exemplo visual do algoritmo NEAT em ação, mostrando a evolução das conexões e nós ao longo das gerações.

Exemplo de evolução de topologia de rede pelo NEAT, mostrando nós e conexões sendo alterados ao longo das gerações.

Figura 3 — Exemplo da evolução da topologia de redes com NEAT (nós e conexões).

Algoritmo Evolutivo (NEAT)

A IA aprende usando o NEAT (NeuroEvolution of Augmenting Topologies), um algoritmo genético especializado em redes neurais, que simula evolução biológica para melhorar o desempenho ao longo das gerações.

  • População: dezenas ou centenas de pássaros são criados, cada um com uma rede neural única. Pesos e biases iniciais são aleatórios.
  • Fitness: pontuação baseada no desempenho (distância percorrida, canos ultrapassados, tempo de sobrevivência), indicando aptidão para reprodução.
  • Seleção: apenas os pássaros com maior fitness têm maior chance de transmitir genes à próxima geração, mas pode selecionar os 20% melhores para a recombinação genética.
  • Crossover: mistura de genes de 2 indivíduos selecionados, combinando características (média dos pesos) de ambos.
  • Mutação: pequenas alterações aleatórias nos pesos, 80% das conexões sofrem uma mutação, a variação média no valor do peso é ±0.5 e 10% das conexões têm o peso totalmente substituído por um novo valor aleatório.

Diferente de algoritmos genéticos tradicionais, o NEAT também evolui a topologia da rede:

  • Adiciona novos neurônios ou conexões entre eles.
  • Mantém histórico de inovações para preservar estruturas vantajosas durante crossover.
  • Permite aumentar a complexidade gradualmente, aprendendo estratégias mais sofisticadas.

Analogia: Algoritmo Genético clássico altera apenas atributos; NEAT altera atributos e estrutura interna, como adicionar asas ou mudar conexões internas do organismo.

Fluxo do NEAT no jogo:

  1. Criar população inicial de redes neurais aleatórias.
  2. Cada pássaro joga, calculando fitness em tempo real.
  3. Selecionar indivíduos de maior performance para reprodução.
  4. Aplicar crossover e mutação, incluindo novos nós ou conexões.
  5. Repetir por várias gerações até obter redes eficientes.

No código, classes como Population, Genome e o método pop.run(main, 100) controlam o processo, executando o jogo por 100 gerações ou até atingir fitness máximo.

Ilustração do processo de seleção natural no NEAT, onde apenas os melhores indivíduos passam seus genes para a próxima geração.

Ilustração do processo de seleção no NEAT mostrando indivíduos escolhidos para reprodução com base no fitness.

Figura 4 — Processo de seleção no NEAT (indivíduos selecionados para reprodução).

Gráfico mostrando a IA alcançando alta pontuação após várias gerações de treinamento.

Figura 5 — Resultado final: IA dominando o jogo após várias gerações.

Baixar o projeto

Você pode baixar o projeto completo com todos os arquivos necessários para executar o Flappy Bird AI:

Baixar projeto (Flappy-Bird-AI.zip)

Como executar localmente

  1. Pré-requisitos: Python 3.8 ou superior.
    python -m venv venv
    source venv/bin/activate  # macOS/Linux
    venv\Scripts\activate   # Windows

    Cria e ativa um ambiente virtual, isolando as dependências do sistema.

  2. Instalar dependências:
    pip install -r requirements.txt

    O arquivo requirements.txt contém todas as bibliotecas necessárias, incluindopygame (motor gráfico) e neat-python (algoritmo evolutivo).

  3. Estrutura mínima do projeto:
    /assets
    /src
      └── /game
           └── FlappyBird.py
           └── configs.txt
    requirements.txt

    A pasta /assets contém as imagens e sons do jogo, enquanto /config define os parâmetros de evolução usados pelo NEAT.

  4. Executar o jogo:
    python src/game/FlappyBird.py

    Durante a execução, o terminal exibirá informações como:

    • Média de fitness da população
    • Melhor fitness da geração
    • Quantidade de espécies e progresso evolutivo

    Esses dados permitem acompanhar a evolução da IA em tempo real.

Exemplo de logs e métricas exibidas durante a execução do treinamento, permitindo acompanhar a evolução da IA em tempo real.

Captura de tela de logs e métricas exibidas durante o treinamento (média de fitness, melhor da geração).

Figura 6 — Logs e métricas exibidas durante o treinamento (fitness, melhor da geração).

Dicas e experimentos

  • Aumente pop_size no arquivo de configuração para ampliar a diversidade genética e acelerar o aprendizado.
  • Teste funções de ativação diferentes (sigmoid, relu, etc.) para observar mudanças no comportamento da IA.
  • Ajuste a função de fitness para enfatizar critérios específicos, como distância percorrida ou estabilidade de voo.
  • Salve o melhor genoma usando pickle para reutilização posterior sem reiniciar a evolução.
  • Observe o terminal para métricas avançadas como tempo por geração e divergência genética (mean genetic distance), úteis na análise de performance.

Seguindo essas etapas, você poderá executar o Flappy Bird AI localmente, testar modificações e explorar a evolução do aprendizado em tempo real.