Por que todo mundo erra a implementação de jogo da velha
A maior parte dos tutoriais que você encontra na internet começa com tabuleiros HTML bonitos e lógica JavaScript que funciona perfeitamente — até você testar no celular. Foi exatamente aí que eu percebi que a maioria dos desenvolvedores web não considera como o jogo da velha se comporta em telas pequenas, onde o usuário pode tocar fora da área do quadrado e o evento de clique simplesmente não disparar. O que costuma acontecer é isso: você cria um jogo jogo da velha online com grid de três por três usando eventos onclick em cada célula. No desktop funciona. No smartphone, o primeiro toque que não é exatamente dentro do div da célula é ignorado ou dispara em outra parte da tela. A solução prática é adicionar um listener de touchstart com preventDefault() e mapear coordenadas de toque para a grade usando getBoundingClientRect() em vez de depender apenas do clique nativo.
O bug silencioso que ninguém menciona
Eu fiz uma versão de jogo jogo da velha online em 2021 e publiquei num repositório. Alguém abriu um issue dizendo que o X sempre vencia no segundo turno quando o computador jogava pela terceira vez. Passei duas horas rastreando o problema. O motivo era simples e frustrante: eu estava chamando a função de verificação de vitória antes de atualizar o estado do tabuleiro na jogada da IA. O computador pensava que ainda havia espaço livre numa célula que já estava marcada, então ele jogava lá de novo e sobrescrevia a verificação. A correção foi mover a função de validação para depois de cada jogada, tanto do jogador quanto da IA, e validar o estado atual do array, não uma cópia desatualizada. Desde então, adoto o hábito de usar um único estado centralizado para o tabuleiro e nunca referenciar variáveis espelhas durante a execução.
Implementação com inteligência artificial básica
O jogo da velha é um dos raros jogos de tabuleiro que já tem solução completa conhecida. O resultado ótimo para ambos os jogadores é sempre empate. Isso significa que implementar uma IA invencível para esse jogo é tecnicamente simples: basta usar o algoritmo Minimax. O Minimax explora todas as combinações possíveis de jogadas e atribui pontuações: +10 para vitória da IA, -10 para vitória do jogador humano, 0 para empate. Em cada turno, a IA escolhe o caminho que maximiza seu resultado e assume que o adversário minimizará o seu. Para o jogo da velha, isso gera cerca de 9! (362.880) estados no pior caso. Na prática, com poda alfa-betta, o número cai para menos de 10.000 avaliações, o que roda em milissegundos em qualquer dispositivo moderno.
Um detalhe importante: não implemente o Minimax puro sem poda em produção. Já vi código que usa recursão profunda sem alpha-beta e o browser trava por meio segundo inteiro enquanto calcula. Isso é perceptível para o usuário e quebra a fluidez do jogo.
Estrutura mínima que funciona
Aqui está uma estrutura funcional que eu recomendo como ponto de partida:
👉 Clique no botão abaixo para saber mais sobre o assunto!
const board = Array(9).fill(null);
let currentPlayer = 'X';
let gameOver = false;
const winConditions = [
[0,1,2],[3,4,5],[6,7,8],
[0,3,6],[1,4,7],[2,5,8],
[0,4,8],[2,4,6]
];
O array de condições de vitória é fixo e contém todas as oito linhas possíveis. Você pode verificar vitória com uma função simples que itera sobre essas combinações e testa se as três posições têm o mesmo valor diferente de null. Para o movimento da IA, o Minimax retorna o índice da melhor posição. Você mapeia esse índice para a célula correta no DOM e atualiza o estado. Não use animações CSS longas antes de processar a jogada da IA. Animações de 500ms ou mais criando delay entre a jogada do jogador e a resposta da máquina causam percepção de lentidão desnecessária.
Alternativas ao Minimax para projetos menores
Se você está construindo algo rápido e não precisa de uma IA imbatível, considere uma abordagem heurística com prioridade de movimentos. A ordem ideal é: centro, cantos, depois lados. Esse padrão derrota jogadores humanos casuais na maioria das vezes e roda instantaneamente, sem recursão alguma. O problema dessa estratégia é que jogadores que conhecem a sequência de aberturas conseguem forçar empate contra ela. Se o seu público-alvo joga com frequência, essa simplificação pode parecer fácil demais e gerar reclamações. A escolha entre Minimax completo e heurística depende do nível de dificuldade que você quer entregar, não da complexidade técnica.
O jogo jogo da velha online como projeto de aprendizado
Se o objetivo é criar um jogo jogo da velha online como exercício de programação, foque nos pontos que mais dão trabalho na prática: estado compartilhado, tratamento de eventos em múltiplos dispositivos e a lógica de decisão da IA. A maior parte dos erros que aparecem em projetos reais vem de gerenciamento de estado inconsistente, não de algoritmos complexos. Use um objeto de estado central. Não misture dados da interface com dados do jogo. Separe a camada de renderização da camada de lógica. Isso evita bugs como o que eu descrevi anteriormente, onde a interface mostrava um tabuleiro atualizado mas a lógica interna ainda via o estado anterior.
Teste em pelo menos três tamanhos de tela diferentes antes de considerar o projeto pronto. A maioria dos desenvolvedores pula essa etapa e só descobre problemas de usabilidade quando recebe feedback de jogadores móveis.
Recursos para começar
Você pode encontrar repositórios de referência no GitHub buscando por "tic-tac-toe minimax javascript" ou "jogo da velha open source". Procure projetos que tenham pelo menos 100 estrelas e issues abertas respondidas, pois isso indica manutenção ativa. Evite forks muito recentes sem histórico de uso. Para hospedagem gratuita, plataformas como GitHub Pages, Netlify ou Vercel oferecem deploy direto de projetos estáticos sem configuração de servidor. O jogo da velha não requer backend, então essa é uma opção válida e econômica.
Uma última observação prática: se você pretende adicionar multiplayer online, precisará de um servidor WebSocket ou serviço como Socket.io com Node.js. A latência entre jogadores em tempo real exige sincronização de estado do lado do servidor, caso contrário um jogador pode ver movimentos diferentes do que o outro vê. Esse é um salto significativo em complexidade em relação à versão single-player com IA.