Pular para o conteúdo
murad.guide

Introdução Completa à Web

Capítulo 22

Projeto: Palavras da Restinga

6 min de leitura 1.058 palavras 2 gravuras

Último projeto do percurso: um jogo de cinco letras na Casa da Restinga, em Natal. Há uma palavra secreta. Seis tentativas. Verde: letra certa no lugar. Amarelo: letra existe, lugar errado. Cinza: não entra. O nome do jogo é Palavras da Restinga. Esta é o capítulo 22 de 23.

A palavra e a lista de válidas vêm de palavras.json na mesma pasta. Não chame API na nuvem. Sirva em 127.0.0.1. O exemplo completo está em exemplos/22-palavras/.

Trabalhe na cópia inicio da etapa. Leia e digite o código; mude uma coisa. Antes de recarregar, anote sua previsão. Depois confira com a referência completa. Se nada mudar, confira o arquivo salvo e o caminho aberto. Blocos adicionais do capítulo podem ser digitados no console; o exemplo completo mostra a aplicação daquela etapa.

O que você vai aprender

Regras da Restinga

Não use marca de outro jogo. O tabuleiro, as cores e o JSON são deste volume.

Pare e experimente. Abra exemplos/22-palavras/ em http://127.0.0.1:8771/exemplos/22-palavras/. Preveja seis linhas vazias. Digite cinco letras e Enter. Confira as cores.

Erro comum: abrir pelo disco. O fetch do JSON falha. Use o servidor do capítulo 21.

Seis linhas, cinco letras, verde amarelo cinza

HTML e CSS primeiro

Trinta casas: seis linhas, cinco letras. Cada casa pode ser um div com classe .casa ou um button desativado para o desenho. O exemplo usa div com classe cel no tabuleiro e um campo com rótulo e formulário local. Título, lang="pt-BR", viewport, O script da referência usa defer: espera a leitura do HTML.

<!DOCTYPE html>
<html lang="pt-BR">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Palavras da Restinga</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <main>
    <h1>Palavras da Restinga</h1>
    <p id="aviso">Carregando o dicionário…</p>
    <div id="grade" class="grade"><!-- 30 casas na referência completa --></div>
    <form id="form-chute">
      <label for="chute">Seu chute</label>
      <input id="chute" required minlength="5" maxlength="5" pattern="[A-Za-z]{5}">
      <button type="submit">Enviar</button>
    </form>
    <button id="tentar" type="button">Carregar novamente</button>
  </main>
  <script src="jogo.js"></script>
</body>
</html>

No CSS, o tabuleiro é Grid: grid-template-columns: repeat(5, 3rem) e gap. Classes .certo, .lugar, .fora pintam o fundo depois do Enter. Foco visível ~3px se houver controle clicável.

Monte as 30 casas no JS com um for, ou escreva no HTML. Os dois valem. A referência escreve as trinta casas no HTML. A classe cel marca cada casa.

Pare e experimente. Mude o gap para 0.8rem. Preveja casas mais afastadas. Recarregue. Confira. Restaure.

Erro comum: seis input de uma letra e lutar com o foco. Um único campo com rótulo mantém a digitação e o foco simples.

palavras.json local

O arquivo na pasta tem a secreta e as válidas. Carregar novamente reinicia o jogo depois de corrigir o arquivo. Formato do exemplo:

{
  "secreta": "PRAIA",
  "validas": ["PRAIA", "DUNAS", "AREIA", "COSTA", "VERDE", "FAROL", "ONDAS", "PEDRA", "VENTO", "NATAL"]
}

Carregue antes de aceitar teclas:

async function iniciar() {
  const resposta = await fetch("palavras.json");
  const dados = await resposta.json();
  const secreta = dados.secreta.toUpperCase();
  const validas = dados.validas.map(function (p) {
    return p.toUpperCase();
  });
  // estado: linhaAtual, palpite, acabou
}
iniciar();

Se o palpite de cinco letras não está em validas, não pinte: avise no #aviso e deixe a linha para correção.

Pare e experimente. Troque "secreta" no JSON para "DUNAS". Preveja o jogo novo. Recarregue em 127.0.0.1. Confira.

