1 15/05/2026 9 min

Crea il tuo primo gioco: Tris in Python per principianti

Il Tris è un ottimo primo progetto in Python perché mette insieme variabili, cicli, funzioni, liste e gestione dell’input senza costringerti a studiare librerie esterne. La logica è semplice, ma abbastanza completa da farti vedere come si costruisce un programma interattivo vero: stato del gioco, validazione dei dati, controllo della vittoria, pareggio e ripartenza. Se lo scrivi bene una volta, poi puoi riutilizzare la struttura per giochi più complessi.

L’obiettivo qui non è solo “far funzionare” il gioco. L’obiettivo è scriverlo in modo leggibile, separando le responsabilità: una funzione stampa la griglia, una controlla il vincitore, una gestisce la mossa dell’utente, una coordina il turno corrente. Questo approccio evita il classico script tutto dentro un unico while lungo cento righe, difficile da leggere e ancora più difficile da correggere.

Requisiti minimi e ambiente di lavoro

Ti basta Python 3.10 o superiore. Il codice che segue funziona anche su versioni precedenti, ma usare una versione recente ti evita sorprese con il sistema e con eventuali estensioni future. Per verificare la versione installata:

python3 --version

Se il comando non esiste, su Linux puoi installarlo dal gestore pacchetti della tua distribuzione. Su Windows e macOS, assicurati di avere Python aggiunto al PATH. Non serve un IDE specifico: un editor semplice va bene, purché mostri bene l’indentazione. In Python l’indentazione non è estetica, è sintassi.

Struttura del gioco: quello che dobbiamo tenere in memoria

Il campo del Tris è una griglia 3x3. La rappresentiamo con una lista di nove elementi, uno per casella. Ogni elemento può contenere uno spazio vuoto, X o O. La scelta della lista piatta, invece di una lista di liste, semplifica il controllo degli indici e rende il codice più corto. Per un principiante è anche più facile da leggere.

La corrispondenza tra numeri e caselle può essere questa:

1 2 3

4 5 6

7 8 9

All’inizio è utile mostrare i numeri al posto delle caselle vuote, così il giocatore capisce subito dove inserire la mossa. Dopo ogni turno, la griglia viene ristampata con i simboli aggiornati.

Il codice completo del Tris

Qui sotto trovi una versione completa, lineare e facile da estendere. Funziona da terminale e permette a due giocatori di sfidarsi sulla stessa macchina.

def stampa_griglia(griglia):
    print()
    for riga in range(3):
        base = riga * 3
        valori = []
        for colonna in range(3):
            i = base + colonna
            if griglia[i] == " ":
                valori.append(str(i + 1))
            else:
                valori.append(griglia[i])
        print(f" {valori[0]} | {valori[1]} | {valori[2]} ")
        if riga < 2:
            print("---+---+---")
    print()


def controlla_vittoria(griglia, simbolo):
    combinazioni = [
        (0, 1, 2), (3, 4, 5), (6, 7, 8),
        (0, 3, 6), (1, 4, 7), (2, 5, 8),
        (0, 4, 8), (2, 4, 6),
    ]
    for a, b, c in combinazioni:
        if griglia[a] == griglia[b] == griglia[c] == simbolo:
            return True
    return False


def partita_pareggiata(griglia):
    return all(casella != " " for casella in griglia)


def chiedi_mossa(griglia, simbolo):
    while True:
        scelta = input(f"Giocatore {simbolo}, scegli una casella (1-9): ").strip()
        if not scelta.isdigit():
            print("Inserisci solo un numero da 1 a 9.")
            continue

        posizione = int(scelta)
        if posizione < 1 or posizione > 9:
            print("La casella deve essere compresa tra 1 e 9.")
            continue

        indice = posizione - 1
        if griglia[indice] != " ":
            print("Casella già occupata, scegli un'altra posizione.")
            continue

        return indice


