Desenvolvendo um Jogo da Velha / Parte 3
Vamos finalizar nossa série de artigos para exercitar programação com a última parte do nosso Jogo da Velha.
Finalmente chegamos na terceira parte do desenvolvimento do nosso jogo da velha.
Na primeira parte nós aprendemos sobre matrizes, como percorrer essa matriz com laços de repetição aninhados e desenhamos o nosso tabuleiro, já na segunda parte vimos como utilizar a API do Javascript para melhorar o nosso tabuleiro com botões, adicionamos eventos para esses botões para fazer as jogadas utilizamos um pouquinho dos objetos literais do Javascript.
Nessa terceira parte iremos focar em implementar a lógica de gameover e deixar nosso tabuleiro um pouco mais bonito com CSS
.
Deixando nosso tabuleiro mais bonito
Para deixar nosso tabuleiro mais bonito vamos utilizar CSS
. O CSS
(Cascading Style Sheet), Folha de Estilo em Cascata, é uma linguagem de marcação que modifica a aparencia dos elementos do navegador.
Para adicionar estilo de CSS
para o nosso documento precisamos adicionar a tag <style></style>
, e dentro dessa tag iremos adicionar as regras do CSS
.
Vamos criar uma regra para que os botões, button
, do tabuleiro fiquem com a largura, width
, e altura, height
, de 30 pixels, 30px
:
button {
width: 30px;
height: 30px;
}
Essa regra diz que TODOS os botões terão largura e altura de 30 pixels. Estamos selecionando pelo elemento button
, então todo elemento button
aplicará essa regra.
As regras do CSS
são compostas por um seletor
, que indica quais elementos serão afetados pela regra seguido pela abertura de {}
, que é onde adicionamos as propriedades que serão modificados do elemento. As regras do CSS
podem e vão se sobrescrever dependendo da especificidade do seletor
. Se quisermos que outro button
tenha um tamanho diferente teremos que criar uma regra com um seletor
mais especifico.
Para evitar essa necessidade de utilizar seletores mais específicos para nossas regras de CSS
vamos utilizar o seguinte seletor
:
.botaoTabuleiro {
width: 30px;
height: 30px;
}
Chamamos isso de seletor de classe
pois ele é aplicado para todos os elementos do HTML que possuírem o nome botaoTabuleiro
no atributo class
. Repare que utilizamos um .
(ponto) antes da palavra. Esse .
indica que estamos selecionando uma classe com aquele nome para aplicar a regra. Nesse caso apenas o elementos <button class="botaoTabuleiro"></button>
terão suas propriedades visuais modificadas.
Mas estamos criando esses botões dinamicamente. Como podemos adicionar essas classes dinamicamente?
É simples. Lembra como criamos os botões do nosso tabuleiro? Só precisamos ir até a função criaBotaoTabuleiro(linha, coluna)
e utilizar o comando button.classList.add("botaoTabuleiro")
:
var criaBotaoTabuleiro = function(linha, coluna){
var button = document.createElement("button");
button.textContent = tabuleiro[linha][coluna].simbolo;
button.classList.add("botaoTabuleiro");//Adiciona a classe
button.onclick = function eventoFazerJogada(){
fazerJogada(linha, coluna)
};
document.body.appendChild(button);
}
Dessa forma iremos criar os botões com a classe botaoTabuleiro
para que a nosso seletor
da nossa regra do CSS
possa encontra-los e modificar suas propriedades visuais.
A propriedade
classList
existe em todos os elementos HTML e só pode ser lida. Para adicionar, remover ou alternar seu conteúdo precisamos utilizar suas funçõesadd()
,remove()
etoggle()
passando o nome da classe como parametro. Fonte: Mozzila Developer Network - Element.classList
Mas ainda precisamos melhorar mais um pouquinho a aparência do nosso tabuleiro. Podemos ao menos dar um jeito de centraliza-lo na tela e substituir o alert()
de quando o jogador faz uma jogada inválida por um elemento na tela.
body {
display: flex;
justify-content: center;
align-items: center;
}
A propriedade display
indica o tipo de caixa que iremos utilizar, que nesse caso será flex
. Flex é propriedade que torna a div
em uma caixa flexível que foi tornando mais fácil o posicionamento dos elementos dentro dela. A propriedade justify-content
é uma propriedade especifica para a caixa do tipo flexível, display: flex
, qual será a posição do conteúdo, que nesse caso será no centro horizontalmente, center
.
Para saber mais sobre Flexbox: Usando CSS flexible boxes (Caixas Flexíveis)
Mas podemos ver que tivemos um problema com a nossa regra:
As caixas flexíveis (flexbox) colocam os elementos um do lado do outro, incluindo o <br>
. Para resolver isso vamos fazer alguns ajustes criando duas div
, uma dentro da outra, dentro do body
:
<body onload="desenharTabuleiro()">
<div id="principal">
<div id="tabuleiro"></div>
</div>
</body>
Adicionamos também o atributo id
em ambas para que possamos identifica-las.
Então vamos apenas mudar o seletor
e a regra de:
body {
display: flex;
justify-content: center;
}
Para:
#principal {
display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
}
As propriedades width
e height
com o valor 100%
indica que terão o tamanho total do elemento pai, que é body
. Já a propriedade align-items
serve para centralizar os elementos verticalmente de acordo com a caixa flexível.
Não existe muito problema em modificar as propriedades visuais do body
, mas assim fica mais fácil de mudar o nosso HTML. Se tivéssemos modificado diretamente o body
e depois fosse necessário adicionar um novo elemento dentro dele só que sem centraliza-lo, justify-content: center
, teríamos que pensar um pouco em como resolver.
Agora para continuarmos vamos precisar alterar uma coisinha nas funções: novaLinha()
, limparTela()
e desenharTabuleiro()
.
Nessas funções estamos acessando e modificando o document.body
, que representa nosso <body></body>
no HTML, diretamente, adicionando e apagando diretamente todos os seus elementos, mas agora que criamos mais elementos dentro do <body></body>
no nosso arquivo HTML nós precisamos utiliza-los da forma correta.
var novaLinha = function() {
var br = document.createElement("br");
document.querySelector("#tabuleiro").appendChild(br);
}
var limparTela = function() {
document.querySelector("#tabuleiro").innerHTML = '';
}
var criaBotaoTabuleiro = function(linha, coluna){
var button = document.createElement("button");
button.textContent = tabuleiro[linha][coluna].simbolo;
button.classList.add("botaoTabuleiro");
button.onclick = function eventoFazerJogada(){
fazerJogada(linha, coluna)
};
document.querySelector("#tabuleiro").appendChild(button);
}
Substituimos todos os document.body
por document.querySelector("#tabuleiro")
. A função querySelector()
utiliza a mesma lógica dos seletores do CSS
para devolver o primeiro elemento encontrado. Repare que o seletor
utilizado foi #tabuleiro
com o prefixo #
que indica que é um id
.
A funcionalidade do nosso jogo continua a mesma, mas ao invés de modificar o conteúdo do body
, nós vamos modificar o conteúdo da nova div
que criamos com o id="tabuleiro"
.
Agora para anunciar as jogada inválidas vamos criar mais uma div
com o id="status"
no documento HTML dentro da div
com id="principal"
:
<body onload="desenharTabuleiro()">
<div id="principal">
<div id="tabuleiro"></div>
<div id="status"></div>
</div>
</body>
E agora na função fazerJogada(linha, coluna)
dentro do else
vamos buscar pelo elemento com o id="status"
:
else {
document.querySelector("#status").innerHTML = 'Jogada Inválida!';
}
Depois de buscar pelo elemento nós apenas atribuímos a mensagem para seu conteúdo.
Para manter a facilidade de leitura do código, vamos extrair esse trecho para uma função chamada alterarStatus
e passar por parâmetro a mensagem que será exibida:
var alterarStatus = function(mensagem) {
document.querySelector("#status").innerHTML = mensagem;
}
var fazerJogada = function(linha, coluna){
if(tabuleiro[linha][coluna] === VAZIO){
tabuleiro[linha][coluna] = jogadorAtual;
desenharTabuleiro();
proximoJogador();
}
else {
alterarStatus("Jogada Inválida!");
}
}
Mas ainda temos um probleminha.
Não queremos que o texto apareça do lado do tabuleiro, mas abaixo dele.
Para resolver isso só precisamos mudar a direção do eixo dos elementos dentro de #principal
utilizando a propriedade flex-direction: column
.
Isso fará com que todos os elementos dentro de #principal
fiquem na vertical.
Para separar um pouco o texto do tabuleiro vamos adicionar uma margem no topo e uma altura fixa para o #status
:
#status {
margin-top: 20px;
height: 20px;
}
A altura fixa será útil para que o tabuleiro não saia do lugar ao ser adicionado uma mensagem.
E agora temos um resultado muito melhor.
Mas ainda falta uma coisinha. Caso o jogador faça uma jogada válida nós precisamos apagar essa mensagem do #status
. A função limparTela()
é chamada sempre que desenhamos o tabuleiro e nós só desenhamos os tabuleiro quando fazemos uma jogada válida, então vamos apenas modificar ela para também limpar o #status
.
var limparTela = function() {
document.querySelector("#tabuleiro").innerHTML = '';
document.querySelector("#status").innerHTML = '';
}
Temos um resultado MUITO melhor. :)
Lógica de Vitória - Game Over
Para a lógica de vitória vamos utilizar uma validação diferente de comparar:
if(tabuleiro[0][0].simbolo === jogadorAtual.simbolo && tabuleiro[0][1].simbolo === jogadorAtual.simbolo && tabuleiro[0][2].simbolo === jogadorAtual.simbolo)
Isso é um pouco chato e estranho de ler. Vamos tentar algo diferente.
Vamos adicionar a propriedade valor
para os objetos VAZIO
, jogadorX
e jogadorO
e atribuir 0
, 1
e -1
para eles.
var VAZIO = {
simbolo:"",
valor:0
}
var jogadorX= {
simbolo:"X",
valor:1
}
var jogadorO= {
simbolo:"",
valor:-1
}
E agora para fazer a validação vamos criar uma função chamada checarTrilha
recebendo como parâmetro um array com 3 posições do tabuleiro:
var checarTrilha = function(trilha) {
if(trilha[0].valor + trilha[1].valor + trilha[2].valor === jogadorAtual.valor * 3) {
return true;
}
return false;
}
A verificação que fazemos é bem simples. Nós verificamos se a soma do valor
das trilhas é igual ao jogadorAtual
vezes três.
Para fazer a verificação vamos começar criando uma nova função chamada verificarVencedor
.
var verificarVencedor = function() {
}
E agora vamos começar verificando as linhas:
var verificarVencedor = function() {
if(checarTrilha(tabuleiro[0]) || checarTrilha(tabuleiro[1]) || checarTrilha(tabuleiro[2])) {
alterarStatus("Jogador " + jogadorAtual.simbolo + " venceu!");
}
}
Para verificarmos as linhas é bem simples. As linhas já estão no formato esperado pela função checarTrilha(trilha)
, então só precisamos passar qual [linha]
queremos checar. Se qualquer uma das funções na condicional retornar true
nós podemos chamar a função alterarStatus(mensagem)
para exibir a mensagem que o jogador venceu.
Mas teremos uma complexidade um pouco maior para verificar as trilhas das colunas e diagonais. Como eu disse antes, as linhas já estão na forma esperada pela função checarTrilha(trilha)
. Por esse motivo vamos extrair a checagem das linhas para uma nova função chamada verificarLinhas
para manter as verificações separadas da função verificarVencedor()
.
var verificarLinhas = function() {
if(checarTrilha(tabuleiro[0]) || checarTrilha(tabuleiro[1]) || checarTrilha(tabuleiro[2])) {
return true;
}
return false;
}
var verificarVencedor = function() {
if(verificarLinhas()) {
alterarStatus("Jogador " + jogadorAtual.simbolo + " venceu!");
}
}
Agora podemos seguir para a verificação das colunas.
Para verificar as trilhas das colunas podemos criar um novo array e atribuir cada uma das posições para esse novo array, como nesse exemplo:
var verificarColunas = function() {
var arrayColunaUm = [
tabuleiro[0][0],
tabuleiro[1][0],
tabuleiro[2][0]
];
var arrayColunaDois = [
tabuleiro[0][1],
tabuleiro[1][1],
tabuleiro[2][1]
];
var arrayColunaTres = [
tabuleiro[0][2],
tabuleiro[1][2],
tabuleiro[2][2]
];
if(checarTrilha(arrayColunaUm) || checarTrilha(arrayColunaDois) || checarTrilha(arrayColunaTres)) {
return true;
}
return false;
}
E para as diagonais vamos fazer o mesmo.
var verificarDiagonais = function() {
var arrayDiagonalPrincipal = [
tabuleiro[0][0],
tabuleiro[1][1],
tabuleiro[2][2]];
var arrayDiagonalSecundaria = [
tabuleiro[0][2],
tabuleiro[1][1],
tabuleiro[2][0]];
if(checarTrilha(arrayDiagonalPrincipal)
|| checarTrilha(arrayDiagonalSecundaria)) {
return true;
}
return false;
}
E na nossa função verificarVencedor()
ficará assim:
var verificarVencedor = function() {
if(verificarLinhas() || verificarColunas() || verificarDiagonais()) {
alterarStatus("Jogador " + jogadorAtual.simbolo + " venceu!");
}
}
Mas ainda precisamos verificar se houve um empate. No jogo da velha um empate acontece quando não existem mais espaços para efetuar uma jogada e nenhum dos jogadores venceu:
var numeroDeJogadas = 0;
var verificarEmpate = function() {
if(numeroDeJogadas === 9) {
return true;
}
return false;
}
Aqui criamos uma variável chamada numeroDeJogadas
com o valor 0
e uma função chamada verificarEmpate()
que checa se o numeroDeJogadas
é igual à 9, que equivale às 9 posições possíveis do tabuleiro.
Agora na nossa função verificarVencedor()
só precisamos adicionar um else if
, senão se, chamando a função verificarEmpate()
e caso verdadeiro, alteramos a mensagem do #status
:
var verificarVencedor = function() {
if(verificarLinhas() || verificarColunas() || verificarDiagonais()) {
alterarStatus("Jogador " + jogadorAtual.simbolo + " venceu!");
}
else if(verificarEmpate()) {
alterarStatus("Empate!");
}
}
Agora podemos chamar verificarVencedor()
na função fazerJogada(linha, coluna)
em qualquer lugar antes de chamar proximoJogador()
. Também devemos colocar o incremento do numeroDeJogadas
antes de verificarVencedor()
:
var fazerJogada = function(linha, coluna){
if(tabuleiro[linha][coluna] === VAZIO){
tabuleiro[linha][coluna] = jogadorAtual;
desenharTabuleiro();
numeroDeJogadas++;
verificarVencedor();
proximoJogador();
}
else {
alterarStatus("Jogada Inválida!");
}
}
Conclusão
Com isso concluímos mais uma parte do nosso jogo da velha. E nesta parte vimos como utilizar uma parte bem superficial do CSS e do flexbox, aprendemos a utilizar o document.querySelector()
para buscar o primeiro elemento de um determinado seletor
, como o #status
ou .botaoTabuleiro
, do CSS e também criamos uma lógica de vitória (gameover) utilizando a soma dos valor
em cada uma das posições.
Tem uma ideia diferente de como fazer a lógica de vitória ou alguma sugestão de como incrementar nosso jogo da velha?? Comente aqui embaixo com sua sugestão. E se tiver interesse em aprender mais sobre Lógica de Programação, Programação Orientada a Objetos ou Desenvolvimento Web, acesse o site da Triadworks e dá uma conferida nos cursos disponiveis. :)
You might also be interested in these articles...
Desenvolvedor na TriadWorks - Email
Posted in: canvascodigocurriculocursoensinofortalezahtmlhtml5javajavascriptlogica de programacaológicoooraciociniotreinamentotriadworksweb