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
- Montar o tabuleiro 6 × 5 em HTML e CSS.
- Pedir
palavras.jsoncomfetchlocal. - Tratar tecla: letra, Enter, Backspace.
- Pintar verde, amarelo e cinza na ordem certa (contar repetidas).
- Encerrar em vitória ou nas seis tentativas.
- Seguir um plano: uma mecânica por vez, não o jogo inteiro de uma vez.
Regras da Restinga
- Palavra secreta de cinco letras, sem acento neste material (
PRAIA,DUNAS,COSTA). - Seis linhas. Acertou: o jogo acaba. Gastou as seis: mostra a secreta.
- Verde: letra e posição certas.
- Amarelo: a letra aparece na secreta, mas noutro lugar. Conta a quantidade. Se a secreta é
COSTAe o palpite éOSSOS, o S da terceira posição fica verde; os outros S ficam cinza porque a única ocorrência já foi usada. O primeiro O fica amarelo e o outro O fica cinza. - Cinza: a letra não resta na secreta.
- Teclado físico neste exemplo. Ignore número, espaço e símbolo.
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.

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.

Ordem que fecha o jogo
Não escreva duzentas linhas de uma vez. Ordem que o exemplo segue:
- HTML, CSS, trinta casas vazias.
- Teclado: letra, backspace, enter incompleto ignorado.
fetchdepalavras.jsonem127.0.0.1.- Enter compara palpite e secreta;
alertou#statusde vitória e derrota. - Verdes.
- Cinzas das letras que não existem.
- Amarelos com o mapa de quantidade.
- 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
- Quantas letras e quantas tentativas tem o jogo?
- Por que o
fetchpedepalavras.jsone não uma URL de nuvem? - Por que existem duas passagens de cor, não uma só com
includes? - O que acontece se a pessoa aperta Enter com quatro letras?
Respostas comentadas
- Cinco letras, seis tentativas.
- O combinado do volume: dado local, servidor em
127.0.0.1, sem API obrigatória. - A primeira gasta os verdes. A segunda pinta amarelo só enquanto o mapa ainda tem estoque daquela letra.
- Nada:
enviarretorna. A linha continua editável.