- Home
- iThink
- Programmazione per Ragazzi
- Il gioco dell'impiccato in Python
Il gioco dell'impiccato in Python
Divento Programmatore — Capitolo 5: L'Impiccato Digitale
Obiettivi di Apprendimento
Al termine di questo capitolo avrai costruito un gioco completo dell'Impiccato Digitale e imparerai a:
- Manipolare le stringhe in modo avanzato usando indici e metodi come
.upper() - Usare l'operatore
inper verificare se un carattere o una sottostringa fa parte di una stringa - Gestire strutture dati di tipo
set(insiemi) per registrare le lettere uniche provate dal giocatore - Costruire disegni dinamici in ASCII Art basati su liste multilinea
- Leggere file esterni (
parole.txt) in modo sicuro gestendo l'eccezioneFileNotFoundError
Termini Fondamentali
| Termine | Simbolo | Significato | | --------- | --------- | ------------- | | Stringa (string) | 🔠 | Sequenza ordinata di caratteri tipografici. In Python le stringhe sono immutabili (non si possono modificare i singoli caratteri al loro interno direttamente) | | Operatore in | 🔍 | Parola chiave di Python usata per controllare se un elemento è presente all'interno di una collezione (come una lista, un set o una stringa) | | Insieme (set) | 🧺 | Collezione disordinata di elementi unici (non può contenere duplicati), ideale per tenere traccia delle lettere già provate | | ASCII Art | 🎨 | Tecnica di design che consiste nel comporre immagini usando solo i caratteri standard disponibili sulla tastiera | | FileNotFoundError | 📂 | Eccezione sollevata da Python quando si tenta di aprire o leggere un file che non esiste nel percorso indicato |
La Storia: Il gioco delle spie e i primi pixel
Sebbene le origini precise del gioco dell'impiccato si siano perse nel tempo, le prime tracce scritte risalgono all'epoca vittoriana nel Regno Unito, dove veniva chiamato Gallows (il patibolo) o Hanger. Era considerato un passatempo didattico per aiutare i bambini a migliorare l'ortografia e il vocabolario usando una semplice lavagnetta di ardesia.
Durante la Guerra Fredda, il gioco assunse una veste più misteriosa: gli addetti alla crittografia e alla sicurezza lo usavano come metafora per spiegare come fosse possibile indovinare una chiave di cifratura segreta provando lettere una alla volta.
Nel 1978, con la nascita dei primi personal computer dotati di monitor grafici (come l'Apple II o il Commodore PET), i programmatori scrissero le prime versioni digitali del gioco. Non c'erano motori 3D o schede video avanzate, perciò per mostrare il pupazzo dell'impiccato si ricorreva alla ASCII Art: piccoli disegni stilizzati composti da trattini, barre e lettere stampati sulla console. Oggi imparerai proprio a programmare una di queste storiche interfacce testuali!
Concept 1: Le stringhe sono come treni di caratteri (e sono immutabili!)
In Python, una stringa come "PYTHON" è a tutti gli effetti una sequenza di caratteri ordinata. Puoi pensare a ogni carattere come a un vagone di un treno, identificato da un numero di posizione (indice) che parte da 0.
Indice: 0 1 2 3 4 5
Carattere: P Y T H O N
Puoi estrarre un singolo carattere specificando il suo indice tra parentesi quadre: lettera = "PYTHON"[2] restituisce 'T'
Tuttavia, c'è un dettaglio fondamentale: le stringhe in Python sono immutabili. Questo significa che se provi a scrivere:
parola = "PYTHON"
parola[0] = "J" # ERRORE! Python solleverà un TypeError
Non puoi cambiare un vagone del treno sul posto. Se vuoi modificare una stringa, devi costruirne una completamente nuova concatenando i pezzi o usando metodi come .replace()!
Concept 2: ASCII Art e Liste Multilinea
Nel gioco dell'impiccato, il disegno del patibolo cambia a ogni errore commesso dal giocatore. Come possiamo rappresentare questi disegni in Python? Possiamo inserire intere stringhe multilinea (delimitate da tre virgolette """) all'interno di una lista ordinata!
STADI_IMPICCATO = [
"""
+---+
| |
|
=========
""",
"""
+---+
| |
O |
=========
"""
]
Se il giocatore ha commesso 0 errori, stamperemo STADI_IMPICCATO[0]. Se ha commesso 1 errore, stamperemo STADI_IMPICCATO[1]. La corrispondenza tra il numero di errori e l'indice della lista rende la visualizzazione del disegno dinamica e incredibilmente semplice da gestire!
Costruiamo il Gioco: Versione Base
Ecco la versione base con una parola segreta fissa e una meccanica di gioco minimale:
parola_segreta = "CODICE"
lettere_indovinate = []
tentativi_rimasti = 6
print("🎮 BENVENUTO ALL'IMPICCATO BASE 🎮\n")
while tentativi_rimasti > 0:
# Mostriamo lo stato corrente della parola (es: _ O _ I _ E)
stato = []
for lettera in parola_segreta:
if lettera in lettere_indovinate:
stato.append(lettera)
else:
stato.append("_")
print("Parola:", " ".join(stato))
# Controlliamo se il giocatore ha vinto
if "_" not in stato:
print("\n🎉 Complimenti! Hai vinto!")
break
lettera_scelta = input("\nInserisci una lettera: ").upper()
if lettera_scelta in parola_segreta:
print("✅ Colpita!")
lettere_indovinate.append(lettera_scelta)
else:
print("❌ Mancata!")
tentativi_rimasti -= 1
print(f"Tentativi rimasti: {tentativi_rimasti}")
if tentativi_rimasti == 0:
print(f"\n💀 Hai perso! La parola era: {parola_segreta}")
Sfide: Fai salire di livello il tuo Impiccato
Sfida 1 — Evitare i Doppioni
Se il giocatore inserisce una lettera che ha già tentato in precedenza, il gioco deve segnalarglielo senza fargli sprecare un tentativo (sia che la lettera fosse corretta, sia che fosse errata).
Sfida 2 — Disegno ASCII Dinamico
Integra la lista STADI_IMPICCATO vista nel Concept 2 per disegnare graficamente il patibolo e l'impiccato che si compone riga dopo riga a ogni errore.
Sfida 3 — Caricamento da file esterno (parole.txt)
Fai in modo che il gioco non usi una parola fissa o una lista interna, ma carichi un vocabolario di parole da un file di testo chiamato parole.txt in cui ogni riga contiene una parola e il relativo indizio nel formato PAROLA:INDIZIO. Gestisci con try/except il caso in cui il file non esista per evitare crash!
Sfida 4 — Suggerimenti di Categoria
Mostra a schermo l'indizio (la categoria o la descrizione della parola) caricato dal file parole.txt per aiutare il giocatore a indovinare le parole più lunghe e complesse.
Il Codice Completo con Tutte le Sfide
Salva questo codice in Thonny come impiccato_digitale.py e crea nella stessa cartella un file chiamato parole.txt contenente alcune parole e definizioni per testare il gioco!
import random
import os
# Fasi grafiche dell'impiccato in ASCII Art
STADI_IMPICCATO = [
"""
+---+
| |
|
|
|
|
=========
""",
"""
+---+
| |
O |
|
|
|
=========
""",
"""
+---+
| |
O |
| |
|
|
=========
""",
"""
+---+
| |
O |
/| |
|
|
=========
""",
"""
+---+
| |
O |
/|\ |
|
|
=========
""",
"""
+---+
| |
O |
/|\ |
/ |
|
=========
""",
"""
+---+
| |
O |
/|\ |
/ \ |
|
=========
"""
]
# Parole di riserva qualora il file esterno non sia presente
PAROLE_DEFAULT = {
"PYTHON": "Un linguaggio di programmazione fantastico",
"COMPUTER": "La macchina su cui stai scrivendo codice",
"MATRICE": "Una lista di liste ordinata in righe e colonne",
"FUNZIONE": "Un blocco di codice riutilizzabile con def",
"VARIABILE": "Una scatola in memoria con un nome ed un valore"
}
def carica_parole(nome_file="parole.txt"):
"""Sfida 3: Carica le parole da un file esterno.
Se il file non esiste, crea un file di esempio e usa le parole di fallback."""
parole = {}
if not os.path.exists(nome_file):
try:
with open(nome_file, "w", encoding="utf-8") as f:
for k, v in PAROLE_DEFAULT.items():
f.write(f"{k}:{v}\n")
except IOError:
pass
return PAROLE_DEFAULT
try:
with open(nome_file, "r", encoding="utf-8") as f:
for riga in f:
riga = riga.strip()
if ":" in riga:
parola, indizio = riga.split(":", 1)
parole[parola.strip().upper()] = indizio.strip()
except Exception:
return PAROLE_DEFAULT
return parole if parole else PAROLE_DEFAULT
def leggi_lettera(lettere_tentate):
"""Legge una lettera valida dall'utente evitando input doppi o non validi."""
while True:
scelta = input("Inserisci una lettera: ").strip().upper()
if len(scelta) != 1:
print("🤖 Inserisci una singola lettera alla volta!")
elif not scelta.isalpha():
print("🤖 Inserisci solo lettere dell'alfabeto!")
elif scelta in lettere_tentate:
print(f"🤖 Hai già inserito la lettera '{scelta}'! Riprova.")
else:
return scelta
def mostra_parola_nascosta(parola, lettere_indovinate):
"""Rappresenta la parola oscurando le lettere non indovinate con '_'."""
visualizzazione = []
for lettera in parola:
if lettera in lettere_indovinate:
visualizzazione.append(lettera)
else:
visualizzazione.append("_")
return " ".join(visualizzazione)
def gioca():
dizionario_parole = carica_parole()
parola_segreta = random.choice(list(dizionario_parole.keys()))
suggerimento = dizionario_parole[parola_segreta]
lettere_indovinate = set()
lettere_tentate = set()
errori = 0
max_errori = len(STADI_IMPICCATO) - 1
print("╔══════════════════════════════════════╗")
print("║ 🎮 L'IMPICCATO DIGITALE 🎮 ║")
print("║ Indovina la parola prima della fine ║")
print("╚══════════════════════════════════════╝")
while errori < max_errori:
print(STADI_IMPICCATO[errori])
print(f"Suggerimento: {suggerimento}")
print(f"Parola da indovinare: {mostra_parola_nascosta(parola_segreta, lettere_indovinate)}")
# Mostriamo lo storico delle lettere provate per facilitare il gioco
if lettere_tentate:
print(f"Lettere provate: {', '.join(sorted(lettere_tentate))}")
print(f"Errori commessi: {errori}/{max_errori}")
lettera = leggi_lettera(lettere_tentate)
lettere_tentate.add(lettera)
if lettera in parola_segreta:
print("✅ Ottimo! La lettera è presente nella parola.")
lettere_indovinate.add(lettera)
# Controlliamo se il giocatore ha indovinato tutte le lettere
if set(parola_segreta).issubset(lettere_indovinate):
print(f"\n🎉 VITTORIA! Hai indovinato la parola '{parola_segreta}'! 🎉")
return
else:
print("❌ Peccato! La lettera non è presente.")
errori += 1
# Se il giocatore esaurisce i tentativi
print(STADI_IMPICCATO[errori])
print(f"💀 GAME OVER! Sei stato impiccato. La parola era: '{parola_segreta}'")
if __name__ == "__main__":
gioca()
Analisi del Codice Completo
Analizziamo da vicino come funzionano le parti chiave del nostro codice:
1. L'uso efficiente dei Set (Insiemi)
Per tenere traccia delle lettere indovinate e provate, utilizziamo set(). A differenza delle liste, i set non ordinano gli elementi e non consentono duplicati. In più, supportano operazioni matematiche veloci come set(parola_segreta).issubset(lettere_indovinate), che controlla istantaneamente se tutte le lettere uniche della parola da indovinare fanno parte del set di lettere indovinate dal giocatore.
2. Operazione di Split su File (split(":", 1))
Nella funzione carica_parole, leggiamo ogni riga del file parole.txt e la dividiamo usando il metodo .split(":", 1). Il secondo parametro 1 indica a Python di dividere la stringa solo in corrispondenza del primo due punti : incontrato. Questo ci permette di avere indizi che contengono al loro interno altri due punti (es: CODICE:Rappresenta le righe scritte in Python: un gioco da ragazzi!) senza rompere il programma.
3. Il blocco di autoconvalida leggi_lettera
Questa funzione verifica tre condizioni fondamentali per blindare l'input:
len(scelta) != 1: Impedisce all'utente di premere invio a vuoto o di inserire intere parole per barare.not scelta.isalpha(): Esclude numeri, simboli o spazi vuoti.scelta in lettere_tentate: Riconosce i doppioni e protegge il giocatore dallo sprecare vite inutilmente.
Curiosità: La frequenza delle lettere
Lo sapevi che la crittografia moderna fa ampio uso del concetto di frequenza delle lettere? In lingua italiana, la lettera più comune nei testi scritti è la E, seguita da A e I, mentre le meno frequenti sono la Q e le lettere straniere come X e Y. Nel gioco dell'impiccato, iniziare provando le vocali più frequenti è una strategia matematica collaudata!
Provaci tu! — Parole infinite
Modifica il file parole.txt sul tuo computer aprendolo con Blocco Note o TextEdit. Aggiungi 10 nuove parole a tua scelta con i rispettivi indizi (ricordati di seguire il formato PAROLA:INDIZIO, una per riga) e avvia nuovamente il programma in Thonny per vedere le nuove sfide comparire a schermo!
Il Piccolo Glossario
| Termine | Definizione | | --------- | ------------- | | Immutable / Immutabile | Un oggetto il cui stato o valore interno non può essere modificato dopo la sua creazione (es: le stringhe in Python). | | Set / Insieme | Una collezione disordinata di elementi univoci. | | In | Operatore logico che restituisce True se un valore è presente all'interno di una collezione, False altrimenti. | | .upper() | Metodo che converte tutti i caratteri minuscoli di una stringa in maiuscolo. | | .isalpha() | Metodo delle stringhe che restituisce True se la stringa contiene solo lettere dell'alfabeto e non è vuota. | | split(separatore, numero_split) | Metodo che suddivide una stringa in una lista di sottostringhe in base al separatore indicato. | | os.path.exists(path) | Funzione della libreria di sistema os che verifica l'effettiva presenza di un file su disco prima di tentare di aprirlo. |
Prossimo capitolo
Capitolo 6: Arte con il Computer
Turtle graphics, coordinate cartesiane (0,0 centro), penup/pendown/goto, cicli geometrici spirografo, speed(0), modulo % per colori ciclici, begin_fill/end_fill, hideturtle. Codice completo con 4 sfide: stella 5 punte, colori arcobaleno, spirografo matematico, opera astratta.
Questo post è un estratto dal libro "Divento Programmatore: Python e i Giochi" (Volume 2 della collana "Cervello Elettronico") di Rocco Balzamà. Target: 10-14 anni · ~180 pagine · Thonny + VS Code.
Per approfondire
- Liste e dizionari in Python con esempi pratici
- Moduli e librerie standard di Python
- Calcolatrice parlante in Python con le funzioni
- Corsi e percorsi di programmazione