aguarde...

23 de novembro de 2021

Crie o Jogo da Velha simples usando HTML, CSS, JavaScript

Crie o Jogo da Velha simples usando HTML, CSS, JavaScript

Criar um jogo com javascript é a maneira mais divertida de aprender. Isso o manterá motivado e isso é crucial para aprender habilidades complexas, como desenvolvimento web. Além disso, você pode jogar com seus amigos ou apenas mostrar a eles o que criou e eles ficarão maravilhados. Na postagem de hoje, criaremos um jogo jogo da velha usando apenas HTML, CSS e Javascript.

Implementando o HTML

Primeiro, na seção principal, incluirei nossos arquivos css e javascript que criaremos mais tarde. Também adicionarei uma fonte do Google chamada Itim, que acho que se encaixa perfeitamente neste jogo.

    <link rel="stylesheet" href="style.css">
    <link rel="preconnect" href="https://fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css2?family=Itim&display=swap" rel="stylesheet">
    <script src="./index.js"></script>

O corpo do HTML será bastante simples. Para embrulhar tudo, usarei uma tag principal e aplicarei uma classe de backgrounda ela. Dentro do maininvólucro, teremos cinco seções.

A primeira seção conterá apenas nosso título dentro de a h1.

A segunda seção exibirá de quem é a vez no momento. Dentro da tela, temos um intervalo que conterá Xou Odependendo do usuário atual. Aplicaremos classes a este período para colorir o texto.

A terceira seção é a que contém o tabuleiro do jogo. Possui uma container classe para que possamos posicionar corretamente nossos ladrilhos. Dentro desta seção, temos nove divs que atuarão como as peças dentro do tabuleiro.

A quarta seção será responsável por anunciar o resultado final do jogo. Por padrão, ele está vazio e vamos modificar seu conteúdo de javascript.

A última seção conterá nossos controles, que contém um botão de reinicialização.

<main class="background">
        <section class="title">
            <h1>Jogo da Velha</h1>
        </section>
        <section class="display">
            Player <span class="display-player playerX">X</span>'s turn
        </section>
        <section class="container">
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
        </section>
        <section class="display announcer hide"></section>
        <section class="controls">
            <button id="reset">Reset</button>
        </section>
    </main>

Adicionando o CSS

Primeiro, vou criar o style.css arquivo e remover quaisquer margens e preenchimentos definidos pelo navegador e definir a fonte do Google que incluí no HTML para todo o documento.

* {
    padding: 0;
    margin: 0;
    font-family: 'Itim', cursive;
}

A próxima coisa importante a adicionar é o estilo do nosso quadro. Usaremos a grade CSS para criar a placa. podemos dividir o contêiner igualmente em três, fornecendo 3 vezes 33% para as colunas e as linhas. Vamos centralizar o contêiner no meio definindo uma largura máxima e aplicando margin: 0 auto;.

.container {
    margin: 0 auto;
    display: grid;
    grid-template-columns: 33% 33% 33%;
    grid-template-rows: 33% 33% 33%;
    max-width: 300px;
}

Em seguida, adicionaremos o estilo para as peças dentro do tabuleiro. Vamos aplicar uma pequena borda branca e definir uma largura e altura mínimas de 100 pixels. Vamos centralizar o conteúdo usando o flexbox e definindo o justify-contente o align-itemspara center. Forneceremos a ele um tamanho de fonte grande e o aplicaremos cursor: pointerpara que o usuário saiba que este campo é clicável.

.tile {
    border: 1px solid white;
    min-width: 100px;
    min-height: 100px;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 50px;
    cursor: pointer;
}

Usarei duas cores diferentes para diferenciar melhor os dois jogadores. Para fazer isso, criarei duas classes de utilitários. O jogador X terá uma cor verde, enquanto o jogador O terá uma cor azul.

.playerX {
    color: #09C372;
}

.playerO {
    color: #498AFB;
}

Implementar a parte Javascript

Como incluímos nosso arquivo javascript no <head>, temos que envolver tudo em nosso código entre este manipulador de eventos. Isso é necessário porque nosso script será carregado antes que o corpo do HTML seja analisado pelo navegador. Se você não quiser embrulhar tudo dentro desta função, sinta-se à vontade para adicionar deferna tag do script ou mover a tag do script para a parte inferior do body.