Erro comum: sortear a palavra no JS e esquecer o JSON. O combinado deste capítulo é fetch local.

Digitação e envio pelo teclado

A referência usa o campo chute e o formulário form-chute. Clique no rótulo ou chegue com Tab. Digite cinco letras; Backspace corrige como em qualquer campo. Enter envia o palpite sem trocar de página.

form.addEventListener("submit", (evento) => {
  evento.preventDefault();
  enviar();
});

preventDefault cancela o envio normal. A função enviar confere se o dicionário carregou, se o palpite tem cinco letras A a Z e se aparece na lista de válidas. O arquivo jogo.js contém a referência completa.

Mude uma coisa. Troque apenas uma palavra da lista de válidas no JSON. Preveja que o palpite retirado será recusado sem gastar uma linha. Recarregue, confira e restaure.

Erro comum: tentar jogar antes de o JSON terminar. O campo começa desativado; se houver erro, a mensagem permite carregar novamente.

Verde, depois amarelo, depois cinza

Duas passagens. Primeiro os verdes, para gastar as letras certas. Depois amarelo ou cinza, olhando o que ainda resta.

function contar(letras) {
  const mapa = {};
  for (let i = 0; i < letras.length; i += 1) {
    const L = letras[i];
    mapa[L] = (mapa[L] || 0) + 1;
  }
  return mapa;
}

function pintar(palpite, secreta) {
  const chutes = palpite.split("");
  const certas = secreta.split("");
  const mapa = contar(certas);
  const cores = ["cinza", "cinza", "cinza", "cinza", "cinza"];
  for (let i = 0; i < 5; i += 1) {
    if (chutes[i] === certas[i]) {
      cores[i] = "verde";
      mapa[chutes[i]] -= 1;
    }
  }
  for (let i = 0; i < 5; i += 1) {
    if (cores[i] === "verde") {
      continue;
    }
    if (mapa[chutes[i]] > 0) {
      cores[i] = "amarelo";
      mapa[chutes[i]] -= 1;
    }
  }
  return cores;
}

Aplique as classes nas cinco casas da linha. Se as cinco forem verdes: vitória. Se linhaAtual virar 6: mostre a secreta.

Pare e experimente. Com secreta COSTA, chute OSSOS. Preveja o S da terceira posição verde, os outros S cinza e somente um O amarelo. Recarregue. Confira.

Erro comum: pintar amarelo só com secreta.includes(letra). Repetidas estouram. O mapa evita isso.

Início da referência desta etapa no navegador local

Ordem que fecha o jogo

Não escreva duzentas linhas de uma vez. Ordem que o exemplo segue:

  1. HTML, CSS, trinta casas vazias.
  2. Teclado: letra, backspace, enter incompleto ignorado.
  3. fetch de palavras.json em 127.0.0.1.
  4. Enter compara palpite e secreta; alert ou #status de vitória e derrota.
  5. Verdes.
  6. Cinzas das letras que não existem.
  7. Amarelos com o mapa de quantidade.
  8. Recusar palpite fora de validas.

O arquivo jogo.js da pasta já junta isso. Leia, rode, mude uma coisa (a secreta no JSON), preveja, recarregue, confira.

Pare e experimente. Acerte a secreta na primeira linha. Preveja o status de vitória e teclas ignoradas depois. Recarregue para jogar de novo.

Erro comum: validar a palavra num servidor externo. Aqui a lista é o JSON da pasta.

Atividades

  1. Quantas letras e quantas tentativas tem o jogo?
  2. Por que o fetch pede palavras.json e não uma URL de nuvem?
  3. Por que existem duas passagens de cor, não uma só com includes?
  4. O que acontece se a pessoa aperta Enter com quatro letras?

Respostas comentadas

  1. Cinco letras, seis tentativas.
  2. O combinado do volume: dado local, servidor em 127.0.0.1, sem API obrigatória.
  3. A primeira gasta os verdes. A segunda pinta amarelo só enquanto o mapa ainda tem estoque daquela letra.
  4. Nada: enviar retorna. A linha continua editável.

MDN