E aí, pessoal! Recentemente, eu estava fuçando o Frontend Mentor e me deparei com um desafio sobre o "Connect Four", aquele joguinho clássico de tabuleiro que é super simples mas mega divertido. Essa curiosidade me levou a cair num buraco de coelho e, antes que percebesse, estava encarando um desafio no Codewars (Kata de lv 4) que me fez pensar bastante. Era tudo sobre a lógica do jogo, sem se preocupar com o visual por enquanto. Pensei comigo: "Por que não?", e decidi mergulhar de cabeça pra entender melhor como tudo funciona por trás das cortinas.
Entendendo o "Connect Four"
Pra quem não conhece, no "Connect Four" você tem que alinhar quatro peças da mesma cor em linha reta, seja pra cima, pra os lados ou na diagonal, sem mudar de direção. É um daqueles jogos que você joga sem ver o tempo passar, sabe?
Como Eu Encarei o Desafio
O Começo da Jornada
O Codewars me jogou um array cheio de jogadas dos dois jogadores, e minha missão era criar um tabuleiro com isso, colocar as peças nos lugares certos, ver se alguém ganhou e, se sim, quem foi. Parece fácil, mas só parece.
Criando o Tabuleiro
Logo no início, vi que fazer uma matriz era o caminho certo para o nosso jogo. Então, botei a mão na massa e montei uma matriz de 6 linhas por 7 colunas, deixando todas as posições inicialmente com uma string vazia. Era como preparar o palco antes do show começar, com cada espaço pronto para receber as jogadas.
const createGame = () => { const matrix = new Array(6).fill(''); for (let i = 0; i < matrix.length; i++) { matrix[i] = new Array(7).fill(''); } return matrix; };
Encaixando as peças
No jogo real, as peças caem até a posição mais baixa possível por causa da gravidade. Aqui, na nossa versão digital, a abordagem foi direta: eu só precisava checar em qual linha da coluna escolhida ainda não tinha uma peça. Dessa forma, quando uma nova peça era adicionada, ela automaticamente ocupava a posição mais baixa disponível, seguindo fielmente a regra do jogo. Simples e eficaz, garantindo que tudo funcionasse como se estivéssemos jogando com o tabuleiro físico na nossa frente.
piecesPositionList.map((pice) => { const { column, color } = translatePosition(pice); for (let row = 5; row >= 0; row--) { if (table[row][column] == '') { table[row][column] = color; break; } } const result = validateResult(table); if (result !== 'Draw' && finalResult === 'Draw') { finalResult = result; } });
Depois de colocar cada peça no seu lugar, eu dava uma pausa para verificar se já tínhamos um vencedor. Era como fazer uma checagem rápida após cada movimento, só para ter certeza de que não estávamos passando batido por um momento de glória.
Como calcular e quem Ganha?
Agora, chegamos à parte mais cabeça: decidir quem leva a melhor nesse jogo. Criei uma função que começava a varredura da esquerda para a direita, buscando qualquer sinal de um possível vencedor.
Primeiro, botei na cabeça algumas regras básicas:
- Eu ia sempre olhar de baixo para cima e da esquerda para a direita. Assim, não precisava me preocupar em checar as posições diretamente abaixo ou à esquerda — a diagonal esquerda era a única exceção.
- Minha meta era achar quatro peças em linha, começando pela que eu estava analisando. Contava ela mais três. Se aparecesse qualquer outra cor ou um espaço vazio, eu já cortava o barato do loop ali mesmo.
- Por último, eu calculava quantas peças dava para checar em cada direção. Nas três últimas colunas, nem me preocupava, pois não tinha como somar quatro. O mesmo valia para as posições muito altas ou à esquerda.
Se por acaso eu encontrasse alguém marcando quatro pontos, era jogo rápido: já declarava o vencedor na hora, sem nem continuar o resto das jogadas do histórico.
O código
O código não foi refatorado, mas creio que de para entender o passo a passo de como solucionar esse desafio
Coloquei uma função para conveguir ver visualmente o tabuleiro no console.log (showBoard)
Códiogo (spoiler)
const POSITIONS_BASE = ['A', 'B', 'C', 'D', 'E', 'F', 'G']; const POSITIONS_COORDINATES = { 'left-up': [-1, -1], up: [-1, 0], 'right-up': [-1, 1], right: [0, 1], }; function whoIsWinner(piecesPositionList) { const table = createGame(); let finalResult = 'Draw'; piecesPositionList.map((pice) => { const { column, color } = translatePosition(pice); for (let row = 5; row >= 0; row--) { if (table[row][column] == '') { table[row][column] = color; break; } } const result = validateResult(table); if (result !== 'Draw' && finalResult === 'Draw') { finalResult = result; } }); showBoard(table); return finalResult; } const createGame = () => { const matrix = new Array(6).fill(''); for (let i = 0; i < matrix.length; i++) { matrix[i] = new Array(7).fill(''); } return matrix; }; const translatePosition = (position) => { const [index, color] = position.split('_'); const POSITIONS_BASE = ['A', 'B', 'C', 'D', 'E', 'F', 'G']; return { column: POSITIONS_BASE.indexOf(index), color: color }; }; const checkNeighborhood = (table, row, col, direction) => { const color = table[row][col]; if (color === '') return 0; let count = 1; for (let i = 1; i < 4; i++) { newRow = POSITIONS_COORDINATES[direction][0] * i + row; newCol = POSITIONS_COORDINATES[direction][1] * i + col; if (table[newRow][newCol] === color) { count++; } else { break; } } return count; }; const validateResult = (table) => { for (let row = table.length - 1; row >= 0; row--) { for (let col = 0; col < table[row].length; col++) { if (row >= 3 && col >= 3) { if (checkNeighborhood(table, row, col, 'left-up') === 4) return table[row][col]; } if (row >= 3) { if (checkNeighborhood(table, row, col, 'up') === 4) return table[row][col]; } if (row >= 3 && col <= 3) { if (checkNeighborhood(table, row, col, 'right-up') === 4) return table[row][col]; } if (col <= 3) { if (checkNeighborhood(table, row, col, 'right') === 4) return table[row][col]; } } } return 'Draw'; }; const showBoard = (table) => { let string = ''; for (let row = 0; row < table.length; row++) { for (let col = 0; col < table[row].length; col++) { if (table[row][col] === '') { string = string + ' ⚫ ' + ''; } if (table[row][col] === 'Yellow') { string = string + ' 🟡 '; } if (table[row][col] === 'Red') { string = string + ' 🔴 '; } } string = string + '\n'; } console.log(string); };
O Que Eu Levei Disso Tudo
Não foi só um passatempo. Foi uma lição de casa que valeu por muitas. Me fez ver o quanto é importante dominar as bases da programação antes de sair colocando a mão na massa no visual.