window.addEventListener('DOMContentLoaded', () => {
  // everything goes here
});

Primeiro, salvaremos as referências aos nossos nós DOM. Vamos agarrar todos os ladrilhos usando document.querySelectorAll(). Queremos um array, mas esta função retorna um NodeList, então temos que convertê-lo em um array adequado com Array.from(). Também pegaremos uma referência à tela do player, botão de reinicialização e o locutor.

const tiles = Array.from(document.querySelectorAll('.tile'));
const playerDisplay = document.querySelector('.display-player');
const resetButton = document.querySelector('#reset');
const announcer = document.querySelector('.announcer');

A seguir, adicionaremos as variáveis ​​globais de que precisamos para controlar nosso jogo. Inicializaremos uma placa com um Array de nove strings vazias. Isso conterá os valores X abd O para cada peça no tabuleiro. Teremos um currentPlayerque contém o sinal do jogador cujo ativo no turno atual. A isGameActivevariável será verdadeira até que alguém ganhe ou o jogo termine empatado. Nesses casos, definiremos como falso para que os blocos restantes fiquem inativos até uma reinicialização. Temos três constantes que representam os estados finais do jogo. Usamos esses constantes para evitar erros de digitação.

let board = ['', '', '', '', '', '', '', '', ''];
let currentPlayer = 'X';
let isGameActive = true;

const PLAYERX_WON = 'PLAYERX_WON';
const PLAYERO_WON = 'PLAYERO_WON';
const TIE = 'TIE';

Na próxima etapa, armazenaremos todas as posições vencedoras no tabuleiro. Em cada submatriz, armazenaremos os índices das três posições que podem vencer o jogo. Portanto, o [0, 1, 2]representará um caso em que a primeira linha horizontal é ocupada por um jogador. Usaremos essa matriz para decidir se temos um vencedor ou não.

/*
   Indexes within the board
   [0] [1] [2]
   [3] [4] [5]
   [6] [7] [8]
*/

const winningConditions = [
   [0, 1, 2],
   [3, 4, 5],
   [6, 7, 8],
   [0, 3, 6],
   [1, 4, 7],
   [2, 5, 8],
   [0, 4, 8],
   [2, 4, 6]
];

Agora vamos escrever algumas funções utilitárias. Na isValidActionfunção, decidiremos se o usuário deseja realizar uma ação válida ou não. Se o texto interno do ladrilho for Xou Oretornamos false porque a ação é inválida, caso contrário, o ladrilho está vazio e a ação é válida.

const isValidAction = (tile) => {
    if (tile.innerText === 'X' || tile.innerText === 'O'){
        return false;
    }

    return true;
};

A próxima função de utilidade será muito simples. Nesta função, receberemos um índice como parâmetro e definiremos o elemento correspondente na matriz do tabuleiro para ser o sinal do nosso jogador atual.

const updateBoard =  (index) => {
   board[index] = currentPlayer;
}

Vamos escrever uma pequena função que tratará da mudança do player. Nesta função, primeiro removeremos a classe do jogador atual do playerDisplay. O literal de modelo de string player${currentPlayer}se tornará playerXou, playerOdependendo do jogador atual. A seguir, usaremos uma expressão ternária para alterar o valor do jogador atual. Se foi X, será de Ooutra forma, será X. Agora que alteramos o valor de nosso usuário, precisamos atualizar o innerTextde playerDisplaye aplicar a nova classe de jogador a ele.

const changePlayer = () => {
    playerDisplay.classList.remove(`player${currentPlayer}`);
    currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
    playerDisplay.innerText = currentPlayer;
    playerDisplay.classList.add(`player${currentPlayer}`);
}

Agora escreveremos a função de anunciante que anunciará o resultado final do jogo. Ele receberá um tipo de jogo final e atualizará o innerTextnó DOM do anunciante com base no resultado. Na última linha temos que remover a classe ocultar, pois o locutor fica oculto por padrão até o final do jogo.

const announce = (type) => {
    switch(type){
       case PLAYERO_WON:
            announcer.innerHTML = 'Player <span class="playerO">O</span> Won';
            break;
       case PLAYERX_WON:
            announcer.innerHTML = 'Player <span class="playerX">X</span> Won';
            break;
       case TIE:
            announcer.innerText = 'Tie';
        }
    announcer.classList.remove('hide');
};

