Batalha Naval Plano Cartesiano - Batalha Naval Plano Cartesiano - RETOEDU
Batalha Naval Plano Cartesiano - RETOEDU

Como implementar batalha naval usando plano cartesiano

Você já percebeu que o Battleship clássico é, na prática, um exercício de sistema de coordenadas? O tabuleiro é um plano cartesiano disfarçado de grade alfanumérica. A maioria dos tutoriais que você encontra na internet mostra o jogo pronto, mas raramente explica como construir a lógica por trás dele — e é aí que as coisas dão errado.

A base da batalha naval plano cartesiano

No plano cartesiano, cada linha é o eixo X (horizontal) e cada coluna é o eixo Y (vertical). No jogo de batalha naval, a intersecção desses dois eixos forma uma célula. Na prática, isso significa que cada posição no tabuleiro pode ser representada por um par ordenado (x, y). A coordenada (3, 5), por exemplo, indica terceira coluna, quinta linha. Isso é tudo o que o jogo precisa para funcionar. O que os manuais costumam omitir é que a convenção mais usada nos jogos não segue estritamente o padrão matemático do plano cartesiano. No plano cartesiano puro, Y cresce para cima. No tabuleiro de batalha naval, Y cresce para baixo — como em qualquer sistema de array ou matriz. Essa inversão é a causa número um de bugs quando alguém tenta programar o jogo do zero. Eu já corrigi isso em pelo menos quatro projetos diferentes. A solução é simples: ao renderizar a coordenada na tela, subtraia o valor de Y do tamanho total do tabuleiro menos um. Se o tabuleiro tem 10 linhas, a linha de índice 0 na matriz corresponde a Y = 9 no plano cartesiano visual. Não pule essa etapa.

Os navios são posicionamentos de sequências de coordenadas adjacentes. Um porta-aviões de 5 cascos ocupa cinco pares consecutivos alinhados horizontal ou verticalmente. A verificação de colisão exige checar se algum ponto do navio novo sobrepõe um ponto já ocupado. É uma comparação O(n) simples, mas fica cara se você fizer uma verificação ingênua a cada turno. A otimização razoável é manter um segundo mapa de booleanos, com o mesmo tamanho do tabuleiro, marcando apenas células ocupadas. Assim, a checagem vira uma leitura direta de array, não um loop sobre todos os navios.

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

Como funciona o disparo no plano cartesiano

Cada vez que um jogador atira, você converte a escolha dele em coordenadas (x, y) e consulta a matriz do oponente. Três estados são possíveis: água, navio ou já atingido. O estado "já atingido" existe porque o mesmo ponto pode ser disparado múltiplas vezes. Sem um registro separado de disparos, você acaba recontando hits ou, pior, tratando um tiro já registrado como se fosse novo. Aqui vai algo que quase ninguém menciona: o plano cartesiano expõe um viés psicológico nos jogadores. Posições centrais como (5, 5) atraem mais tiros porque parecem mais "prováveis". Na prática, isso não altera a probabilidade — todos os pontos têm chance idêntica. Mas como programador, você precisa lidar com isso se estiver implementando uma IA básica. Um algoritmo de caça ingênuo que só atira aleatoriamente perde para qualquer oponente que entenda distribuição. O ajuste mínimo é adicionar um padrão em cruz depois de um acerto: se (x, y) foi um hit, os próximos tiros devem cobrir (x+1, y), (x-1, y), (x, y+1) e (x, y-1). Isso converte uma IA passiva em algo competente em segundos.

Também vale notar que o plano cartesiano cria um problema real de borda que tutoriais ignora. Quando um navio toca a borda do tabuleiro, você precisa garantir que a lógica de rotação e posicionamento não permita coordenadas negativas ou maiores que o tamanho da grade. Implementei uma validação que recusa qualquer movimento onde x < 0, x >= largura, y < 0 ou y >= altura. Sem isso, o jogo crasha ou, em versões mais permissivas, permite navios parcialmente fora do tabuleiro — o que quebra a lógica de verificação de afundamento completamente.

Implementação prática

Se você quer construir isso em JavaScript, o caminho mais direto é usar uma classe Tabuleiro com duas matrizes 10x10: uma para navios e outra para tiros. Cada navio é um objeto com propriedades x, y, direcao e tamanho. O posicionamento pede validação de bordas e sobreposição. O disparo pede verificação de estado e atualizaçao de ambos os tabuleiros — o do atirador e o do alvo. Uma questão que apareceu num projeto meu recentemente e que demorei para resolver foi o caso de uma navegação com dimensões dinâmicas. Um usuário pediu um tabuleiro 12x12 com navios de tamanhos variados. O código original estava hardcoded para 10x10. A correção foi passar o tamanho do tabuleiro como parâmetro construtor e calcular o estado de vitória com base no total de cascos, não no total de células. Sem essa mudança, o jogo declarava vitória antecipadamente ou nunca declarava. Foi um erro bobo, mas daqueles que passam despercebidos porque o cenário padrão sempre funciona.

Para quem quer o código, ele não é complexo. A estrutura central gira em torno de três funções: posicionarNavio, atirar e verificarFimDeJogo. O resto é interface. Se o objetivo é apenas jogar, existem diversas implementações open source no GitHub que usam exatamente esse modelo de plano cartesiano. Uma busca por "battleship cartesian plane github" ou "batalha naval javascript plano cartesiano" traz resultados funcionais que você pode adaptar ou estudar. O plano cartesiano transforma um jogo de mesa em um problema de coordenadas tratável. A maior armadilha não é a matemática em si, mas a convenção de coordenadas invertida e a gestão de estados de disparo. Resolvidos esses dois pontos, o resto é lógica direta de arrays e condições.