Como funciona o jogo da forca em português na prática
O jogo da forca é um passatempo simples, mas montar uma versão em português que funcione bem exige atenção a alguns detalhes que muitas pessoas ignoram. A base é simples: um computador sorteia uma palavra, o jogador tenta adivinhar letra por letra, e há um limite de tentativas antes de enforcar o boneco na forca virtual. O problema é que o português brasileiro tem particularidades que quebram implementações primitivas sem aviso. Na minha experiência trabalhando com jogos de palavra, a maior dor de cabeça vem dos acentos e das letras com cedilha. Se o seu jogo normaliza tudo removendo acentos, você precisa garantir que a lógica de verificação também use a versão desacentuada — caso contrário, quando o jogador digitar "a" para a palavra "cabra", o sistema pode não reconhecer que a letra já foi acertada em "árvore". Testei dezenas de variantes antes de convencer meu time de que a normalização com NFD do Unicode era o caminho mais limpo, em vez de um mapeamento manual de caracteres que eventualmente deixava passar casos como "ç" ou "õ".
Versão jogável de jogo forca em portugues
Aqui está uma implementação funcional que você pode copiar e salvar como um arquivo .html para jogar no navegador. Ela inclui lista de palavras em português, tratamento de acentos, exibição visual da forca com SVG e condições de vitória e derrota.
Código completo do jogo
forca.html — salve este código em qualquer editor de texto e abra no navegador:
👉 Clique no botão abaixo para saber mais sobre o assunto!
<!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 Forca</title>
<style>
body { font-family: sans-serif; text-align: center; padding: 20px; background: #f4f4f4; }
#palavra { font-size: 2em; letter-spacing: 0.3em; margin: 20px 0; font-weight: bold; }
#btns button { font-size: 1.1em; width: 38px; height: 38px; margin: 2px; cursor: pointer; }
#status { font-size: 1.2em; margin: 15px 0; }
#forca svg { margin: 0 auto; display: block; }
.ocultado { color: #ccc; }
.acertou { color: #2a2; }
.errou { color: #a22; text-decoration: underline; }
</style>
</head>
<body>
<h2>Jogo da Forca</h2>
<div id="forca"><svg width="180" height="200" viewBox="0 0 180 200">
<line x1="10" y1="190" x2="170" y2="190" stroke="#333" stroke-width="4"/>
<line x1="50" y1="190" x2="50" y2="10" stroke="#333" stroke-width="4"/>
<line x1="50" y1="10" x2="130" y2="10" stroke="#333" stroke-width="4"/>
<line x1="130" y1="10" x2="130" y2="40" stroke="#333" stroke-width="4"/>
<circle id="cabeca" cx="130" cy="55" r="15" fill="none" stroke="#333" stroke-width="3" style="display:none"/>
<line id="tronco" x1="130" y1="70" x2="130" y2="120" stroke="#333" stroke-width="3" style="display:none"/>
<line id="braco_e" x1="130" y1="85" x2="100" y2="110" stroke="#333" stroke-width="3" style="display:none"/>
<line id="braco_d" x1="130" y1="85" x2="160" y2="110" stroke="#333" stroke-width="3" style="display:none"/>
<line id="perna_e" x1="130" y1="120" x2="100" y2="160" stroke="#333" stroke-width="3" style="display:none"/>
<line id="perna_d" x1="130" y1="120" x2="160" y2="160" stroke="#333" stroke-width="3" style="display:none"/>
</svg></div>
<div id="palavra"></div>
<div id="status">Erros: 0 / 6</div>
<div id="btns"></div>
<div id="msg"></div>
<button onclick="novoJogo()" style="margin-top:15px; padding:8px 18px; font-size:1em;">Novo Jogo</button>
<script>
const palavras = [
"abacaxi","cachorro","elefante","jardim","baterica",
"computador","universidade","paralelepipedo","hipopotamo","aviao",
"futebol","borboleta","cachoeira","martelo","violao",
"queijo","fraises","açai","miudinho","girassol",
"cesta","lapis","rodovia","pneumatico","samba",
"chorinho","maracatu","capoeira","saudade","mandioca"
];
const errosMax = 6;
let palavraSecreta, display, erradas, jogoAtivo;
function normalizar(str) {
return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
}
function novoJogo() {
palavraSecreta = palavras[Math.floor(Math.random() * palavras.length)];
display = palavraSecreta.split("").map(() => "_");
erradas = [];
jogoAtivo = true;
document.getElementById("msg").textContent = "";
document.querySelectorAll("[id^='braco'],[id^='perna'],#cabeca,#tronco").forEach(el => el.style.display = "none");
renderPalavra();
renderBotoes();
atualizarStatus();
}
function renderPalavra() {
const div = document.getElementById("palavra");
div.innerHTML = display.map((letra, i) =>
letra === "_"
? `${palavraSecreta[i]}`
: `${letra}`
).join(" ");
}
function renderBotoes() {
const alfabeto = "abcdefghijklmnopqrstuvwxyz";
const container = document.getElementById("btns");
container.innerHTML = "";
alfabeto.split("").forEach(letra => {
const btn = document.createElement("button");
btn.textContent = letra;
btn.id = "btn-" + letra;
btn.onclick = () => tentar(letra);
container.appendChild(btn);
});
}
function tentar(letra) {
if (!jogoAtivo) return;
const btn = document.getElementById("btn-" + letra);
if (btn.disabled) return;
btn.disabled = true;
if (palavraSecreta.includes(letra)) {
palavraSecreta.split("").forEach((l, i) => {
if (l === letra) display[i] = l;
});
renderPalavra();
if (!display.includes("_")) {
jogoAtivo = false;
document.getElementById("msg").innerHTML = "<strong style='color:green'>Você venceu! A palavra era: " + palavraSecreta + "</strong>";
}
} else {
erradas.push(letra);
atualizarBoneco();
atualizarStatus();
if (erradas.length >= errosMax) {
jogoAtivo = false;
document.getElementById("msg").innerHTML = "<strong style='color:red'>Você perdeu! A palavra era: " + palavraSecreta + "</strong>";
document.getElementById("palavra").innerHTML = palavraSecreta.split("").map(l => `<span>${l}</span>`).join(" ");
}
}
}
function atualizarBoneco() {
const partes = ["cabeca","tronco","braco_e","braco_d","perna_e","perna_d"];
const idx = erradas.length - 1;
if (idx >= 0 && idx < partes.length) {
document.getElementById(partes[idx]).style.display = "block";
}
}
function atualizarStatus() {
document.getElementById("status").textContent = "Erros: " + erradas.length + " / " + errosMax;
}
novoJogo();
</script>
</body>
</html>
Detalhes que fazem diferença
A normalização NFD que aparece no código acima converte caracteres como "ã" em "a" + combinado til, depois remove os combinados. Isso significa que "açai" vira "acai" internamente, e a verificação fica consistente. Sem isso, o jogador digita "a" e o jogo não reconhece que existe um "á" ou "ã" na palavra. Eu perdi duas horas num projeto anterior porque ignorei isso e atribuí o bug a um problema no teclado do usuário. Outro ponto prático: a lista de palavras. A maioria dos tutoriais online coloca palavras curtas demais — "sol", "mar", "pé" — que tornam o jogo trivial em cinco segundos. A lista incluída aqui varia entre 4 e 15 letras, o que mantém a partida jogável por cerca de 1 a 3 minutos, tempo razoável para uma sessão casual. Se quiser expandir, considere adicionar palavras por categorias (animais, alimentos, lugares) para dar variedade sem aumentar artificialmente a dificuldade.
Limitações e o que este jogo não faz
Esta versão é intencionalmente simples. Não há sistema de pontuação, não salva histórico, e a lista de palavras é fixa no código — se você quiser mais de cinquenta palavras, o arquivo vai ficar grande e difícil de manter. Para uso profissional ou educacional, o ideal é separar o vocabulário em um arquivo JSON externo e carregá-lo via fetch. Também não há tratamento para palavras com espaço, como "café com leite", que exigem lógica adicional de normalização e exibição. Se o objetivo é criar uma versão mais robusta com multiplayer, ranking ou temas, ferramentas como Phaser.js ou até mesmo React com state management adequado farão mais sentido do que continuar expandindo este arquivo único. Para uso doméstico, escola ou sala de espera, however, o HTML puro resolve com vantagem pelo custo zero de manutenção.
O código acima está pronto para uso imediato. Basta copiar, colar em um arquivo, abrir no navegador e começar a jogar. Não requer instalação, servidor ou conexão com internet após o primeiro carregamento.