A seguir, escreveremos uma das partes mais interessantes deste projeto, a avaliação de resultados. Primeiro, vamos criar uma variável roundWon e inicializá-la com false. Em seguida, percorreremos a winConditionsmatriz e verificaremos no quadro cada condição de vitória. Assim, por exemplo, na segunda iteração, verificaremos estes valores: board[3](a), board[4](b), board[5](c).

Também faremos algumas otimizações, se algum dos campos estiver vazio chamaremos continuee pularemos para a próxima iteração, porque você não pode ganhar se houver uma peça vazia na condição de vitória. Se todos os campos forem iguais, então temos um vencedor, então definimos o roundWon como true e quebramos o loop for, porque qualquer iteração posterior seria um cálculo perdido.

Após o loop, verificaremos o valor da roundWonvariável e, se for verdade, anunciaremos um vencedor e definiremos o jogo como inativo. Se não tivermos um vencedor, verificaremos se temos peças vazias no tabuleiro e se não tivermos um vencedor e não houver mais peças vazias, anunciamos o empate.

function handleResultValidation() {
  let roundWon = false;
  for (let i = 0; i <= 7; i++) {
    const winCondition = winningConditions[i];
    const a = board[winCondition[0]];
    const b = board[winCondition[1]];
    const c = board[winCondition[2]];
    if (a === "" || b === "" || c === "") {
      continue;
    }
    if (a === b && b === c) {
      roundWon = true;
      break;
    }
  }

  if (roundWon) {
    announce(currentPlayer === "X" ? PLAYERX_WON : PLAYERO_WON);
    isGameActive = false;
    return;
  }

  if (!board.includes("")) announce(TIE);
}

A seguir, trataremos da ação do usuário. Esta função receberá um bloco e um índice como parâmetro. Esta função será chamada quando o usuário clicar em um bloco. Primeiro precisamos verificar se é uma ação válida ou não e também verificaremos se o jogo está ativo ou não. Se ambos forem verdadeiros, atualizamos o innerTextda peça com o sinal do jogador atual, adicionamos a classe correspondente e atualizamos a matriz do tabuleiro. Agora que tudo está atualizado, temos que verificar se o jogo terminou ou não, então ligamos handleResultValidation(). Por último, temos que chamar o changePlayermétodo para passar a vez para o outro jogador.

const userAction = (tile, index) => {
  if (isValidAction(tile) && isGameActive) {
    tile.innerText = currentPlayer;
    tile.classList.add(`player${currentPlayer}`);
    updateBoard(index);
    handleResultValidation();
    changePlayer();
  }
};

Para fazer o jogo funcionar, temos que adicionar ouvintes de eventos às peças. Podemos fazer isso percorrendo a matriz de blocos e adicionar um ouvinte de evento para cada um. (Para obter um desempenho mais otimizado, poderíamos apenas adicionar um ouvinte de evento ao contêiner e usar o borbulhamento de evento para capturar os cliques no bloco no pai, mas acho que para iniciantes isso é mais fácil de entender.)

tiles.forEach( (tile, index) => {
    tile.addEventListener('click', () => userAction(tile, index));
});

Há apenas uma funcionalidade da qual sentimos falta: reiniciar o jogo. Para isso, escreveremos uma resetBoardfunção. Nesta função configuramos o tabuleiro para consistir em nove strings vazias, definimos o jogo como ativo, removemos o locutor e mudamos o jogador de volta para X(por definição Xcomeça sempre).

A última coisa que temos que fazer é percorrer as peças e definir o innerText de volta para uma string vazia e remover quaisquer classes específicas do jogador das peças.

const resetBoard = () => {
    board = ['', '', '', '', '', '', '', '', ''];
    isGameActive = true;
    announcer.classList.add('hide');

    if (currentPlayer === 'O') {
        changePlayer();
    }

    tiles.forEach(tile => {
        tile.innerText = '';
        tile.classList.remove('playerX');
        tile.classList.remove('playerO');
    });
}

Agora só temos que registrar essa função como um manipulador de eventos de clique para o botão de redefinição.

resetButton.addEventListener('click', resetBoard);

E é isso, temos um jogo da velha totalmente funcional que você pode jogar com seu amigo e se divertir.

Feliz hackeamento!

Postado em Blog
Escreva um comentário