Le jeu du pendu en Python

Le jeu du pendu en Python

J’ai fait un exécutable Windows du jeu du pendu réalisé en Python, que vous pouvez trouver sur cette page.

Pou celles et ceux qui souhaitent savoir comment réaliser un tel jeu, voici la démarche que j’ai adoptée.

jeu pendu python

Le jeu du pendu en Python : le principe

Avant tout, il faut rappeler le principe du jeu. Un mot est choisi au hasard et le joueur doit le deviner en proposant des lettres.

Si la lettre proposée est présente dans le mot, elle s’affiche aux endroits adéquats; sinon, un élément du dessin apparaît.

Il y a 11 éléments sur le dessin. 4 éléments de la potence, 1 corde et 6 élément du pendu lui-même. La personne qui joue ne doit donc pas faire plus de 10 erreurs, la onzième lui étant fatale.

À travers ce principe, on voir alors se dessiner (sans jeu de mots) un semblant de script:

  1. Choisir au hasard un mot dans une liste.
  2. Mettre des underscores à la place des lettres.
  3. Tant que le mot n’est pas trouvé et tant que le nombre d’erreurs est inférieur strictement à 11, demander une lettre.
  4. Si la lettre est dans le mot, l’afficher aux bons endroits; sinon, incrémenter le nombre d’erreur d’une unité et dessiner un élément du dessin.

Le jeu du pendu : le script Python sans interface graphique

Il faut bien commencer par quelque chose. J’ai toujours commencé mes scripts par leur cœur, c’est-à-dire par ce qui définit réellement le programme.

Je vais donc suivre les points de l’algorithme naturel présenté précédemment.

Pour tout ce qui est programme avec interface graphique, j’ai l’habitude d’utiliser la Programmation Orientée Objet (POO). Cependant, pour simplifier les choses, je ne vais pas faire ça dans cette partie non graphique.

Choisir au hasard un mot dans une liste

Il nous faut une fonction qui retourne un mot en lettres majuscules, pris au hasard dans une liste.

J’ai récupéré sur la page http://www.pallier.org/liste-de-mots-francais.html, le fichier texte suivant :

Il contient 336 531 mots du français (encodés en utf-8) en minuscules. Tous ne sont pas bons à prendre. Ceux avec trait d’union ou ceux à moins de 5 lettres par exemple sont à exclure.

Il y a ici deux façons de raisonner :

  • une liste de tous les mots qui nous intéressent (sans trait d’union, avec au moins 5 lettres) est construite;
  • une ligne du fichier est choisie au hasard et on vérifie que le mot correspondant rentre dans nos critères.

Dans le programme que j’ai fait, j’ai opté pour la première solution. Cependant, je vais ici vous exposer la seconde, plus rapide.

Voici donc une fonction qui retourne un mot au hasard:

from random import choice
from unidecode import unidecode

def word():
    f = open('mots.txt', 'r' , encoding = 'utf8')
    contenu = f.readlines()
    return unidecode( choice(contenu) ).upper().replace('\n','')

Je fais ici appel à deux modules :

  • random, et sa fonction choice qui retourne pseudo-aléatoirement un élément de la liste contenu, qui n’est autre que la liste des mots contenus dans le fichier texte;
  • unidecode, et sa fonction éponyme qui retourne la chaîne de caractères sans accents.

J’utilise ensuite la méthode upper() pour mettre en majuscules.

De plus, chaque ligne du fichier se termine par “\n”. J’ai donc utilisé la méthode replace() pour supprimer le “\n”.

Si vous souhaitez conserver les accents, comme c’est le cas dans mon programme exécutable, il suffit de ne pas ôter les accents et donc de ne pas utiliser unidecode dans cette fonction.

Mettre des underscores à la place des lettres

On va ici écrire une fonction underscore(mot) retournant une chaîne de caractères où les lettres sont remplacées par des “_” (des underscores). Pour plus de lisibilité, on séparera les underscores avec une espace.

def underscore(mot):
    r = '_ ' * len( mot )
    return r[:-1]

Définie ainsi, la fonction est simple. On répète le motif “_ ” autant de fois qu’il y a de lettres dans le mot. Ne pas oublier l’espace à la fin du motif.

Ensuite, cette chaîne est retournée sans le dernier caractère (qui est une espace et qui ne sert à rien).

Nous verrons plus loin que cette fonction devra être modifiée.

Saisie d’une lettre

def saisie():
    lettre = input('Entrez une lettre : ')
    if len( lettre ) > 1 or ord(lettre) < 65 or ord(lettre) > 122:
        return saisie()
    else:
        return lettre.upper()