def gioca():
    griglia = [" "] * 9
    turno = "X"

    print("Benvenuto al Tris!")
    stampa_griglia(griglia)

    while True:
        indice = chiedi_mossa(griglia, turno)
        griglia[indice] = turno
        stampa_griglia(griglia)

        if controlla_vittoria(griglia, turno):
            print(f"Il giocatore {turno} ha vinto!")
            break

        if partita_pareggiata(griglia):
            print("Pareggio!")
            break

        turno = "O" if turno == "X" else "X"


if __name__ == "__main__":
    gioca()

Come funziona il programma, pezzo per pezzo

1. La griglia come lista

La riga griglia = [" "] * 9 crea nove celle vuote. È una scelta semplice ma efficace: ogni volta che un giocatore inserisce una mossa, sostituisci il valore della posizione con X o O. In questo modo lo stato del gioco è sempre in un’unica variabile, facile da passare alle funzioni.

Se invece volessi una versione più avanzata, potresti usare una lista di liste per riflettere meglio la forma visiva del tabellone. Non è necessario per iniziare. Qui conta imparare la logica, non complicare il modello dati.

2. La stampa della griglia

La funzione stampa_griglia costruisce ogni riga e mostra i numeri delle caselle libere. Questo dettaglio migliora l’usabilità: il giocatore non deve indovinare gli indici interni del programma. Se la casella è vuota, stampiamo il numero corrispondente; se è occupata, stampiamo il simbolo presente.

La linea di separazione ---+---+--- è solo presentazione, ma aiuta molto a leggere il tabellone. Nei giochi da terminale la chiarezza visiva vale più di mille commenti.

3. Il controllo della vittoria

Il Tris si vince completando una delle otto combinazioni possibili: tre righe, tre colonne e due diagonali. La funzione controlla_vittoria scorre una lista di terne di indici. Se trova una combinazione in cui tutte e tre le celle contengono lo stesso simbolo, restituisce True.

Questa è una buona abitudine di programmazione: rappresentare i casi possibili in una struttura dati, invece di scrivere una lunga sequenza di if ripetuti. Il codice resta più corto e più facile da modificare. Se domani volessi cambiare il gioco o aggiungere una variante, sapresti subito dove intervenire.

4. Il pareggio

Il pareggio si verifica quando tutte le caselle sono occupate e nessuno ha vinto. La funzione partita_pareggiata usa all() per controllare che non ci siano spazi vuoti. È una soluzione più leggibile rispetto a un ciclo manuale con contatori, e per questo vale la pena impararla presto.

Attenzione all’ordine dei controlli: prima verifica la vittoria, poi il pareggio. Se inverti l’ordine, rischi di dichiarare pareggio una partita che in realtà è stata vinta nell’ultima mossa. È un errore classico nei giochi a turni.

5. La validazione dell’input

La funzione chiedi_mossa è la parte più importante per l’esperienza d’uso. Non basta chiedere un numero: bisogna anche accettare solo input validi, impedire posizioni fuori range e bloccare le caselle già occupate. Se il programma accetta dati errati, il gioco si rompe o diventa frustrante.

Il controllo scelta.isdigit() evita problemi con stringhe non numeriche. Poi convertiamo in intero e controlliamo che il valore sia tra 1 e 9. Infine verifichiamo che la casella sia libera. Questo ordine è sensato: prima eliminiamo gli input palesemente sbagliati, poi controlliamo lo stato del tabellone.

Avvio del gioco da terminale

Salva il file, per esempio come tris.py, e avvialo così:

python3 tris.py

All’avvio vedrai il messaggio di benvenuto e la griglia numerata. Ogni giocatore inserisce il numero della casella desiderata. Dopo ogni mossa, il programma ristampa il tabellone aggiornato. Quando qualcuno vince o si arriva a pareggio, la partita termina.

Se il terminale non mostra l’output atteso, il primo controllo da fare è banale ma efficace: verifica che il file sia stato salvato correttamente e che tu stia eseguendo il file giusto. Un errore frequente è avere più copie dello stesso script in cartelle diverse.

Test manuali utili per capire se il gioco è sano

