← Todos os posts

Como Eu Construí o 2048 em um Fim de Semana — Todo o Loop do Jogo

2026-06-29

Adicionei o 2048 ao ToolKoala recentemente, e a surpresa mais agradável foi como o jogo de verdade é pequeno. As animações e o polimento levam tempo, mas as regras — deslizar, mesclar, gerar, verificar fim de jogo — cabem em cerca de sessenta linhas. Veja como todo o loop funciona.

O tabuleiro é só um array plano

A grade é 4×4, mas eu a armazeno como um array plano de 16 números, onde 0 significa vazio:

[2, 0, 0, 4,
 0, 2, 0, 0,
 0, 0, 0, 0,
 4, 0, 0, 2]

Arrays planos são fáceis de copiar, comparar e salvar no localStorage. A linha r, coluna c fica no índice r * 4 + c.

Uma função faz o trabalho de verdade

Todo movimento — esquerda, direita, cima, baixo — é a mesma operação em uma linha de quatro peças: empurrar tudo para uma das pontas e mesclar vizinhos iguais uma vez. Isso é uma única função:

function compress(line) {
  const arr = line.filter(v => v)   // descarta os zeros
  const res = []
  let gained = 0
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] === arr[i + 1]) {     // duas peças iguais se tocam → mescla
      res.push(arr[i] * 2)
      gained += arr[i] * 2
      i++                            // pula a que mesclamos
    } else {
      res.push(arr[i])
    }
  }
  while (res.length < 4) res.push(0) // completa de volta ao tamanho 4
  return { line: res, gained }
}

O i++ após uma mesclagem é a regra que impede 4 4 4 4 de colapsar em um único 16. Ele vira 8 8 — cada peça mescla no máximo uma vez por movimento.

Quatro direções a partir de uma direção

Eu só escrevi "deslizar para a esquerda". As outras três a reutilizam:

  • Direita — inverta a linha, comprima para a esquerda, inverta de volta.
  • Cima / Baixo — leia o tabuleiro por colunas em vez de linhas, e então a mesma lógica de esquerda/direita.

Então um movimento pega cada linha ou coluna, opcionalmente a inverte, executa o compress e a escreve de volta. Nenhum código separado para cada seta — apenas qual linha eu leio e se eu a viro.

Gere, e então verifique se você travou

Depois de um movimento que de fato mudou o tabuleiro, uma nova peça aparece em uma célula vazia aleatória — um 2 na maioria das vezes, um 4 ocasionalmente. Então eu verifico se algum movimento ainda é possível: há células vazias, ou quaisquer dois vizinhos iguais? Se não, é fim de jogo.

function canMove(g) {
  if (g.includes(0)) return true            // célula vazia → sim
  // qualquer vizinho horizontal/vertical igual → uma mesclagem ainda é possível
  // ...verificado com um pequeno loop...
  return false
}

A verificação "o tabuleiro mudou de verdade?" importa: se um movimento não faz nada, você não deveria ganhar uma peça nova de graça. Apertar esquerda contra uma parede tem que ser uma operação sem efeito.

O bug que me ensinou a usar refs

A primeira versão lia o tabuleiro direto do estado do React dentro do manipulador de teclas. Em repetição rápida de teclas, dois apertos de seta podiam disparar antes do React re-renderizar, então o segundo lia um tabuleiro desatualizado e movimentos eram descartados. A correção foi manter uma ref espelhando o tabuleiro e a pontuação mais recentes, e fazer o manipulador de movimento ler a ref. O estado controla o que você vê; a ref controla a lógica. Depois disso, até martelar as setas se comporta bem.

Continuar e melhor pontuação, tudo local

Duas coisas vivem no localStorage: sua melhor pontuação e o tabuleiro atual. Salve o tabuleiro a cada movimento; limpe-o quando o jogo termina. Ao carregar, se houver um jogo válido não terminado, eu o restauro e mostro uma pequena nota de "continuar" — o mesmo padrão que uso para o Sudoku e o Caça-Palavras. Nada é enviado; seu jogo é seu, no seu dispositivo.

Experimente

Você pode jogar 2048 aqui — teclado ou deslize, e funciona offline depois que a página carregou. Se você gosta desse tipo de coisa, o gerador de Sudoku tem um algoritmo mais interessante por trás.

Perguntas frequentes

É difícil programar o 2048? O núcleo é surpreendentemente pequeno — uma função de "deslizar e mesclar uma linha", reutilizada para as quatro direções, mais gerar uma peça e verificar o fim de jogo. O polimento (animações, controles de toque, salvar o progresso) é onde vai a maior parte do tempo.

Como funciona a regra de mesclagem? Quando duas peças com o mesmo número são empurradas juntas, elas se combinam em uma peça com o dobro do valor — e cada peça só pode mesclar uma vez por movimento, então uma linha de quatro 2s vira dois 4s, não um 8.

Como as quatro direções são tratadas? Você só precisa implementar uma direção. Direita é esquerda com a linha invertida; cima e baixo são a mesma lógica aplicada a colunas em vez de linhas.

O 2048 do ToolKoala salva meu jogo? Sim — sua melhor pontuação e o tabuleiro atual são armazenados no armazenamento local do seu navegador, então você pode fechar a aba e continuar depois. Ele nunca sai do seu dispositivo.

— Milo 🐨