La fonction de saisie est simple : un simple input et on retourne la lettre saisie en majuscule. On fait tout de même une vérification afin de voir si la saisie ne contient qu’un caractère et si elle correspond à une lettre de l’alphabet (le “65” correspond au code ASCII de “A” et le “122” à celui de “z”).

Remarquez que cette fonction est récursive dans le cas où la saisie n’est pas une lettre. Il faut donc penser à la condition d’arrêt, qui est de retourner le caractère saisi dans le cas où c’est bien une lettre de l’alphabet.

La partie principale

On peut imaginer un début de script comme ceci :

mot_a_deviner = word()
affichage = underscore( mot_a_deviner )
print( 'Mot à deviner : ' , affichage )
lettre = saisie()
if lettre in mot_a_deviner:

Mais au moment de tester si la lettre est dans le mot, un dilemme se présente : que faire ? Il faut que l’affichage change en fonction de la lettre proposée. Nous devons donc changer la fonction underscore(). Elle doit recevoir non pas une lettre en argument, mais une liste de lettres (celles déjà proposées).

Cela donne un script comme ceci:

lettres_deja_proposees = []
mot_a_deviner = word()
affichage = underscore( mot_a_deviner )
print( 'Mot à deviner : ' , affichage )

while '_' in affichage:
    lettre = saisie()
    if lettre not in lettres_deja_proposees:
        lettres_deja_proposees += [ lettre ]
            
    affichage = underscore( mot_a_deviner , lettres_deja_proposees )
    print( 'Mot à deviner : ' , affichage )

en changeant la fonction underscore() ainsi :

def underscore(mot , L = []):
    r = ''
    for i in mot:
        if i in L:
            r += i + ' '
        else:
            r += '_ '
            
    return r[:-1]

Compter le nombre d’erreurs

Il nous reste en effet à mettre un peu de piment à tout ça en limitant le nombre d’erreurs à 11.

lettres_deja_proposees = []
mot_a_deviner = word()
affichage = underscore( mot_a_deviner )
print( 'Mot à deviner : ' , affichage )
nb_erreurs = 0

while '_' in affichage and nb_erreurs < 11:
    lettre = saisie()
    if lettre not in lettres_deja_proposees:
        lettres_deja_proposees += [ lettre ]
        
    if lettre not in mot_a_deviner:
        nb_erreurs += 1
            
    affichage = underscore( mot_a_deviner , lettres_deja_proposees )
    print( '\nMot à deviner : ' , affichage , ' '*10 , 'Nombre d\'erreurs maximum :' , 11-nb_erreurs )
jeu pendu python terminal

Le script complet du jeu du pendu en Python

from random import choice
from unidecode import unidecode

# un mot au hasard

def word():
    f = open('mots.txt', 'r' , encoding = 'utf8')
    contenu = f.readlines()
    return unidecode( choice(contenu) ).upper().replace('\n','')

# remplacement par des underscores

def underscore(mot , L = []):
    r = ''
    for i in mot:
        if i in L:
            r += i + ' '
        else:
            r += '_ '
            
    return r[:-1]

# saisie d'une lettre

def saisie():
    lettre = input('Entrez une lettre : ')
    if len( lettre ) > 1 or ord(lettre) < 65 or ord(lettre) > 122:
        return saisie()
    else:
        return lettre.upper()
    

# programme principal

lettres_deja_proposees = []
mot_a_deviner = word()
affichage = underscore( mot_a_deviner )
print( 'Mot à deviner : ' , affichage )
nb_erreurs = 0

while '_' in affichage and nb_erreurs < 11:
    lettre = saisie()
    if lettre not in lettres_deja_proposees:
        lettres_deja_proposees += [ lettre ]
        
    if lettre not in mot_a_deviner:
        nb_erreurs += 1
            
    affichage = underscore( mot_a_deviner , lettres_deja_proposees )
    print( '\nMot à deviner : ' , affichage , ' '*10 , 'Nombre d\'erreurs maximum :' , 11-nb_erreurs )

Interface graphique

Nous sommes tous d’accords pour dire que maintenant que nous avons vu l’essentiel, il serait intéressant d’avoir une interface plus sympathique.

J’utilise tkinter pour cela, car bien plus simple que d’autres modules graphiques comme QT5.

Comme mentionné en début d’article, j’utilise la POO : je considère que le jeu est un objet auquel j’attribue des méthodes.

Les abonné·e·s de mathweb.fr trouveront le script sur cette page dans un fichier zip, contenant aussi le fichier texte.

jeu pendu python tkinter
Aperçu de l’interface graphique tkinter
Stéphane Pasquet
Stéphane Pasquet

Laissez votre message