Jogo Da Galinha Que Atravessa A Rua - O JOGO DA GALINHA QUE ATRAVESSA A RUA 1# JOGOS ALEATÓRIOS - YouTube
O JOGO DA GALINHA QUE ATRAVESSA A RUA 1# JOGOS ALEATÓRIOS - YouTube

Guia prático do jogo da galinha que atravessa a rua

O jogo da galinha que atravessa a rua é aquele exercício clássico onde você controla uma ave precisa chegar ao outro lado de uma estrada com carros se movendo em velocidades e padrões diferentes. Parece simples na teoria, mas a parte programática dele já mandou muita gente tentar implementar o pathfinding e travar por horas.

Como o jogo da galinha que atravessa a rua funciona na prática

Você define um grid, digamos 8 colunas por 6 linhas. A galinha começa na base, os carros vêm em faixas horizontais com velocidades variáveis. O objetivo é calcular uma sequência de movimentos (cima, esquerda, direita) que leve ela até a linha de chegada sem ser atropelada em nenhuma iteração do loop principal. O que as pessoas não entendem de cara: isso é essencialmente um problema de busca em grafo com restrições temporais, não só espaciais. Cada frame do jogo importa porque os carros se reposicionam a cada ciclo. Então o estado não é só a posição da galinha, é a posição da galinha mais a fase do ciclo dos veículos.

Eu já perdi umas duas noites num bug que parecia aleatório. O problema era que o algoritmo A* que eu tinha implementado avaliava apenas a posição atual, mas não considerava que um carro poderia chegar na mesma célula meia segunda depois que a galinha passasse. O solucionador achava que tinha caminho livre e a galinha atravessava numa faixa exatamente no momento errado. A workaround foi simples: incluir um offset temporal na função heurística e travar movimentos para células que tivessem risco de colisão nos próximos N frames, não só no frame presente.

Implementando do zero

Se você tá começando agora, esquece bibliotecas bonitas. Começa com uma estrutura de dados brute. Primeiro, define o grid e as faixas de tráfego. Cada faixa tem velocidade, direção e um padrão de espaçamento. Carros vindo da esquerda em velocidade 2, da direita em velocidade 3, com gaps de talvez 3 a 5 células entre eles. O ciclo se repete a cada X frames, onde X é o mínimo múltiplo comum das velocidades, ou algo próximo disso pra não ter que calcular período exato.

Depois, implementa o movimento da galinha. Cima aumenta o índice da linha, esquerda diminui a coluna, direita aumenta. Sem diagonal. Sem pulos. Se ela sai do grid, você descarta esse branch na busca. O motor de colisão é onde a maioria erra. Em vez de checar só se há um carro na célula alvo no frame atual, você precisa projetar a trajetória. Um jeito barato é verificar se alguma faixa de tráfego intersecta a célula de chegada em qualquer frame dentro de uma janela de segurança. Se a janela for de 2 frames e o carro passa naquele frame e no seguinte, já é risco suficiente pra rejeitar o movimento.

Algoritmos que realmente funcionam

DFS puro dá solution às vezes, mas explode combinatoriamente. Num grid 8x6 com 3 faixas de carro, você chega a milhões de estados em poucos segundos e o algoritmo nunca termina. BFS garante o caminho mais curto em número de passos, mas o custo computacional é alto demais pra busca em tempo real se você não usar poda agressiva. A* com heurística de Manhattan funciona bem se você calibrar o peso temporal. Em vez de usar apenas distância manhattan como heurística, adiciona um termo que penaliza movimentos em faixas com tráfego intenso. Algo como h(n) = distancia_manhattan + alpha * densidade_de_carros_no_caminho_esperado. O alpha precisa ser ajustado por tentativa. Valores muito altos fazem o algoritmo evitar faixas perigosas demais e encontrar caminhos excessivamente longos. Valores muito baixos trazem o problema do frame-atrasso de novo.

👉 Clique no botão abaixo para saber mais sobre o assunto!

Outra opção que funciona decente é Monte Carlo Tree Search adaptado. Você simula aleatoriamente caminhos possíveis e os mais seguros. É mais lento pra primeira consulta mas escala melhor quando o grid fica maior, porque a árvore de decisão naturalmente descarta ramos perigosos early.

