Projeto Jogo da Forca
O jogo da forca de caderno, agora no terminal: o computador escolhe uma palavra e mostra só os tracinhos, um para cada letra. Você chuta letras; cada acerto revela a letra em todos os lugares onde ela aparece, e cada erro desenha mais uma parte do boneco. Seis erros e o boneco está completo: você perdeu. É o projeto ideal para praticar textos e listas juntos.
- Percorrer um texto letra por letra com for e montar outro texto aos poucos com
+=(Operadores de Atribuição). - Perguntar "está dentro?" com in e not in, em textos e em listas.
- Guardar as letras certas e erradas em listas com append().
- Validar o chute com strip(), lower(), isalpha() e count(), e pular com continue.
- Desenhar o boneco com o if em uma linha e o escape
\\. - Sortear a palavra com random.choice() e, no fim, ler as palavras de um arquivo.
O que vamos construir
A forca pronta, no terminal da Ana. A letra depois de cada Letra: foi digitada por ela:
$ python3 forca.py
=== Jogo da Forca: frutas ===
+---+
|
|
|
|
=======
_ _ _ _ _ _
Letra: e
Não tem e.
+---+
O | « primeiro erro: a cabeça
|
|
|
=======
_ _ _ _ _ _
Erradas: e
Letra: i
Tem 1 letra(s) i!
+---+
O |
|
|
|
=======
_ _ i _ _ _
Erradas: e
Letra: a
Tem 2 letra(s) a!
+---+
O |
|
|
|
=======
_ _ i a _ a
Erradas: e
Letra: b
Tem 1 letra(s) b!
+---+
O |
|
|
|
=======
_ _ i a b a
Erradas: e
Letra: g
Tem 1 letra(s) g!
+---+
O |
|
|
|
=======
g _ i a b a
Erradas: e
Letra: o
Tem 1 letra(s) o!
+---+
O |
|
|
|
=======
g o i a b a
Erradas: e
Você venceu! A palavra era goiaba.
Cinco versões, uma novidade em cada:
- A palavra escondida (percorrer o texto, a lista
certas). - Erros contados: dá para perder (a lista
erradas,len). - Chute validado (
strip,lower,isalpha,continue). - Funções, desenho e palavra sorteada (
def,random.choice). - As palavras num arquivo (
openewith).
Antes de começar
Você vai usar strings, listas, while True com break (como na Calculadora) e random (como no Jogo de Adivinhação). Crie a pasta; o arquivo vai se chamar forca.py:
$ mkdir -p projetos/forca $ cd projetos/forca
Versão 1 · A palavra escondida
Primeiro, só o coração do jogo: a palavra fixa "banana", os tracinhos e as letras aparecendo. Sem erros, sem forca: o jogo termina quando a palavra inteira estiver descoberta.
A ideia: o programa guarda numa lista as letras que a pessoa já acertou. A cada volta, ele monta a "máscara" da palavra do zero: passa letra por letra de "banana" e, para cada uma, pergunta "esta letra já foi acertada?". Se sim, escreve a letra; se não, escreve _.
palavra = "banana"
certas = []
print("=== Jogo da Forca ===")
while True:
mascara = ""
for letra in palavra:
if letra in certas:
mascara += letra + " "
else:
mascara += "_ "
print(mascara)
if "_" not in mascara:
print("Você descobriu a palavra!")
break
chute = input("Letra: ")
if chute in palavra:
certas.append(chute)
- certas = []
- Uma lista vazia: as letras acertadas vão entrar aqui.
- mascara = ""
- Um texto vazio, que vai crescer letra por letra. Recomeça vazio a cada volta do
while. - for letra in palavra:
- Um
forsobre um texto passa por cada caractere:"b","a","n","a","n","a". - if letra in certas:
- "Esta letra está na lista das acertadas?"
- mascara += letra + " "
- Acrescenta a letra e um espaço no fim da máscara. O
+=é "o que já tinha, mais isto". - if "_" not in mascara:
- Se não sobrou nenhum tracinho, todas as letras foram descobertas.
- if chute in palavra: certas.append(chute)
- Se a letra existe na palavra, entra na lista. Uma vez na lista, ela aparece em todos os lugares, porque a máscara confere cada posição.
$ python3 forca.py === Jogo da Forca === _ _ _ _ _ _ Letra: a _ a _ a _ a « um chute, três letras Letra: x _ a _ a _ a « x não existe: nada acontece Letra: b b a _ a _ a Letra: n b a n a n a Você descobriu a palavra!
Funciona, mas repare no x: errar não custa nada, e a pessoa nem fica sabendo que errou. A versão 2 conta os erros.
Parece mais fácil guardar o texto "_ _ _ _ _ _" e ir trocando os tracinhos pelas letras. Mas em Python um texto não pode ser alterado por dentro: ele é imutável. Tentar trocar uma letra dá erro:
palavra = "banana" palavra[0] = "B"
$ python3 editar.py
Traceback (most recent call last):
File "/home/ana/projetos/forca/editar.py", line 2, in <module>
palavra[0] = "B"
~~~~~~~^^^
TypeError: 'str' object does not support item assignment
TypeError: 'str' object does not support item assignment = "um objeto str não aceita atribuição a um item": dá para ler palavra[0], mas não para trocá-lo. Para "mudar" um texto, cria-se um texto novo. É o que a versão 1 faz: um mascara novo a cada volta. A aula Mutável × Imutável explica a diferença entre textos e listas.
Missão (versão 1): o programa mostra os tracinhos e pede letras, mas nunca guarda os acertos: a palavra nunca aparece. No lugar do comentário, escreva o if que coloca o chute na lista certas quando a letra está na palavra. A entrada da missão é b, a, n.
palavra = "banana"
certas = []
print("=== Jogo da Forca ===")
while True:
mascara = ""
for letra in palavra:
if letra in certas:
mascara += letra + " "
else:
mascara += "_ "
print(mascara)
if "_" not in mascara:
print("Você descobriu a palavra!")
break
chute = input("Letra: ")
# guarde o chute em certas, se a letra estiver na palavra
palavra = "banana"
certas = []
print("=== Jogo da Forca ===")
while True:
mascara = ""
for letra in palavra:
if letra in certas:
mascara += letra + " "
else:
mascara += "_ "
print(mascara)
if "_" not in mascara:
print("Você descobriu a palavra!")
break
chute = input("Letra: ")
if chute in palavra:
certas.append(chute)
No código inicial, quando a entrada da missão acaba, o input() não tem mais o que ler e o programa termina com um erro EOFError ("fim do arquivo": a entrada acabou). É o sinal de que o jogo continuava esperando letras.
Versão 2 · Erros contados: dá para perder
Agora cada letra errada vai para uma segunda lista, erradas. Quantos erros a pessoa já fez? É o tamanho dessa lista: len(erradas). Com 6 erros, ela perde.
palavra = "banana"
MAX_ERROS = 6
certas = []
erradas = []
print("=== Jogo da Forca ===")
while True:
mascara = ""
for letra in palavra:
if letra in certas:
mascara += letra + " "
else:
mascara += "_ "
print(mascara)
print(f"Erros: {len(erradas)} de {MAX_ERROS}")
if "_" not in mascara:
print(f"Você venceu! A palavra era {palavra}.")
break
if len(erradas) == MAX_ERROS:
print(f"Você perdeu! A palavra era {palavra}.")
break
chute = input("Letra: ")
if chute in palavra:
certas.append(chute)
print("Tem!")
else:
erradas.append(chute)
print("Não tem.")
| O que entrou | Para quê |
|---|---|
MAX_ERROS = 6 | O limite (cabeça, tronco, dois braços, duas pernas), em MAIÚSCULAS por ser um valor fixo. |
erradas = [] | A segunda lista: as letras que não existem na palavra. |
len(erradas) | Quantos itens a lista tem = quantos erros (veja Funções Embutidas). |
if len(erradas) == MAX_ERROS: | O fim do jogo por derrota. Vem depois do teste da vitória e antes do input: assim, o jogo para assim que o sexto erro acontece. |
else: erradas.append(chute) | A letra não está na palavra: vai para as erradas, e o jogo avisa. |
Uma vitória e uma derrota:
$ python3 forca.py === Jogo da Forca === _ _ _ _ _ _ Erros: 0 de 6 Letra: a Tem! _ a _ a _ a Erros: 0 de 6 Letra: e Não tem. _ a _ a _ a Erros: 1 de 6 Letra: n Tem! _ a n a n a Erros: 1 de 6 Letra: o Não tem. _ a n a n a Erros: 2 de 6 Letra: b Tem! b a n a n a Erros: 2 de 6 Você venceu! A palavra era banana. $ python3 forca.py === Jogo da Forca === _ _ _ _ _ _ Erros: 0 de 6 Letra: e Não tem. _ _ _ _ _ _ Erros: 1 de 6 Letra: i Não tem. _ _ _ _ _ _ Erros: 2 de 6 Letra: o Não tem. _ _ _ _ _ _ Erros: 3 de 6 Letra: u Não tem. _ _ _ _ _ _ Erros: 4 de 6 Letra: s Não tem. _ _ _ _ _ _ Erros: 5 de 6 Letra: t Não tem. _ _ _ _ _ _ Erros: 6 de 6 Você perdeu! A palavra era banana.
Quando dá errado na versão 2
$ python3 forca.py === Jogo da Forca === _ _ _ _ _ _ Erros: 0 de 6 Letra: A Não tem. « mas banana tem a! _ _ _ _ _ _ Erros: 1 de 6 Letra: B Não tem. _ _ _ _ _ _ Erros: 2 de 6 Letra: N Não tem. _ _ _ _ _ _ Erros: 3 de 6 Letra: a Tem! _ a _ a _ a Erros: 3 de 6 Letra: b Tem! b a _ a _ a Erros: 3 de 6 Letra: n Tem! b a n a n a Erros: 3 de 6 Você venceu! A palavra era banana.
Para o Python, "A" e "a" são caracteres diferentes, e "A" in "banana" é False. Com o Caps Lock ligado, a Ana gastou três erros com letras que existiam. Não houve traceback: o programa fez exatamente o que foi mandado. A versão 3 passa o chute para minúscula.
$ python3 forca.py
=== Jogo da Forca ===
_ _ _ _ _ _
Erros: 0 de 6
Letra:
Tem! « a Ana só apertou Enter
_ _ _ _ _ _
Erros: 0 de 6
Letra: x
Não tem.
_ _ _ _ _ _
Erros: 1 de 6
Letra: ^CTraceback (most recent call last):
File "/home/ana/projetos/forca/forca.py", line 24, in <module>
chute = input("Letra: ")
KeyboardInterrupt
Apertar só Enter faz o input() devolver o texto vazio, "". E o texto vazio está "dentro" de qualquer texto:
$ python3 Python 3.14.4 (main, Aug 20 2026, 10:41:58) [GCC 15.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> "" in "banana" True >>> exit()
Então o jogo disse "Tem!" e guardou "" na lista certas, sem revelar nada. Também "an" daria "Tem!", porque o in entre textos procura um pedaço. A versão 3 só aceita exatamente uma letra. (No fim, a Ana saiu com Ctrl+C, que interrompe o programa com KeyboardInterrupt.)
Missão (versão 2): o jogo conta os erros, mas nunca termina por derrota: depois do sexto erro, continua pedindo letras. Logo depois do if da vitória, acrescente o if que confere len(erradas) == MAX_ERROS, mostra Você perdeu! A palavra era banana. (use {palavra}) e sai com break. A entrada da missão são seis letras que não existem em banana.
palavra = "banana"
MAX_ERROS = 6
certas = []
erradas = []
print("=== Jogo da Forca ===")
while True:
mascara = ""
for letra in palavra:
if letra in certas:
mascara += letra + " "
else:
mascara += "_ "
print(mascara)
print(f"Erros: {len(erradas)} de {MAX_ERROS}")
if "_" not in mascara:
print(f"Você venceu! A palavra era {palavra}.")
break
chute = input("Letra: ")
if chute in palavra:
certas.append(chute)
print("Tem!")
else:
erradas.append(chute)
print("Não tem.")
palavra = "banana"
MAX_ERROS = 6
certas = []
erradas = []
print("=== Jogo da Forca ===")
while True:
mascara = ""
for letra in palavra:
if letra in certas:
mascara += letra + " "
else:
mascara += "_ "
print(mascara)
print(f"Erros: {len(erradas)} de {MAX_ERROS}")
if "_" not in mascara:
print(f"Você venceu! A palavra era {palavra}.")
break
if len(erradas) == MAX_ERROS:
print(f"Você perdeu! A palavra era {palavra}.")
break
chute = input("Letra: ")
if chute in palavra:
certas.append(chute)
print("Tem!")
else:
erradas.append(chute)
print("Não tem.")
Versão 3 · Chute validado
Três defesas novas, todas antes de o chute ser contado: o chute é limpo (sem espaços, em minúscula), tem de ser uma letra só, e não pode ser uma letra já tentada. Se não passar, o programa avisa e volta para o começo do laço com continue, sem contar nada.
palavra = "banana"
MAX_ERROS = 6
certas = []
erradas = []
print("=== Jogo da Forca ===")
while True:
mascara = ""
for letra in palavra:
if letra in certas:
mascara += letra + " "
else:
mascara += "_ "
print(mascara)
print(f"Erros: {len(erradas)} de {MAX_ERROS}")
if "_" not in mascara:
print(f"Você venceu! A palavra era {palavra}.")
break
if len(erradas) == MAX_ERROS:
print(f"Você perdeu! A palavra era {palavra}.")
break
chute = input("Letra: ").strip().lower()
if len(chute) != 1 or not chute.isalpha():
print("Digite uma letra só.")
continue
if chute in certas or chute in erradas:
print(f"Você já tentou a letra {chute}.")
continue
if chute in palavra:
certas.append(chute)
print("Tem!")
else:
erradas.append(chute)
print("Não tem.")
- input("Letra: ").strip().lower()
- Tira os espaços das pontas e passa para minúscula:
" B "vira"b". - len(chute) != 1
- Recusa o texto vazio (só Enter, tamanho 0) e os chutes de duas letras ou mais (
"ab"). - not chute.isalpha()
isalpha()("é alfabético?") dáTruesó para letras. Onotinverte: recusa"1","?","-".- or
- Basta um dos dois problemas para recusar (veja and, or e not).
- chute in certas or chute in erradas
- A letra já está numa das listas: a pessoa já tentou. Sem este teste, repetir uma letra errada contaria como mais um erro.
- continue
- Volta para o começo do
while: a máscara aparece de novo e o programa pede outra letra, sem mexer nas listas.
$ python3 Python 3.14.4 (main, Aug 20 2026, 10:41:58) [GCC 15.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> "b".isalpha() True >>> "1".isalpha() False >>> "ç".isalpha() True >>> exit()
O "ç" também é letra para o Python: o isalpha() conhece os acentos e as letras de outros alfabetos.
$ python3 forca.py === Jogo da Forca === _ _ _ _ _ _ Erros: 0 de 6 Letra: B Tem! « o B virou b b _ _ _ _ _ Erros: 0 de 6 Letra: b Você já tentou a letra b. b _ _ _ _ _ Erros: 0 de 6 Letra: Digite uma letra só. b _ _ _ _ _ Erros: 0 de 6 Letra: ab Digite uma letra só. b _ _ _ _ _ Erros: 0 de 6 Letra: 1 Digite uma letra só. b _ _ _ _ _ Erros: 0 de 6 Letra: a Tem! b a _ a _ a Erros: 0 de 6 Letra: n Tem! b a n a n a Erros: 0 de 6 Você venceu! A palavra era banana.
Nenhuma das quatro tentativas inválidas virou erro: o placar ficou em 0 de 6 o tempo todo.
Missão (versão 3): este é o código da versão 2. Faça o chute passar por .strip().lower() e acrescente, logo depois do input, os dois testes com continue: "uma letra só" e "já tentou". A entrada da missão é B, b, só Enter, ab, 1, a, n: devem aparecer os dois avisos e a vitória sem nenhum erro (Erros: 0 de 6 no fim).
palavra = "banana"
MAX_ERROS = 6
certas = []
erradas = []
print("=== Jogo da Forca ===")
while True:
mascara = ""
for letra in palavra:
if letra in certas:
mascara += letra + " "
else:
mascara += "_ "
print(mascara)
print(f"Erros: {len(erradas)} de {MAX_ERROS}")
if "_" not in mascara:
print(f"Você venceu! A palavra era {palavra}.")
break
if len(erradas) == MAX_ERROS:
print(f"Você perdeu! A palavra era {palavra}.")
break
chute = input("Letra: ")
if chute in palavra:
certas.append(chute)
print("Tem!")
else:
erradas.append(chute)
print("Não tem.")
palavra = "banana"
MAX_ERROS = 6
certas = []
erradas = []
print("=== Jogo da Forca ===")
while True:
mascara = ""
for letra in palavra:
if letra in certas:
mascara += letra + " "
else:
mascara += "_ "
print(mascara)
print(f"Erros: {len(erradas)} de {MAX_ERROS}")
if "_" not in mascara:
print(f"Você venceu! A palavra era {palavra}.")
break
if len(erradas) == MAX_ERROS:
print(f"Você perdeu! A palavra era {palavra}.")
break
chute = input("Letra: ").strip().lower()
if len(chute) != 1 or not chute.isalpha():
print("Digite uma letra só.")
continue
if chute in certas or chute in erradas:
print(f"Você já tentou a letra {chute}.")
continue
if chute in palavra:
certas.append(chute)
print("Tem!")
else:
erradas.append(chute)
print("Não tem.")
Versão 4 · Funções, desenho e palavra sorteada
Hora de virar jogo de verdade: a palavra é sorteada de uma lista de frutas com random.choice(), e o boneco aparece na forca, uma parte por erro. Com tanta coisa, o programa ganha três funções: uma desenha, uma monta a máscara e uma lê a letra.
import random
PALAVRAS = ["abacaxi", "acerola", "banana", "caju", "goiaba",
"laranja", "melancia", "morango", "pitanga"]
MAX_ERROS = 6
def desenhar(erros):
"""Desenha a forca com as partes do boneco que já apareceram."""
cabeca = "O" if erros >= 1 else " "
tronco = "|" if erros >= 2 else " "
braco_esq = "/" if erros >= 3 else " "
braco_dir = "\\" if erros >= 4 else " "
perna_esq = "/" if erros >= 5 else " "
perna_dir = "\\" if erros >= 6 else " "
print(" +---+")
print(f" {cabeca} |")
print(f" {braco_esq}{tronco}{braco_dir} |")
print(f" {perna_esq} {perna_dir} |")
print(" |")
print("=======")
def mascarar(palavra, certas):
"""Devolve a palavra com _ no lugar das letras ainda não descobertas."""
mascara = ""
for letra in palavra:
if letra in certas:
mascara += letra + " "
else:
mascara += "_ "
return mascara.strip()
def ler_letra(tentadas):
"""Pergunta até a pessoa digitar uma letra que ainda não tentou."""
while True:
chute = input("Letra: ").strip().lower()
if len(chute) != 1 or not chute.isalpha():
print("Digite uma letra só.")
elif chute in tentadas:
print(f"Você já tentou a letra {chute}.")
else:
return chute
palavra = random.choice(PALAVRAS)
certas = []
erradas = []
print("=== Jogo da Forca: frutas ===")
while True:
desenhar(len(erradas))
mascara = mascarar(palavra, certas)
print(mascara)
if erradas:
print("Erradas:", ", ".join(erradas))
if "_" not in mascara:
print(f"Você venceu! A palavra era {palavra}.")
break
if len(erradas) == MAX_ERROS:
print(f"Você perdeu! A palavra era {palavra}.")
break
chute = ler_letra(certas + erradas)
if chute in palavra:
certas.append(chute)
print(f"Tem {palavra.count(chute)} letra(s) {chute}!")
else:
erradas.append(chute)
print(f"Não tem {chute}.")
| Peça | O que faz |
|---|---|
PALAVRAS = [ … ] | A lista de frutas, quebrada em duas linhas: dentro de colchetes, o Python deixa continuar na linha de baixo. Palavras sem acento, de propósito (veja Ideias para continuar). |
random.choice(PALAVRAS) | "Escolha": sorteia um item da lista. Cada fruta tem a mesma chance. |
cabeca = "O" if erros >= 1 else " " | Um if em uma linha: vale "O" se já houve 1 erro, senão um espaço. Cada parte do boneco tem a sua linha, e aparece a partir do seu número de erros. |
"\\" | A barra invertida sozinha. Dentro de um texto, \ começa um escape (como \n), então, para escrever uma barra, escrevem-se duas (veja Caracteres de Escape). |
return mascara.strip() | A função devolve a máscara, sem o espaço que sobrava no fim. |
ler_letra(certas + erradas) | O + entre duas listas cria uma lista nova com os itens das duas: todas as letras já tentadas. |
if erradas: | Uma lista vazia conta como False num if; com algum item, como True. Então: "se já há letras erradas, mostre-as". |
", ".join(erradas) | Junta os itens da lista num texto, com ", " entre eles: "e, o, r". |
palavra.count(chute) | Conta quantas vezes a letra aparece: "banana".count("a") é 3. |
$ python3
Python 3.14.4 (main, Aug 20 2026, 10:41:58) [GCC 15.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> "banana".count("a")
3
>>> ", ".join(["x", "z", "e"])
'x, z, e'
>>> bool([])
False
>>> bool(["x"])
True
>>> ["a", "b"] + ["x"]
['a', 'b', 'x']
>>> exit()
Uma partida perdida, para ver o boneco inteiro:
$ python3 forca.py
=== Jogo da Forca: frutas ===
+---+
|
|
|
|
=======
_ _ _ _ _ _ _
Letra: a
Tem 3 letra(s) a!
+---+
|
|
|
|
=======
a _ a _ a _ _
Letra: e
Não tem e.
+---+
O |
|
|
|
=======
a _ a _ a _ _
Erradas: e
Letra: o
Não tem o.
+---+
O |
| |
|
|
=======
a _ a _ a _ _
Erradas: e, o
Letra: r
Não tem r.
+---+
O |
/| |
|
|
=======
a _ a _ a _ _
Erradas: e, o, r
Letra: s
Não tem s.
+---+
O |
/|\ |
|
|
=======
a _ a _ a _ _
Erradas: e, o, r, s
Letra: i
Tem 1 letra(s) i!
+---+
O |
/|\ |
|
|
=======
a _ a _ a _ i
Erradas: e, o, r, s
Letra: n
Não tem n.
+---+
O |
/|\ |
/ |
|
=======
a _ a _ a _ i
Erradas: e, o, r, s, n
Letra: t
Não tem t.
+---+
O |
/|\ |
/ \ |
|
=======
a _ a _ a _ i
Erradas: e, o, r, s, n, t
Você perdeu! A palavra era abacaxi.
Como no Jogo de Adivinhação, a missão abaixo usa random.seed(5) antes do sorteio: com essa semente, o random.choice(PALAVRAS) escolhe sempre goiaba (no terminal e no editor), e a missão pode conferir o resultado. No jogo de verdade, sem semente.
Missão (versão 4): a lista PALAVRAS existe, mas a palavra ainda é sempre "banana". Troque essa linha por um sorteio com random.choice(PALAVRAS). Com a semente 5, a palavra sorteada é goiaba, e a entrada da missão (g, o, i, a, b) a descobre.
import random
random.seed(5) # só para a missão: o sorteio dá sempre goiaba
PALAVRAS = ["abacaxi", "acerola", "banana", "caju", "goiaba",
"laranja", "melancia", "morango", "pitanga"]
MAX_ERROS = 6
def desenhar(erros):
"""Desenha a forca com as partes do boneco que já apareceram."""
cabeca = "O" if erros >= 1 else " "
tronco = "|" if erros >= 2 else " "
braco_esq = "/" if erros >= 3 else " "
braco_dir = "\\" if erros >= 4 else " "
perna_esq = "/" if erros >= 5 else " "
perna_dir = "\\" if erros >= 6 else " "
print(" +---+")
print(f" {cabeca} |")
print(f" {braco_esq}{tronco}{braco_dir} |")
print(f" {perna_esq} {perna_dir} |")
print(" |")
print("=======")
def mascarar(palavra, certas):
"""Devolve a palavra com _ no lugar das letras ainda não descobertas."""
mascara = ""
for letra in palavra:
if letra in certas:
mascara += letra + " "
else:
mascara += "_ "
return mascara.strip()
def ler_letra(tentadas):
"""Pergunta até a pessoa digitar uma letra que ainda não tentou."""
while True:
chute = input("Letra: ").strip().lower()
if len(chute) != 1 or not chute.isalpha():
print("Digite uma letra só.")
elif chute in tentadas:
print(f"Você já tentou a letra {chute}.")
else:
return chute
palavra = "banana"
certas = []
erradas = []
print("=== Jogo da Forca: frutas ===")
while True:
desenhar(len(erradas))
mascara = mascarar(palavra, certas)
print(mascara)
if erradas:
print("Erradas:", ", ".join(erradas))
if "_" not in mascara:
print(f"Você venceu! A palavra era {palavra}.")
break
if len(erradas) == MAX_ERROS:
print(f"Você perdeu! A palavra era {palavra}.")
break
chute = ler_letra(certas + erradas)
if chute in palavra:
certas.append(chute)
print(f"Tem {palavra.count(chute)} letra(s) {chute}!")
else:
erradas.append(chute)
print(f"Não tem {chute}.")
import random
random.seed(5) # só para a missão: o sorteio dá sempre goiaba
PALAVRAS = ["abacaxi", "acerola", "banana", "caju", "goiaba",
"laranja", "melancia", "morango", "pitanga"]
MAX_ERROS = 6
def desenhar(erros):
"""Desenha a forca com as partes do boneco que já apareceram."""
cabeca = "O" if erros >= 1 else " "
tronco = "|" if erros >= 2 else " "
braco_esq = "/" if erros >= 3 else " "
braco_dir = "\\" if erros >= 4 else " "
perna_esq = "/" if erros >= 5 else " "
perna_dir = "\\" if erros >= 6 else " "
print(" +---+")
print(f" {cabeca} |")
print(f" {braco_esq}{tronco}{braco_dir} |")
print(f" {perna_esq} {perna_dir} |")
print(" |")
print("=======")
def mascarar(palavra, certas):
"""Devolve a palavra com _ no lugar das letras ainda não descobertas."""
mascara = ""
for letra in palavra:
if letra in certas:
mascara += letra + " "
else:
mascara += "_ "
return mascara.strip()
def ler_letra(tentadas):
"""Pergunta até a pessoa digitar uma letra que ainda não tentou."""
while True:
chute = input("Letra: ").strip().lower()
if len(chute) != 1 or not chute.isalpha():
print("Digite uma letra só.")
elif chute in tentadas:
print(f"Você já tentou a letra {chute}.")
else:
return chute
palavra = random.choice(PALAVRAS)
certas = []
erradas = []
print("=== Jogo da Forca: frutas ===")
while True:
desenhar(len(erradas))
mascara = mascarar(palavra, certas)
print(mascara)
if erradas:
print("Erradas:", ", ".join(erradas))
if "_" not in mascara:
print(f"Você venceu! A palavra era {palavra}.")
break
if len(erradas) == MAX_ERROS:
print(f"Você perdeu! A palavra era {palavra}.")
break
chute = ler_letra(certas + erradas)
if chute in palavra:
certas.append(chute)
print(f"Tem {palavra.count(chute)} letra(s) {chute}!")
else:
erradas.append(chute)
print(f"Não tem {chute}.")
Versão 5 · As palavras num arquivo
Para trocar as palavras, hoje é preciso mexer no código. Muito melhor guardá-las num arquivo de texto, palavras.txt, uma por linha, na mesma pasta do jogo. Qualquer pessoa edita o arquivo (no editor de textos) e o jogo muda, sem tocar no programa. A Ana fez um com animais:
$ cat palavras.txt capivara jabuti tucano tatu papagaio macaco
No programa, a lista PALAVRAS some, entra o nome do arquivo e uma função nova, carregar_palavras. As outras três funções ficam iguais:
import random
ARQUIVO = "palavras.txt"
MAX_ERROS = 6
def carregar_palavras(nome_arquivo):
"""Lê o arquivo (uma palavra por linha) e devolve a lista de palavras."""
palavras = []
with open(nome_arquivo, encoding="utf-8") as arquivo:
for linha in arquivo:
linha = linha.strip().lower()
if linha:
palavras.append(linha)
return palavras
# desenhar, mascarar e ler_letra: iguais à versão 4
palavras = carregar_palavras(ARQUIVO)
palavra = random.choice(palavras)
certas = []
erradas = []
print("=== Jogo da Forca ===")
# … o laço principal, igual à versão 4 …
- with open(nome_arquivo, encoding="utf-8") as arquivo:
- Abre o arquivo para leitura e o chama de
arquivodentro do bloco. Owithfecha o arquivo sozinho no fim do bloco, mesmo se der erro; oencoding="utf-8"garante que acentos sejam lidos certo. Veja Abrindo Arquivos. - for linha in arquivo:
- Um
forsobre um arquivo aberto passa por cada linha. Cada linha vem com o\ndo fim (veja Lendo Arquivos). - linha = linha.strip().lower()
- Tira o
\ne espaços, e passa para minúscula:"Tatu \n"vira"tatu". - if linha:
- Um texto vazio conta como
False: pula as linhas em branco do arquivo (uma linha vazia no fim é comum). - return palavras
- Devolve a lista pronta, no mesmo formato da antiga
PALAVRAS. Por isso o resto do programa nem percebe a mudança.
$ python3 forca.py
=== Jogo da Forca ===
+---+
|
|
|
|
=======
_ _ _ _ _ _
Letra: e
Não tem e.
+---+
O |
|
|
|
=======
_ _ _ _ _ _
Erradas: e
Letra: a
Tem 2 letra(s) a!
+---+
O |
|
|
|
=======
_ a _ a _ _
Erradas: e
Letra: c
Tem 2 letra(s) c!
+---+
O |
|
|
|
=======
_ a c a c _
Erradas: e
Letra: m
Tem 1 letra(s) m!
+---+
O |
|
|
|
=======
m a c a c _
Erradas: e
Letra: o
Tem 1 letra(s) o!
+---+
O |
|
|
|
=======
m a c a c o
Erradas: e
Você venceu! A palavra era macaco.
Quando dá errado na versão 5
FileNotFoundError
$ mv palavras.txt guardado.txt
$ python3 forca.py
Traceback (most recent call last):
File "/home/ana/projetos/forca/forca.py", line 57, in <module>
palavras = carregar_palavras(ARQUIVO)
File "/home/ana/projetos/forca/forca.py", line 10, in carregar_palavras
with open(nome_arquivo, encoding="utf-8") as arquivo:
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'palavras.txt'
(As linhas 57 e 10 são as do arquivo completo, com as quatro funções.) Este traceback tem dois trechos File …, e é preciso lê-los de cima para baixo como um caminho: a linha 57 (no programa principal, <module>) chamou carregar_palavras; dentro dela, na linha 10, o open falhou. A última linha diz o motivo: FileNotFoundError: [Errno 2] No such file or directory: 'palavras.txt' = "arquivo não encontrado: não existe esse arquivo ou pasta". O Python procura o arquivo na pasta de onde o programa foi rodado. Como consertar: rode o jogo de dentro da pasta onde está o palavras.txt (ou devolva o arquivo para lá).
IndexError
$ touch palavras.txt
$ python3 forca.py
Traceback (most recent call last):
File "/home/ana/projetos/forca/forca.py", line 58, in <module>
palavra = random.choice(palavras)
File "/usr/lib/python3.14/random.py", line 353, in choice
raise IndexError('Cannot choose from an empty sequence')
IndexError: Cannot choose from an empty sequence
O touch criou um palavras.txt vazio. A leitura deu certo (uma lista vazia), mas sortear de uma lista vazia é impossível: IndexError: Cannot choose from an empty sequence = "não dá para escolher de uma sequência vazia". Repare no segundo File: o erro aconteceu dentro do módulo random (no arquivo random.py do próprio Python). Isso não quer dizer que o Python está com defeito: siga o traceback até a última linha que é sua (a 58), que é onde está a causa. Como consertar: confira a lista antes de sortear: if not palavras: print("O arquivo palavras.txt está vazio."); sys.exit(1) (o sys.exit está na aula Os Módulos os e sys).
Na caixa abaixo, o palavras.txt aparece numa aba ao lado do programa: ele fica na mesma pasta, e você pode editá-lo.
Missão (versão 5): o jogo ainda usa a lista de frutas. Escreva a função carregar_palavras(nome_arquivo) como na versão 5 (com with open(…)) e troque o sorteio por random.choice(carregar_palavras(ARQUIVO)). Com a semente 7 e o arquivo da aba palavras.txt, a palavra é tucano, e a entrada da missão a descobre.
import random
random.seed(7) # só para a missão: com o palavras.txt da aba, dá tucano
ARQUIVO = "palavras.txt"
PALAVRAS = ["abacaxi", "acerola", "banana", "caju", "goiaba",
"laranja", "melancia", "morango", "pitanga"]
MAX_ERROS = 6
def desenhar(erros):
"""Desenha a forca com as partes do boneco que já apareceram."""
cabeca = "O" if erros >= 1 else " "
tronco = "|" if erros >= 2 else " "
braco_esq = "/" if erros >= 3 else " "
braco_dir = "\\" if erros >= 4 else " "
perna_esq = "/" if erros >= 5 else " "
perna_dir = "\\" if erros >= 6 else " "
print(" +---+")
print(f" {cabeca} |")
print(f" {braco_esq}{tronco}{braco_dir} |")
print(f" {perna_esq} {perna_dir} |")
print(" |")
print("=======")
def mascarar(palavra, certas):
"""Devolve a palavra com _ no lugar das letras ainda não descobertas."""
mascara = ""
for letra in palavra:
if letra in certas:
mascara += letra + " "
else:
mascara += "_ "
return mascara.strip()
def ler_letra(tentadas):
"""Pergunta até a pessoa digitar uma letra que ainda não tentou."""
while True:
chute = input("Letra: ").strip().lower()
if len(chute) != 1 or not chute.isalpha():
print("Digite uma letra só.")
elif chute in tentadas:
print(f"Você já tentou a letra {chute}.")
else:
return chute
palavra = random.choice(PALAVRAS)
certas = []
erradas = []
print("=== Jogo da Forca ===")
while True:
desenhar(len(erradas))
mascara = mascarar(palavra, certas)
print(mascara)
if erradas:
print("Erradas:", ", ".join(erradas))
if "_" not in mascara:
print(f"Você venceu! A palavra era {palavra}.")
break
if len(erradas) == MAX_ERROS:
print(f"Você perdeu! A palavra era {palavra}.")
break
chute = ler_letra(certas + erradas)
if chute in palavra:
certas.append(chute)
print(f"Tem {palavra.count(chute)} letra(s) {chute}!")
else:
erradas.append(chute)
print(f"Não tem {chute}.")
capivara jabuti tucano tatu papagaio macaco
import random
random.seed(7) # só para a missão: com o palavras.txt da aba, dá tucano
ARQUIVO = "palavras.txt"
MAX_ERROS = 6
def carregar_palavras(nome_arquivo):
"""Lê o arquivo (uma palavra por linha) e devolve a lista de palavras."""
palavras = []
with open(nome_arquivo, encoding="utf-8") as arquivo:
for linha in arquivo:
linha = linha.strip().lower()
if linha:
palavras.append(linha)
return palavras
def desenhar(erros):
"""Desenha a forca com as partes do boneco que já apareceram."""
cabeca = "O" if erros >= 1 else " "
tronco = "|" if erros >= 2 else " "
braco_esq = "/" if erros >= 3 else " "
braco_dir = "\\" if erros >= 4 else " "
perna_esq = "/" if erros >= 5 else " "
perna_dir = "\\" if erros >= 6 else " "
print(" +---+")
print(f" {cabeca} |")
print(f" {braco_esq}{tronco}{braco_dir} |")
print(f" {perna_esq} {perna_dir} |")
print(" |")
print("=======")
def mascarar(palavra, certas):
"""Devolve a palavra com _ no lugar das letras ainda não descobertas."""
mascara = ""
for letra in palavra:
if letra in certas:
mascara += letra + " "
else:
mascara += "_ "
return mascara.strip()
def ler_letra(tentadas):
"""Pergunta até a pessoa digitar uma letra que ainda não tentou."""
while True:
chute = input("Letra: ").strip().lower()
if len(chute) != 1 or not chute.isalpha():
print("Digite uma letra só.")
elif chute in tentadas:
print(f"Você já tentou a letra {chute}.")
else:
return chute
palavra = random.choice(carregar_palavras(ARQUIVO))
certas = []
erradas = []
print("=== Jogo da Forca ===")
while True:
desenhar(len(erradas))
mascara = mascarar(palavra, certas)
print(mascara)
if erradas:
print("Erradas:", ", ".join(erradas))
if "_" not in mascara:
print(f"Você venceu! A palavra era {palavra}.")
break
if len(erradas) == MAX_ERROS:
print(f"Você perdeu! A palavra era {palavra}.")
break
chute = ler_letra(certas + erradas)
if chute in palavra:
certas.append(chute)
print(f"Tem {palavra.count(chute)} letra(s) {chute}!")
else:
erradas.append(chute)
print(f"Não tem {chute}.")
Formas diferentes de montar a máscara
O for com += das versões 1 a 4 é o jeito mais fácil de entender. Programadores experientes costumam escrever a mesma coisa numa linha só, com join e o if em uma linha dentro de uma list comprehension:
$ python3 Python 3.14.4 (main, Aug 20 2026, 10:41:58) [GCC 15.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> palavra = "banana" >>> certas = ["a", "n"] >>> " ".join([letra if letra in certas else "_" for letra in palavra]) '_ a n a n a' >>> " ".join(letra if letra in certas else "_" for letra in palavra) '_ a n a n a' >>> exit()
Lendo de dentro para fora: "para cada letra da palavra, use a letra se ela estiver em certas, senão "_""; depois, o " ".join junta tudo com espaços (e já sem o espaço sobrando no fim). A segunda forma, sem os colchetes, dá o mesmo resultado: quando a "comprehension" é o único argumento da função, os colchetes podem sair (ela vira uma expressão geradora, assunto da aula Geradores).
| Forma | Quando usar |
|---|---|
for com mascara += … | Quando você está aprendendo, ou quando cada passo precisa de mais de uma linha. É a forma mais clara. |
" ".join([… for letra in palavra]) | Quando a regra cabe numa linha e você já lê comprehensions com facilidade. |
Listas (certas, erradas) | Guardam a ordem das tentativas, que o jogo mostra em "Erradas: e, o, r". |
Um conjunto (set()) | Se a ordem não importasse: o in num conjunto é mais rápido e ele nunca repete itens. Numa forca, com poucas letras, tanto faz. |
Ideias para continuar
- Palavras com acento (maçã, pêssego): do jeito que está, a pessoa teria de digitar
ãeê. Uma saída é comparar sem acentos, com o módulounicodedatada biblioteca do Python. - Chutar a palavra inteira: se o chute tiver mais de uma letra, compare com a palavra; acertou, venceu; errou, perde duas vidas.
- Categorias: um arquivo por tema (
frutas.txt,animais.txt), e o jogo pergunta qual tema a pessoa quer. - Jogar de novo, com placar de vitórias e derrotas, como no Jogo de Adivinhação.
Referência rápida do projeto
| Escrita | Para que serviu |
|---|---|
for letra in palavra: | Passar por cada letra do texto |
mascara += letra + " " | Montar a máscara aos poucos (textos não mudam por dentro: cria-se um novo) |
letra in certas / "_" not in mascara | Perguntar se está (ou não está) dentro |
certas.append(chute) | Guardar a letra na lista |
len(erradas) | Contar os erros |
.strip().lower(), len(chute) != 1, .isalpha() | Aceitar só uma letra, em minúscula |
continue | Recusar o chute sem contar nada |
"O" if erros >= 1 else " " | Escolher entre dois valores numa linha |
"\\" | Uma barra invertida dentro de um texto |
random.choice(lista) | Sortear um item da lista |
", ".join(erradas), palavra.count(letra) | Juntar a lista num texto; contar uma letra |
with open(nome, encoding="utf-8") as arquivo: | Ler as palavras do arquivo, linha por linha |
| Sintoma | Causa provável |
|---|---|
TypeError: 'str' object does not support item assignment | Tentou trocar uma letra de um texto (palavra[0] = "B"): monte um texto novo |
| Letra maiúscula conta como erro | Faltou o .lower() |
| Só Enter (ou duas letras) dá "Tem!" | "" in "banana" é True: exija len(chute) == 1 |
| Repetir uma letra errada gasta outra vida | Faltou o teste "já tentou" |
| O jogo nunca termina com 6 erros | Faltou o if len(erradas) == MAX_ERROS: … break |
EOFError numa missão | A entrada da missão acabou e o programa ainda pedia letras |
FileNotFoundError: … 'palavras.txt' | O arquivo não está na pasta de onde o programa foi rodado |
IndexError: Cannot choose from an empty sequence | random.choice com uma lista vazia (arquivo vazio) |
Exercícios
1. Guardar a letra
Complete o método que acrescenta o chute no fim da lista:
2. Ainda tem tracinho?
Complete para testar se não sobrou nenhum _ na máscara:
3. Sortear da lista
Complete a função do módulo random que sorteia um item da lista:
4. Na versão 2, a Ana apertou só Enter e o jogo respondeu "Tem!". Por quê?
5. Por que a versão 1 monta a máscara de novo a cada volta, em vez de trocar o _ pela letra?
6. Para escrever uma barra invertida \ num texto do Python, como se escreve?
Resumo
- Um
forsobre um texto passa letra por letra; com+=, dá para montar outro texto aos poucos. - Textos são imutáveis: para "mudar" um, crie um novo. Listas mudam:
appendacrescenta. - O
inpergunta "está dentro?"; entre textos, procura um pedaço (e o texto vazio está em todos). len(erradas)conta os erros; o teste de derrota vem antes de pedir a próxima letra..strip().lower(),len(chute) == 1e.isalpha()garantem uma letra só;continuerecusa sem contar.random.choicesorteia um item de uma lista;random.seeddeixa o sorteio previsível para testar.- Guardar os dados num arquivo (
palavras.txt) separa o jogo do conteúdo. Leia comwith open(…, encoding="utf-8").