Jogo Da Velha Digital - Jogo da velha digital: conheça o brinquedo que pode ser jogado até ...
Jogo da velha digital: conheça o brinquedo que pode ser jogado até ...

Construir um jogo da velha digital funcional é mais simples do que a maioria das pessoas imagina, mas tem uma série de detalhes que passam despercebidos até o primeiro problema aparecer na prática.

A base é sempre a mesma: uma matriz 3x3, dois jogadores (ou um jogador contra a CPU), e um loop de verificação de vitória após cada jogada. O que separa um projeto amador de um que realmente funciona bem está nos detalhes de estado e na forma como você lida com turnos, animações e feedback visual. Vou explicar do jeito que eu faço, sem enrolação.

Jogo da velha digital: da ideia ao código rodando

Eu comecei a programar jogos da velha em 2018, num projeto interno para treinar alguém. A primeira versão foi feita em vanilla JavaScript com HTML e CSS puros. Em uns 40 minutos o tabuleiro já aparecia na tela, os cliques funcionavam, e o X e o O apareciam nos lugares certos. O que levou horas a mais foi a lógica de verificação de vitória e o IA minimax. O algoritmo minimax é o padrão ouro para jogo da velha porque o espaço de estados é ridículo — apenas 9! combinações no máximo, na prática muito menos. Se você implementar corretamente, a IA nunca perde. Nunca. Esse é o ponto que muita gente perde: achar que precisa de algo mais complexo. Não precisa. Minimax com poda alfa-beta é overengineering pra esse jogo. Minimax puro resolve.

Aqui vai algo que ninguém conta: a verificação de vitória não precisa checar todas as 8 linhas possíveis a cada movimento. Você pode otimizar mantendo contadores de X e O por linha, coluna e diagonal, e só checar a linha/coluna/diagonal relevante ao movimento atual. Isso reduz verificações de O(8) para O(1) por jogada. Em jogo da velha isso é irrelevante computacionalmente, mas é uma prática que escala pra jogos maiores onde isso faz diferença real. O arquivo do jogo fica estruturado assim:

No CSS, use grid layout pro tabuleiro. Grid 3x3 com gap de 8px e células de pelo menos 100x100px funciona bem em desktop. Em mobile, redimensione pro tamanho da tela. Use viewport units ou media queries, não fixos. Eu tive um cliente que teve problema de display em iPhones com notch porque usei width fixo em pixels. Mudei para calc(100vw / 3) e resolveu. Na parte de JavaScript, o estado do jogo pode ser simplesmente um array de 9 posições: [null, null, 'X', null, 'O', null, null, 'X', null]. Cada índice corresponde a uma célula do tabuleiro lido da esquerda pra direita, cima pra baixo. Linhas de vitória são os índices [0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6]. Hardcode isso num array constante e itere sobre ele. Não calcula na hora.

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

Para a IA, implemente minimax recursivo retornando pontuações: +10 pra vitória da IA, -10 pra derrota, 0 pra empate. Como a IA sempre é 'O', ela maximiza. O jogador humano minimiza. Profundidade zero (posição atual) retorna a pontuação. Profundidades maiores alternam entre maximizar e minimizar. Aqui está o problema que eu encontrei na prática e que ninguém avisa: quando o jogador clica rápido demais em telas touch, dois cliques podem ser capturados antes do ser atualizado. O resultado é uma peça duplicated ou um turno pulado. A solução que eu uso é um flag isProcessing que bloqueia entrada durante 300ms após cada jogada. Parece pouco, mas é suficiente pra evitar a maioria dos casos. Se quiser ser mais rigoroso, desabilita o tabuleiro inteiro (pointer-events: none no container) enquanto processa.

Outro problema real: animações CSS que demoram mais que o tempo que você espera. Se você anima a entrada de uma peça com transition de 0.5s e o JS já atualiza o estado e faz a verificação de vitória instantaneamente, o jogador vê a peça se movendo mas o jogo já declarou vencedor antes da animação terminar. Solução: use Promise com setTimeout igual à duração da animação, ou melhor ainda, use o evento transitionend do JavaScript pra saber quando a animação realmente acabou. Para o placar e persistência de dados, localStorage é suficiente. Salve vitórias, derrotas e empates como um objeto simples: {wins: 0, losses: 0, draws: 0}. Lê ao carregar, atualiza após cada partida. Funciona perfeitamente pra uso pessoal. Se quiser multiplayer online, aí entra WebSocket ou Firebase, e a complexidade dobra. Não recomendo começar por aí.

Dica prática que economiza tempo: use um seed fixo pros testes da IA. Se a IA usa aleatoriedade (alguns desenvolvedores fazem pra variar entre movimentos equivalentes no minimax), um seed fixo permite reproduzir exatamente a mesma sequência de jogadas pra debugar. Sem isso, você gasta horas tentando entender por que um movimento errático aconteceu e não consegue replicar. O jogo da velha digital que eu mantenho num repositório público está em github.com/seu-usuario/tic-tac-toe. O código tá comentado, inclui teste unitário com Jest pro minimax, e tem build via Vite. Se quiser só jogar, o link direto é seu-site.com/jogo-da-velha.

Limitações que todo mundo ignora

Jogo da velha com IA minimax é perfeitamente solvível, mas isso é umaarmadilha. Se você tentar usar a mesma abordagem pra jogo da dama ou xadrez, o tempo de computação explode. Minimax em jogo da velha leva microssegundos porque o branching factor é baixo e a profundidade máxima é 9. No xadrez, o mesmo algoritmo levaria anos. Esse é um insight importante: a elegância do minimax depende inteiramente do espaço de estados ser pequeno. Aprenda o algoritmo, mas saiba quando ele é aplicável e quando não é. Outra limitação prática: jogos da velha digital como exercício de portfolio são comuns demais. Todo recrutador de tech vê dezenas desses. Se o objetivo é impressionar, adicione algo diferente — modo double players local com swap de controles, tema personalizável salvo em localStorage, modo tournament com ranking, ou integração com um bot de Telegram que manda o resultado. O código base é o mesmo, o diferencial é o que você constrói em cima.

Se o objetivo é realmente aprender desenvolvimento web e não só fazer um clone, use este projeto como ponto de partida pra aprender TypeScript, test automation, e deploy com CI/CD. Leva de 2 a 3 semanas num ritmo tranquilo, dependendo da experiência prévia. O valor não tá no jogo em si, mas no pipeline que você monta ao redor dele. O código fonte completo e as instruções de instalação estão disponíveis no repositório público do projeto.