Armazenamento e replay

Se o jogo vai salvar sequências de movimentos, use um array compacto. Cada movimento cabe num byte: 0 pro cima, 1 pra esquerda, 2 pra direita. Uma sequência de 200 movimentos são 200 bytes. Se quiser compressão extra, codifica em escape sequences — três movimentos iguais seguidos viram um par (valor, contador). Isso reduz arquivos de replay de cerca de 40% em média, o que é relevante se você tiver centenas de runs salvas. Eu já tive um problema chato onde o replay falhava em velocidades altas porque o número de frames entre cada movimento não era registrado corretamente. A solução foi adicionar um timestamp relativo em cada entrada do replay, não só o comando. Aí o replay sempre sincroniza independentemente do FPS do jogo.

Pegadinhas comuns

Um erro recorrente é assumir que os carros têm periodicidade perfeita. Eles podem ter fases diferentes, especialmente se você inicializa várias faixas com offsets aleatórios. Num cenário que eu teste recentemente, duas faixas com velocidades 3 e 5 e offsets de 1 e 4 criavam um padrão de colisão que só se repetia a cada 15 frames, não a cada 3 ou 5. O solucionador que eu tinha escrito assumia período igual ao máximo das velocidades e falhava silenciosamente nos casos de fase dessincronizada. Outro problema é o caso onde a galinha fica presa num canto. Movimentos laterais podem empurrar ela contra a parede e o algoritmo não tem forma de sair se você não permitir retroceder. Certifique-se de que o grafo de estados permita movimentos para trás, senão você vai ter false positives de "impossível resolver" em níveis que na verdade são resolvíveis.

Dicas para otimização

Se o grid for maior que 10x8, considere cache de estados visitados. Um dicionário mapeando (linha, coluna, frame_modulo_periodo) para booleano de visitado elimina repetição de trabalho. No meu setup, isso reduziu o tempo de busca de cerca de 4 segundos pra algo em torno de 300 milissegundos num grid 12x8 com 5 faixas de tráfego. Se você precisa de resposta instantânea, pré-compute os padrões de tráfego em uma tabela lookup. Cada faixa gera uma lista de posições por frame. A colisão vira uma consulta de array em vez de um loop condicional. Isso corta o custo por verificação de colisão de aproximadamente 12 operações pra 2 operações.

Para publicação ou competição, o critério costuma ser tamanho do programa, velocidade de resolução ou comprimento do caminho. Não adianta ter o solucionador mais rápido se ele gera sequências com 500 movimentos quando uma solução de 80 movimentos existe. Adicione um pós-processamento que tente encurtar o caminho removendo loops óbvios e movimentos redundantes. Num teste real, esse passo reduziu o tamanho médio da solução em 35% sem aumentar o tempo de computação significativamente.

Aprendizado e próximos passos

Se quiser evoluir o projeto, o próximo nível natural é adicionar galinhas com IA que aprendem. Um algoritmo genético simples funciona: populações de sequências de movimentos, avaliação por fitness (distância percorrida menos penalidade de colisão), crossover e mutação. Em cerca de 200 gerações com população de 500 indivíduos, você vê surgindo comportamentos coerentes de desvio de veículos. Não é elegante, mas é funcional e didático. Outra direção é transformar isso num benchmark para algoritmos de planejamento sob incerteza. Se você introduz carros com velocidade variável ou comportamento não periódico, o problema deixa de ser determinístico e ganha caráter de MDP (Markov Decision Process). Aí entra Q-learning ou policy gradient. É um salto conceitual grande, mas transforma um jogo simples num laboratório interessante.

Se o objetivo é sóResolver o jogo sem codear nada, existem solvers prontos que aceitam input de grid e retornam a sequência de movimentos. Procure por implementações em Python ou JavaScript. A versão em Python com library networkx e numpy costuma ser a mais acessível, mas exige configuração de ambiente com dependências específicas. A versão web roda direto no navegador e é mais rápida pra testes pontuais. O jogo em si pode ser encontrado em repositórios open source com licença MIT ou GPL, dependendo da implementação. Antes de usar em projeto comercial, verifique a licença. Alguns forks incluem assets gráficos que têm restrições diferentes do código base.