Quando scrivi il primo gioco, fare test manuali è normale. Non serve una suite di test complessa per iniziare, ma devi almeno provare alcuni casi essenziali. Ecco quelli più importanti:

  • Inserire una lettera invece di un numero: il programma deve rifiutare l’input.
  • Inserire 0 o 10: il programma deve segnalare che la casella è fuori range.
  • Scegliere una casella già occupata: il programma deve chiedere una nuova mossa.
  • Portare la partita a una vittoria: il programma deve fermarsi e dichiarare il vincitore.
  • Riempire tutte le caselle senza vincitore: il programma deve mostrare pareggio.

Se uno di questi casi fallisce, il problema è quasi sempre localizzato: validazione input, controllo vittoria o gestione del turno. Questo rende il debug molto più semplice rispetto a un’applicazione complessa con più moduli e dipendenze esterne.

Come migliorarlo senza stravolgerlo

Una volta che la versione base funziona, puoi aggiungere estensioni utili senza cambiare l’architettura di fondo. Per esempio puoi introdurre un menu iniziale, il conteggio delle vittorie, la scelta del nome dei giocatori o una modalità contro il computer. Il punto è non mischiare subito tutto nel codice principale.

Una modifica sensata è separare la logica del gioco dall’interfaccia testuale. Oggi l’input arriva da input() e l’output va su schermo, ma domani potresti voler creare una versione grafica o web. Se la logica è già isolata in funzioni pure, il salto è molto più facile.

Un’altra estensione interessante è la versione con computer avversario. Per iniziare puoi far scegliere al PC una casella libera a caso. Non è intelligente, ma ti obbliga a ragionare su come generare mosse valide senza duplicare il codice di validazione. Più avanti potrai implementare una strategia minimax o regole semplici di difesa e attacco.

Una variante più ordinata: funzioni piccole e stato chiaro

Se vuoi fare un passo in più, puoi trasformare questo script in una base più pulita. L’idea è mantenere ogni funzione con un solo compito. Per esempio, una funzione non dovrebbe sia leggere l’input sia decidere il vincitore. Questo tipo di separazione è utile in qualsiasi progetto, anche fuori dai giochi.

In pratica, il flusso del programma dovrebbe essere facile da raccontare a voce: inizializza la griglia, stampa lo stato iniziale, alterna i turni, valida la mossa, aggiorna il tabellone, controlla vittoria o pareggio. Se riesci a descriverlo con poche frasi, di solito il codice è sulla strada giusta.

Errori comuni da evitare

Il primo errore è confondere gli indici della lista con i numeri mostrati all’utente. Nel programma, la casella 1 corrisponde all’indice 0. Questo scarto di uno va gestito con attenzione. Il secondo errore è dimenticare di aggiornare il turno dopo una mossa valida. Il terzo è controllare il pareggio prima della vittoria.

Un altro problema frequente è usare variabili globali ovunque. Funziona all’inizio, ma rende più difficile capire cosa cambia davvero. Meglio passare la griglia alle funzioni in modo esplicito. È una disciplina utile fin da subito, perché ti abitua a scrivere codice più prevedibile.

Perché questo esercizio vale più di quanto sembri

Un Tris in Python non è solo un gioco da terminale. È un esercizio completo di programmazione imperativa: stato, condizioni, cicli, funzioni, gestione degli errori, lettura dell’input, output leggibile. In poche righe tocchi concetti che ritroverai in progetti molto più grandi, dai tool di amministrazione ai piccoli servizi.

In più, ti insegna una cosa importante: un programma non deve limitarsi a “non crashare”. Deve anche guidare l’utente, respingere gli input sbagliati e rendere chiaro cosa sta succedendo. Questa è una qualità che distingue uno script improvvisato da un software che si può davvero usare.

Se vuoi proseguire, il passo successivo naturale è aggiungere una IA semplice, poi una versione grafica con tkinter, oppure riscrivere la logica in modo orientato agli oggetti. Ma prima conviene padroneggiare bene questa versione. Quando sai spiegare ogni riga del codice, hai già fatto il salto più importante.

Il Tris è piccolo, ma è un ottimo banco di prova: se sai costruirlo bene, hai già messo mano a quasi tutto ciò che serve per affrontare programmi più seri con meno confusione e più metodo.