O jogo da velha, também conhecido como “tic-tac-toe” em inglês, é um clássico que atravessa gerações. Sua simplicidade o torna perfeito para quem está aprendendo programação, especialmente quando se trata de criar jogos interativos para web.
Neste tutorial, vamos mergulhar no mundo do desenvolvimento web criando nosso próprio jogo da velha usando JavaScript, HTML e CSS. Você aprenderá como estruturar o tabuleiro, estilizá-lo para uma aparência atraente e implementar toda a lógica do jogo usando JavaScript.
Ao final deste tutorial, você terá:
- Criado um jogo da velha totalmente funcional
- Praticado habilidades essenciais de HTML, CSS e JavaScript
- Aprendido sobre manipulação do DOM e lógica de programação
- Ganhado experiência em criar aplicações web interativas
Aqui uma demonstração do código em ação no Codepen para você ver como fica.
See the Pen Jogo da Velha – Código Fácil by Código Fácil (@CodigoFacil) on CodePen.
Então, prepare seu editor de código favorito e vamos começar o projeto!
Pré-requisitos
Antes de mergulharmos no código, certifique-se de que você tem:
- Conhecimentos básicos de:
- HTML: para estruturar o tabuleiro do jogo
- CSS: para estilizar e dar vida ao nosso jogo
- JavaScript: para implementar a lógica e interatividade
- Ferramentas necessárias:
- Um editor de código (como Visual Studio Code, Sublime Text, ou Atom)
- Um navegador web moderno (como Chrome, Firefox, ou Edge)
- Compreensão básica de:
- Manipulação do DOM com JavaScript
- Funções e eventos em JavaScript
Não se preocupe se você não se sentir 100% confiante com todos esses conceitos. Este tutorial é projetado para ser acessível a iniciantes, e explicaremos cada passo do caminho.
Estrutura HTML
Vamos começar criando a estrutura básica do nosso jogo da velha usando HTML. Esta estrutura servirá como o esqueleto do nosso jogo, sobre o qual adicionaremos estilos e funcionalidades.
Crie um novo arquivo chamado index.html
e adicione o seguinte código:
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jogo da Velha em JavaScript</title>
<link rel="stylesheet" href="estilo.css">
</head>
<body>
<div class="jogo">
<h1>Jogo da Velha</h1>
<div class="tabuleiro" id="tabuleiro">
<div class="celula" data-celula></div>
<div class="celula" data-celula></div>
<div class="celula" data-celula></div>
<div class="celula" data-celula></div>
<div class="celula" data-celula></div>
<div class="celula" data-celula></div>
<div class="celula" data-celula></div>
<div class="celula" data-celula></div>
<div class="celula" data-celula></div>
</div>
<p id="status"></p>
<button id="reiniciar">Reiniciar</button>
</div>
<script src="script.js"></script>
</body>
</html>
Vamos analisar os elementos principais desta estrutura:
- Metadados: Definimos o idioma como português do Brasil, especificamos a codificação UTF-8 e configuramos a viewport para responsividade.
- Título: “Jogo da Velha em JavaScript” aparecerá na aba do navegador.
- Link para o CSS: Conectamos nosso arquivo de estilos (que criaremos em breve).
- Estrutura do Jogo:
- Um
<div>
com classe “jogo” envolve todo o conteúdo do jogo. - Um
<h1>
para o título do jogo. - O tabuleiro é representado por um
<div>
com classe “tabuleiro”. - Nove
<div>
s com classe “celula” representam as células do tabuleiro. - Um
<p>
com id “status” para exibir mensagens do jogo. - Um
<button>
para reiniciar o jogo.
5. Link para o JavaScript: No final do body, linkamos nosso arquivo de script.
Cada célula do tabuleiro tem um atributo data-celula
. Este é um atributo de dados personalizado que usaremos mais tarde em nosso JavaScript para interagir com as células.
Esta estrutura HTML fornece uma base sólida para nosso jogo. Na próxima seção, daremos vida a esta estrutura com CSS.
Estilização com CSS
Agora que temos nossa estrutura HTML, vamos dar-lhe uma aparência atraente com CSS. Criaremos um arquivo chamado estilo.css
e o preencheremos com os seguintes estilos:
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
.jogo {
text-align: center;
}
.tabuleiro {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-gap: 5px;
margin-top: 20px;
}
.celula {
width: 100px;
height: 100px;
background-color: #fff;
border: 2px solid #333;
font-size: 2em;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: background-color 0.3s ease;
}
.celula:hover {
background-color: #eee;
}
.celula.vencedor {
background-color: #aaffaa;
}
button {
margin-top: 20px;
padding: 10px 20px;
font-size: 1em;
cursor: pointer;
}
#status {
margin-top: 20px;
font-size: 1.2em;
font-weight: bold;
}
Vamos analisar os principais aspectos deste CSS:
Estilo do Body:
- Usamos flexbox para centralizar o conteúdo verticalmente e horizontalmente.
- Definimos a altura como 100vh para ocupar toda a altura da viewport.
- Aplicamos um fundo cinza claro.
Estilo do Tabuleiro:
- Utilizamos CSS Grid para criar um layout 3×3 para o tabuleiro.
- Cada célula tem 100px de largura e altura.
Estilo das Células:
- Aplicamos uma borda e um fundo branco para cada célula.
- Usamos flexbox para centralizar o conteúdo (X ou O) dentro da célula.
- Adicionamos um efeito hover para feedback visual.
- A classe ‘vencedor’ mudará o fundo para verde claro quando aplicada.
Estilo do Botão de Reinício:
- Adicionamos margem superior e padding para melhor aparência e usabilidade.
Estilo do Status:
- Aumentamos o tamanho da fonte e aplicamos negrito para destaque.
Estes estilos criam um layout limpo e responsivo para nosso jogo da velha. O tabuleiro fica centralizado na página, as células são facilmente clicáveis, e temos espaço para exibir o status do jogo e o botão de reinício.
Na próxima seção, vamos adicionar a lógica do jogo com JavaScript para tornar nosso jogo da velha interativo e funcional.
Lógica do Jogo em JavaScript
Agora que temos a estrutura e o estilo do nosso jogo, vamos adicionar a interatividade e a lógica do jogo usando JavaScript. Criaremos um arquivo chamado script.js
e o preencheremos com o seguinte código:
// Selecionando elementos do DOM
const tabuleiro = document.getElementById('tabuleiro');
const celulas = document.querySelectorAll('[data-celula]');
const status = document.getElementById('status');
const botaoReiniciar = document.getElementById('reiniciar');
// Variáveis de controle do jogo
let jogadorAtual = 'X';
let jogoAtivo = true;
// Combinações vencedoras possíveis
const combinacoesVencedoras = [
[0, 1, 2], [3, 4, 5], [6, 7, 8], // Linhas
[0, 3, 6], [1, 4, 7], [2, 5, 8], // Colunas
[0, 4, 8], [2, 4, 6] // Diagonais
];
// Função para lidar com o clique em uma célula
function lidarComCliqueCelula(e) {
const celula = e.target;
const indiceCelula = Array.from(celulas).indexOf(celula);
// Verifica se a célula já está preenchida ou se o jogo acabou
if (celula.textContent !== '' || !jogoAtivo) return;
// Preenche a célula com o símbolo do jogador atual
celula.textContent = jogadorAtual;
if (verificarVitoria()) {
const comboVencedor = obterComboVencedor();
destacarCelulasVencedoras(comboVencedor);
status.textContent = `Jogador ${jogadorAtual} venceu!`;
jogoAtivo = false;
} else if (verificarEmpate()) {
status.textContent = 'Empate!';
jogoAtivo = false;
} else {
// Troca o jogador atual
jogadorAtual = jogadorAtual === 'X' ? 'O' : 'X';
status.textContent = `Vez do jogador ${jogadorAtual}`;
}
}
// Função para verificar se houve vitória
function verificarVitoria() {
return combinacoesVencedoras.some(combinacao => {
return combinacao.every(indice => {
return celulas[indice].textContent === jogadorAtual;
});
});
}
// Função para obter a combinação vencedora
function obterComboVencedor() {
return combinacoesVencedoras.find(combinacao => {
return combinacao.every(indice => {
return celulas[indice].textContent === jogadorAtual;
});
});
}
// Função para destacar as células vencedoras
function destacarCelulasVencedoras(combo) {
combo.forEach(indice => {
celulas[indice].classList.add('vencedor');
});
}
// Função para verificar se houve empate
function verificarEmpate() {
return Array.from(celulas).every(celula => celula.textContent !== '');
}
// Função para reiniciar o jogo
function reiniciarJogo() {
jogadorAtual = 'X';
jogoAtivo = true;
celulas.forEach(celula => {
celula.textContent = '';
celula.classList.remove('vencedor');
});
status.textContent = `Vez do jogador ${jogadorAtual}`;
}
// Adicionando event listeners
celulas.forEach(celula => celula.addEventListener('click', lidarComCliqueCelula));
botaoReiniciar.addEventListener('click', reiniciarJogo);
// Inicialização do status do jogo
status.textContent = `Vez do jogador ${jogadorAtual}`;
Vamos analisar as principais partes deste código:
- Seleção de Elementos: Usamos
document.getElementById()
edocument.querySelectorAll()
para selecionar os elementos do DOM que vamos manipular. - Variáveis de Controle:
jogadorAtual
ejogoAtivo
controlam o estado do jogo. - Combinações Vencedoras: Definimos todas as possíveis combinações de vitória no jogo.
- Função
lidarComCliqueCelula
: Esta é a função principal que gerencia o fluxo do jogo quando uma célula é clicada. - Funções de Verificação:
verificarVitoria()
everificarEmpate()
checam o estado do jogo após cada jogada. - Função
reiniciarJogo
: Reseta o tabuleiro e as variáveis de controle para um novo jogo. - Event Listeners: Adicionamos listeners para os cliques nas células e no botão de reiniciar.
Este código implementa toda a lógica necessária para um jogo da velha funcional. Na próxima seção, explicaremos em detalhes como cada parte do código funciona.
Explicação do Código
Vamos analisar em detalhes as partes mais importantes do nosso código JavaScript:
1. Seleção de Elementos e Variáveis de Controle
const tabuleiro = document.getElementById('tabuleiro');
const celulas = document.querySelectorAll('[data-celula]');
const status = document.getElementById('status');
const botaoReiniciar = document.getElementById('reiniciar');
let jogadorAtual = 'X';
let jogoAtivo = true;
Aqui, usamos métodos do DOM para selecionar os elementos HTML que vamos manipular. querySelectorAll('[data-celula]')
seleciona todas as células do tabuleiro usando o atributo personalizado que definimos no HTML.
As variáveis jogadorAtual
e jogoAtivo
controlam o estado do jogo. Usamos let
porque esses valores serão alterados durante o jogo.
2. Combinações Vencedoras
const combinacoesVencedoras = [
[0, 1, 2], [3, 4, 5], [6, 7, 8], // Linhas
[0, 3, 6], [1, 4, 7], [2, 5, 8], // Colunas
[0, 4, 8], [2, 4, 6] // Diagonais
];
Este array contém todas as combinações possíveis de vitória no jogo da velha. Cada sub-array representa os índices das células que, quando preenchidas pelo mesmo jogador, resultam em vitória.
3. Função lidarComCliqueCelula
function lidarComCliqueCelula(e) {
const celula = e.target;
const indiceCelula = Array.from(celulas).indexOf(celula);
if (celula.textContent !== '' || !jogoAtivo) return;
celula.textContent = jogadorAtual;
if (verificarVitoria()) {
const comboVencedor = obterComboVencedor();
destacarCelulasVencedoras(comboVencedor);
status.textContent = `Jogador ${jogadorAtual} venceu!`;
jogoAtivo = false;
} else if (verificarEmpate()) {
status.textContent = 'Empate!';
jogoAtivo = false;
} else {
jogadorAtual = jogadorAtual === 'X' ? 'O' : 'X';
status.textContent = `Vez do jogador ${jogadorAtual}`;
}
}
Esta função é o coração do nosso jogo. Ela é chamada cada vez que uma célula é clicada. Vamos analisar seu funcionamento:
- Primeiro, verificamos se a célula já está preenchida ou se o jogo já acabou.
- Se não, preenchemos a célula com o símbolo do jogador atual.
- Em seguida, verificamos se houve uma vitória ou empate.
- Se não houver vitória nem empate, trocamos o jogador atual.
4. Funções de Verificação
function verificarVitoria() {
return combinacoesVencedoras.some(combinacao => {
return combinacao.every(indice => {
return celulas[indice].textContent === jogadorAtual;
});
});
}
function verificarEmpate() {
return Array.from(celulas).every(celula => celula.textContent !== '');
}
verificarVitoria()
usa os métodos some()
e every()
para checar se alguma das combinações vencedoras foi alcançada pelo jogador atual.
verificarEmpate()
checa se todas as células estão preenchidas, indicando um empate se não houver vitória.
5. Função reiniciarJogo
function reiniciarJogo() {
jogadorAtual = 'X';
jogoAtivo = true;
celulas.forEach(celula => {
celula.textContent = '';
celula.classList.remove('vencedor');
});
status.textContent = `Vez do jogador ${jogadorAtual}`;
}
Esta função reseta o jogo para seu estado inicial, limpando o tabuleiro e redefinindo as variáveis de controle.
6. Event Listeners
celulas.forEach(celula => celula.addEventListener('click', lidarComCliqueCelula));
botaoReiniciar.addEventListener('click', reiniciarJogo);
Aqui, adicionamos os event listeners necessários para que o jogo responda aos cliques do usuário.
Testando o Jogo
Agora que temos todo o código necessário para o nosso jogo da velha, vamos testar para garantir que tudo está funcionando corretamente.
Preparação
Certifique-se de que você tem três arquivos em seu diretório de trabalho:
index.html
estilo.css
script.js
Verifique se os arquivos CSS e JavaScript estão corretamente linkados no seu arquivo HTML.
Executando o Jogo
- Abra o arquivo
index.html
em seu navegador preferido. Você pode fazer isso de duas maneiras:
- Dê um duplo clique no arquivo
index.html
no seu explorador de arquivos. - Arraste o arquivo
index.html
para uma janela aberta do navegador.
- Você deverá ver o tabuleiro do jogo da velha no centro da página, com um título acima, um espaço para status abaixo e um botão de reiniciar.
Testando as Funcionalidades
Vamos testar cada aspecto do jogo para garantir que tudo está funcionando como esperado:
Jogabilidade Básica:
- Clique em diferentes células e verifique se os símbolos ‘X’ e ‘O’ aparecem alternadamente.
- Observe se a mensagem de status muda corretamente, indicando de quem é a vez.
Verificação de Vitória:
- Jogue até conseguir uma vitória (três símbolos iguais em linha, coluna ou diagonal).
- Verifique se o jogo reconhece a vitória, destaca as células vencedoras e exibe a mensagem correta.
Verificação de Empate:
- Jogue até preencher todas as células sem que haja um vencedor.
- Confirme se o jogo reconhece o empate e exibe a mensagem apropriada.
Reiniciar o Jogo:
- Após uma vitória ou empate, clique no botão “Reiniciar”.
- Verifique se o tabuleiro é limpo, as células vencedoras são desmarcadas (se houver) e o jogo está pronto para uma nova partida.
Responsividade:
- Teste o jogo em diferentes tamanhos de tela (você pode usar as ferramentas de desenvolvedor do seu navegador para simular dispositivos móveis).
- Verifique se o layout se ajusta adequadamente e permanece jogável em telas menores.
Resolução de Problemas
Se você encontrar algum problema durante o teste:
- Use as ferramentas de desenvolvedor do seu navegador (geralmente acessíveis pressionando F12) para verificar se há erros no console.
- Revise o código em busca de erros de digitação ou syntax errors.
- Verifique se todos os seletores no JavaScript correspondem aos IDs e classes no HTML.
Lembre-se, testar exaustivamente é crucial para garantir uma boa experiência do usuário. Não hesite em jogar várias partidas para ter certeza de que todas as funcionalidades estão operando corretamente em diferentes cenários.
Sugestões de Melhorias
Agora que temos um jogo da velha funcional, podemos pensar em maneiras de melhorá-lo e expandir suas funcionalidades. Aqui estão algumas ideias que você pode implementar para tornar o jogo ainda mais interessante:
Placar:
- Adicione um sistema de pontuação que mantenha o registro de vitórias para cada jogador ao longo de várias partidas.
- Implemente um armazenamento local para manter o placar mesmo após o fechamento do navegador.
Escolha de Símbolos:
- Permita que os jogadores escolham seus próprios símbolos em vez de usar apenas ‘X’ e ‘O’.
- Você pode usar emojis ou até mesmo permitir que os jogadores insiram suas iniciais.
Modo Contra o Computador:
- Implemente uma IA simples para que um jogador possa jogar contra o computador.
- Para um desafio maior, você pode criar diferentes níveis de dificuldade para a IA.
Animações:
- Adicione animações suaves quando os símbolos são colocados no tabuleiro.
- Implemente uma animação para destacar a linha vencedora.
Temas Personalizáveis:
- Permita que os usuários escolham entre diferentes esquemas de cores ou temas para o jogo.
- Você pode implementar um modo escuro/claro, por exemplo.
Tamanho do Tabuleiro Ajustável:
- Permita que os jogadores escolham jogar em tabuleiros maiores, como 4×4 ou 5×5.
- Isso exigiria ajustar a lógica de vitória e o layout do tabuleiro.
Multiplayer Online:
- Para um desafio maior, implemente um sistema de jogo online onde dois jogadores podem jogar remotamente.
- Isso exigiria o uso de tecnologias de back-end e tempo real, como WebSockets.
Histórico de Movimentos:
- Adicione um recurso que permita aos jogadores ver o histórico de movimentos da partida atual.
- Implemente um botão de “desfazer” para voltar atrás em um movimento.
Efeitos Sonoros:
- Adicione sons para quando um jogador faz uma jogada, quando há uma vitória ou um empate.
- Permita que os usuários liguem/desliguem os efeitos sonoros.
Responsividade Aprimorada:
- Melhore a experiência em dispositivos móveis, talvez implementando gestos de toque para interações.
- Otimize o layout para diferentes orientações de tela em dispositivos móveis.
Implementar estas melhorias não só tornará o jogo mais divertido e envolvente, mas também proporcionará excelentes oportunidades de aprendizado em várias áreas do desenvolvimento web front-end e, potencialmente, back-end.
Lembre-se de abordar cada melhoria como um mini-projeto, planejando cuidadosamente como ela se integrará ao código existente e testando exaustivamente após a implementação.
Conclusão
Parabéns! Você acaba de criar um jogo da velha funcional usando HTML, CSS e JavaScript. Este projeto, embora simples, abrange vários conceitos fundamentais do desenvolvimento web front-end:
- HTML: Utilizamos HTML para estruturar o conteúdo do nosso jogo, criando o tabuleiro e os elementos de interface.
- CSS: Aplicamos estilos para dar ao jogo uma aparência atraente e responsiva, utilizando flexbox e grid para um layout eficiente.
- JavaScript: Implementamos toda a lógica do jogo, incluindo:
- Manipulação do DOM para interagir com os elementos HTML
- Uso de eventos para responder às ações do usuário
- Lógica condicional para determinar o estado do jogo (vitória, empate)
- Funções para gerenciar diferentes aspectos do jogo
- Lógica de Programação: Desenvolvemos algoritmos para verificar condições de vitória e empate, demonstrando habilidades de resolução de problemas.
- Boas Práticas de Codificação: Organizamos nosso código de forma modular e utilizamos nomes descritivos para variáveis e funções, melhorando a legibilidade e manutenção.
Este projeto serve como uma excelente base para explorar conceitos mais avançados de desenvolvimento web. As sugestões de melhorias oferecidas na seção anterior podem ser usadas como pontos de partida para expandir suas habilidades e conhecimentos.
Próximos Passos
- Experimente as Melhorias: Tente implementar algumas das sugestões de melhorias mencionadas. Cada uma delas oferece oportunidades únicas de aprendizado.
- Refine o Código: Revise o código e veja se há áreas que podem ser otimizadas ou refatoradas para maior eficiência ou legibilidade.
- Explore Frameworks: Considere recriar o jogo usando frameworks populares como React, Vue.js ou Angular para entender como eles podem simplificar o desenvolvimento de aplicações web.
- Compartilhe seu Trabalho: Publique seu jogo online e compartilhe com amigos ou na comunidade de desenvolvedores. Receber feedback é uma ótima maneira de melhorar suas habilidades.
- Continue Aprendendo: Use o conhecimento adquirido neste projeto como um trampolim para projetos mais complexos e continue explorando o vasto mundo do desenvolvimento web.
Lembre-se, a prática constante é a chave para se tornar um desenvolvedor habilidoso. Continue codificando, experimentando e, acima de tudo, divirta-se no processo!
Espero que este tutorial tenha sido informativo e inspirador. Boa sorte em suas futuras aventuras de programação!