Licence CC BY-NC-SA

Interagir avec l'utilisateur

À ce stade, vous vous dites probablement qu’il manque quelque chose pour que nos programmes soient plus dynamiques. Il est donc temps de voir comment turtle nous permet d’interagir avec l’utilisateur !

Les saisies de l'utilisateur

Avec turtle, il existe deux fonctions pour demander à l’utilisateur de saisir des informations.

Demander une chaîne de caractères

Pour commencer, nous pouvons lui demander de saisir une chaîne de caractères avec textinput. Pour ce faire, il nous faut passer le titre ainsi que le texte de notre future popup à la fonction. En retour, cette dernière nous renvoie la saisie de l’utilisateur. Grâce à cela, nous pouvons connaître son nom par exemple :

nom = turtle.textinput("Nom", "Veuillez saisir votre nom s'il vous plaît")
print(nom)  #Affiche le nom saisi
La popup résultante.
La popup résultante.
Demander un nombre

Ensuite, nous pouvons aussi lui demander un nombre avec numinput. De la même manière que pour demander une chaîne de caractères, il faut indiquer le titre et le texte de notre future popup. En plus, nous avons la possibilité d’indiquer une valeur par défaut (default), une valeur minimum (minval) ainsi qu’une valeur maximum (maxval). La fonction retourne un nombre flottant. Voici quelques exemples :

age = int(turtle.numinput("Âge", "Veuillez saisir votre âge s'il vous plaît"))
age = int(turtle.numinput("Âge", "Veuillez saisir votre âge s'il vous plaît", 18))
age = int(turtle.numinput("Âge", "Veuillez saisir votre âge s'il vous plaît", minval = 5, maxval = 125))

Vous remarquerez que nous pourrions très bien utiliser textinput pour demander un nombre et ensuite convertir la valeur, mais il est plus sage et plus propre d’utiliser numinput qui est faite pour ça. En outre, cette dernière vérifie si la valeur saisie est bel et bien un nombre. En vérité, il faut toujours contrôler les données entrées par l’utilisateur pour s’assurer qu’elles sont en adéquation avec ce que nous attendons. Par exemple, lorsque nous lui avons demandé son nom, il aurait très bien répondre n’importe quoi comme "3.14" ou encore "x = 9" et nous aurions affiché cela sans broncher ! Pire encore, il aurait pu cliquer sur Cancel et nous n’aurions eu aucune valeur !

Pour vous entraîner, vous pouvez par exemple demander successivement des valeurs à l’utilisateur comme un formulaire (prénom, nom, date de naissance, addresse, etc.) tout en vérifiant la véracité des saisies. Par exemple :

while True:
    try :
        nom = turtle.textinput("Nom", "Veuillez saisir votre nom s'il vous plaît")
        if nom.isalpha() and len(nom) < 21:
            break
    except TypeError:
        pass
print(nom)

Ici, nous demandons à l’utilisateur d’entrer son nom tant que nous ne jugeons pas sa saisie admissible. Pour ce faire, nous testons si le nom contient au moins une lettre et uniquement des lettres (et non pas des chiffres ou des caractères spéciaux) et nous vérifions que le nom n’excède pas une certaine longueur (20 caractères ici). De plus, nous nous servons de l’exception TypeError pour nous prémunir de tout clique sur le bouton Cancel. En effet, dans ce cas là, notre variable nom n’aura pas de valeur et donc pas de type (NoneType). En conséquence, les méthodes que nous appliquons aux chaînes de caractères ne pourront pas s’appliquer ce qui lèvera une exception.

En plus des saisies, l’utilisateur peut interagir en provoquant des événements.

Les événements

Grâce à sa simplicité, turtle nous permet d’utiliser facilement les événements.

Mais en fait, qu’est-ce qu’un événement ?

Un événement est une information générée lorsque l’utilisateur utilise des périphériques spécifiques tel que le clavier ou encore la souris par exemple. Cela est très utile, car de cette manière l’utilisateur peut faire connaître ses décisions au programme. Par exemple, dans un jeu nous pouvons généralement nous diriger en appuyant sur les flèches ou encore nous pouvons tirer en faisant un clique gauche.

Écouter les événements du clavier

Pour commencer, nous devons utiliser la fonction listen pour pouvoir écouter et capturer les événements en provenance du clavier. Nous pouvons la placer n’importe où dans le code, mais il est plus propre de la placer avant de gérer les événements afin de mieux s’y retrouver.

turtle.listen()
#Les événements du clavier
#...
La boucle d’événements

Les fonctions mainloop et done permettent de démarrer la boucle d’événements. Dans le monde des événements, nous utilisons une boucle infinie. De cette manière, le programme continue d’écouter les interactions jusqu’à ce que l’utilisateur informe qu’il a terminé en fermant la fenêtre par exemple. Dès lors, nous sortons de la boucle et le programme peut s’achever. Ces fonctions mettent en place cette boucle infinie pour nous. D’après la documentation, nous devons utiliser une de ces fonctions tout à la fin de notre programme turtle.

#Le code
#....
turtle.mainloop()  #Ou bien turtle.done()
Le clavier

Commençons avec les événements en provenance du clavier. Il en existe deux types.

Appuyer sur une touche

Tout d’abord, une touche peut être pressée et c’est la fonction onkeypress qui nous permet de le savoir. En premier paramètre, nous devons lui passer une fonction qui sera exécutée lors de l’appui. En second paramètre, nous pouvons lui indiquer à quelle touche nous souhaitons associer la fonction sous la forme d’une chaîne de caractères : "space" pour la touche Espace par exemple. Si nous omettons ce second paramètre, alors la fonction sera attribuée à toutes les touches libres.

Pour l’appui, une touche ne peut être associée qu’à une seule fonction.

Avec l’exemple ci-dessous, "Touche 'a' pressée !" est affiché lorsque nous pressons la touche A et "Touche pressée !" est affiché lorsque nous appuyons sur une autre touche. Si nous avions inversé les lignes avec les onkeypress, nous aurions eu le même comportement puisque la touche A étant déjà associée à une fonction, elle n’aurait pas été prise en compte par le second appel.

def appui_sur_a():
    print("Touche 'a' pressée !")

def appui_quelconque():
    print("Touche pressée !")

if __name__ == "__main__":
    turtle.listen()
    turtle.onkeypress(appui_quelconque)  #Toutes les touches libres sont associées à appui_quelconque
    turtle.onkeypress(appui_sur_a, "a")  #La touche A est désormais associée à appui_sur_a
    turtle.mainloop()
Relâcher une touche

Ensuite, une touche peut être relâchée. En passant à onkeyrelease une fonction ainsi qu’une touche (ce paramètre n’est pas facultatif ici contrairement à onkeypress), nous pouvons gérer cela.

Pour le relâchement, une touche ne peut être associée qu’à une fonction.

def relachement_a():
    print("Touche 'a' relâchée !")

def relachement_haut():
    print("Touche 'Up' relâchée !")

if __name__ == "__main__":
    turtle.listen()
    turtle.onkeyrelease(relachement_haut, "a")  #La touche "A" est associée à relachement_haut
    turtle.onkeyrelease(relachement_a, "a")  #La touche "A" est maintenant associée à relachement_a
    turtle.onkeyrelease(relachement_haut, "Up")  #La touche "Haut" est associée à relachement_haut
    turtle.mainloop()

Ainsi, lorsque nous relâchons la touche A, "Touche 'a' relâchée !" est affiché, et lorsque nous relâchons la touche Haut, c’est "Touche 'Up' relâchée !" qui est affiché.

Exemple : une balle qui roule

En mettant en application ce que nous venons de voir, nous pouvons réaliser un code basique, permettant de déplacer une balle à l’écran. L’appuie sur les flèches permet de se diriger et la touche Espace, lorsqu’elle est relâchée, permet de réinitialiser la position.

Le code :

import turtle

def deplacer_sans_tracer(x, y = None):
    """Fonction pour se déplacer à un point sans tracer"""
    turtle.up()
    if (isinstance(x, tuple) or isinstance(x, list)) and len(x) == 2:
        turtle.goto(x)
    else:
        turtle.goto(x, y)
    turtle.down()

def affiche_point(diametre, couleur):
    """Fonction pour afficher seulement la balle"""
    deplacer_sans_tracer(pos_x, pos_y)
    turtle.clear()
    turtle.dot(diametre, couleur)

def reinit_pos():
    """Fonction pour réinitialiser la position de la balle"""
    global pos_x, pos_y
    pos_x = pos_y = 0
    affiche_point(diametre_balle, couleur_balle)
    
def deplace_x(num = 0):
    """Fonction pour changer la coordonnée x de la balle"""
    global pos_x
    pos_x += num
    affiche_point(diametre_balle, couleur_balle)

def deplace_y(num = 0):
    """Fonction pour changer la coordonnée y de la balle"""
    global pos_y
    pos_y += num
    affiche_point(diametre_balle, couleur_balle)

if __name__ == "__main__":
    turtle.speed(0)
    pos_x, pos_y = 0, 0
    diametre_balle, couleur_balle = 20, 'red'

    turtle.hideturtle()
    affiche_point(diametre_balle, couleur_balle)
    
    turtle.listen()  #Pour écouter
    turtle.onkeypress(lambda : deplace_x(-10), "Left")  #Touche gauche
    turtle.onkeypress(lambda : deplace_x(10), "Right")  #Touche droite
    turtle.onkeypress(lambda : deplace_y(10), "Up")  #Touche haut
    turtle.onkeypress(lambda : deplace_y(-10), "Down")  #Touche bas
    turtle.onkeyrelease(reinit_pos, "space")  #Touche espace
    turtle.mainloop()  #Pour démarrer la boucle d'événements

Dans la boucle principale, nous écoutons les événements en provenance du clavier. Ensuite, nous prenons en compte ceux qui nous intéressent en utilisant des lambda afin de passer des paramètres aux fonctions. Enfin, nous terminons en démarrant la boucle d’événements. Les scintillements que vous pourrez voir en testant sont explicables par l’utilisation de la fonction clear lorsque l’on nettoie l’écran pour afficher la balle.

Vous pouvez améliorer le programme en demandant à l’utilisateur de choisir le diamètre ainsi que la couleur de la balle ou encore en veillant à ce que la balle ne puisse pas sortir de l’écran, par exemple.

Une balle qui peut bouger.
Une balle qui peut bouger.
La souris

La souris peut aussi générer des événements (bouton cliqué, bouton relâché, souris déplacée). Avec turtle, nous pouvons prendre en compte l’appui sur un bouton de la souris. Si vous vous rappelez, nous utilisions déjà cette source d’événements lorsque nous utilisions exitonclick pour quitter notre programme avec un clique gauche.

onscreenclick est une fonction pour capturer l’appui sur un bouton de la souris. Nous devons lui passer la fonction qui sera associée au clique. Ensuite, nous pouvons lui passer à quel bouton sera associé la fonction (par défaut le clique gauche) et enfin nous avons la possibilité de choisir si la fonction s’ajoutera à (avec True) ou remplacera (avec False ou rien) la ou les fonctions déjà associée(s). De cette manière, un même clique pourra déclencher plusieurs fonctions.

Notons que les coordonnées du clique seront automatiquement passées à la fonction c’est pourquoi vous devez les prendre en compte dans la définition de votre fonction sans quoi vous aurez un joli message d’erreur. On peut ainsi se déplacer au point cliqué par exemple :

turtle.up()
turtle.onscreenclick(turtle.goto)  #Se déplace au point cliqué 
turtle.mainloop()

Le tableau ci-dessous contient les valeurs, correspondant aux boutons, que vous pouvez passer.

Valeur Bouton
1 Gauche
2 Central
3 Droit

Enfin, voici un petit exemple comme d’habitude pour bien comprendre le comportement de cette fonction :

import turtle

def toto(x, y):
    print(f"toto : {x}, {y}")

def tutu(x, y):
    print(f"tutu : {x}, {y}")
    
def tata(x, y):
    print(f"tata : {x}, {y}")

def titi(x, y):
    print(f"titi : {x}, {y}")
    
def tete(x, y):
    print(f"tete : {x}, {y}")

if __name__ == "__main__":
    #Pas besoin de turtle.listen() vu que l'on ne s'occupe pas du clavier

    #Clique gauche : juste toto au final
    turtle.onscreenclick(titi, 1)
    turtle.onscreenclick(toto, 1)  # <=>  turtle.onscreenclick(toto, 1, False)
   
    #Clique central : juste tata
    turtle.onscreenclick(tata, 2)

    #Clique droit : tete et tutu
    turtle.onscreenclick(tete, 3)
    turtle.onscreenclick(tutu, 3, True)
    
    turtle.mainloop()

Comme expliqué dans le code, on associe au clique gauche les fonctions titi et toto (la première étant alors remplacée par la seconde). Ensuite au associe au clique central, la fonction tata. Enfin, on associe les fonctions tete et tutu au clique droit. À chaque fois, on affiche les coordonnées du curseur de la souris lors du clique.

Encore une fois, n’hésitez pas à tester les fonctions que nous avons vues. Nous allons maintenant voir comment écrire dans la fenêtre.

L'écriture à l'écran

En écrivant un message à l’utilisateur, nous pouvons l’informer ou lui demander une information (c’est ce que nous avons fait avec les saisies par exemple).

Bien que nous pourrions afficher nos messages dans la console grâce à print, turtle nous offre write, une fonction pour écrire directement dans la fenêtre. Pour ce faire, il suffit de lui passer le texte à écrire. Notons au passage que la couleur d’écriture sera celle de tracé. En plus de cela, nous pouvons choisir si le crayon reprendra sa position préalable ou sera dans le coin bas droit du texte écrit respectivement avec les valeurs False et True (paramètre move). Nous pouvons aussi indiquer comment sera aligné le texte par rapport à la position du crayon (align) ainsi que la police utilisée (font). Cette dernière se décompose en un nom de police, une taille de police ainsi qu’un un formatage de texte. Voici un exemple :

turtle.write("Bonjour !", True)  #Écrit et déplace le crayon à la fin du texte
turtle.write("Salut !", align = "left")  #Écrit à droite du crayon
turtle.write("Coucou !", font = ("Arial", 15, "bold"))  #Écrit selon une police

Si nous donnons la valeur True au paramètre move, un trait sera tracé sous notre texte dû au déplacement que notre crayon soit baissé ou levé.

Voici ci-dessous un tableau avec les alignements par rapport au curseur possibles :

Valeur Alignement
"left" Droit
"center" Centré
"right" Gauche

Oui, vous avez bien lu. Les alignements gauche et droite sont inversés par rapport à ce que nous aurions pu penser. Pour bien comprendre, imaginez-vous que pour écrire, le crayon regarde dans votre direction. Ainsi, quand il se déplacera à la gauche du point d’alignement, pour vous il ira vers la droite, et vice versa.

Voici ci-dessous un autre tableau, contenant les formatages possibles :

Valeur Formatage
"normal" Normal
"bold" Gras
"italic" Italique
"underline" Souligné

Dans l’exemple ci-dessous suivi d’un résultat en image, nous écrivons aléatoirement dans la fenêtre chaque fruit de notre dictionnaire. Pour sa couleur, nous prenons la valeur associée tandis que nous choisissons de manière aléatoire les autres caractéristiques du texte (alignement, taille et formatage). En définitive, c’est presque le même procédé qu’avec notre exemple avec les points, si vous vous rappelez.

Le code :

import turtle
from random import randint

LARGEUR = HAUTEUR = 400

ALIGNEMENTS = ["left", "center", "right"]
FORMATAGES = ["normal", "bold", "italic", "underline"]
FRUITS = {"Clémentine" : "orange", "Ananas" : "brown", "Pomme" : "green",
          "Poire" : "lightgreen", "Banane" : "yellow", "Orange" : "orange",
          "Cerise" : "darkred", "Abricot" : "orange", "Kiwi" : "green",
          "myrtille" : "darkblue", "prune" : "blue"}

def deplacer_sans_tracer(x, y = None):
    """Fonction pour se déplacer à un point sans tracer"""
    turtle.up()
    if (isinstance(x, tuple) or isinstance(x, list)) and len(x) == 2:
        turtle.goto(x)
    else:
        turtle.goto(x, y)
    turtle.down()

if __name__ == "__main__":
    turtle.setup(LARGEUR+50, HAUTEUR+50) 
    for fruit, couleur in FRUITS.items():
        deplacer_sans_tracer(randint(-LARGEUR//2, LARGEUR//2), randint(-HAUTEUR//2, HAUTEUR//2))
        turtle.pencolor(couleur)
        turtle.write(
            fruit,
            align = ALIGNEMENTS[randint(0, len(ALIGNEMENTS)-1)],
            font = ("Arial", randint(14, 30),
            FORMATAGES[randint(0, len(FORMATAGES)-1)])
        )
De quoi faire un bon smoothie.
De quoi faire un bon smoothie.

Découvrons à présent une nouvelle notion : les timers.

L'utilisation de timers

Avec la fonction ontimer, nous avons la possibilité de faire appel à une fonction (que l’on passe en premier paramètre) après une durée exprimée en millisecondes (que l’on passe en second paramètre, la valeur par défaut étant 0) :

turtle.ontimer(lambda : print("tutu"), 2000)  #Affiche 'tutu' après deux secondes, donc après le 'toto'
turtle.ontimer(lambda : print("toto"))  #Affiche 'toto' immédiatement

Notons au passage que l’utilisation de cette fonction n’est pas bloquante puisque 'toto' est bien affiché en premier malgré le fait que l’instruction soit après celle affichant 'tutu'.

Dans le programme ci-dessous, en affichant le temps écoulé depuis le lancement du programme, nous utilisons cette fonction de deux manières différentes. Dans un premier temps, nous nous en servons pour commencer à incrémenter après une seconde écoulée. Puis, dans incremente_temps, nous nous en servons de sorte que la fonction s’appelle elle-même toutes les secondes pour continuer à incrémenter. C’est une sorte de boucle infinie que l’on peut rompre avec la croix rouge ou avec un clique gauche : en fermant la fenêtre.

Le code :

import turtle

def ecrit_temps():
    """Fonction pour écrire le temps écoulé"""
    turtle.clear()
    turtle.write(f'''Le programme tourne depuis 
                 {heure} h {minute} min et {seconde} s''',
                 align = "center",
                 font = ("Arial", 16, "bold"))
    
def incremente_temps():
    """Fonction pour incrémenter le temps"""
    global heure, minute, seconde
    seconde += 1
    if seconde == 60:
        minute += 1
        seconde = 0
    if minute == 60:
        heure += 1
        minute = 0
    ecrit_temps()
    turtle.ontimer(incremente_temps, 1000)  #Fait appel à elle-même toutes les secondes

if __name__ == "__main__":
    turtle.speed(0)
    turtle.hideturtle()
    heure = minute = seconde = 0
    ecrit_temps()
    turtle.ontimer(incremente_temps, 1000)  #Attend une seconde puis commence à incrémenter
    turtle.exitonclick()
Résultat après un certain temps...
Résultat après un certain temps…

L’utilisation de timers nous ouvre de nouveaux champs de possibilités. Sur ce, nous allons passer à la pratique !

TP : Le jeu des allumettes

Grâce à ce que nous venons d’apprendre, nous allons maintenant réaliser notre premier jeu avec turtle : le jeu des allumettes !

Il existe plusieurs variantes de ce jeu qui voit deux joueurs s’affronter. Nous partirons du principe qu’une partie commence avec 19 allumettes. Chacun leur tour, les joueurs retirent 1, 2 ou 3 allumettes. Un joueur a perdu lorsque son tour arrive et qu’il ne reste plus qu’une allumette sur le plateau.

Pour notre version, les joueurs choisiront combien d’allumettes ils souhaitent retirer en relâchant les touches a, z ou e pour respectivement 1, 2 ou 3 allumettes. Pour quitter, il suffira de faire un clique droit. Nous afficherons les informations essentielles telles que le nombre d’allumettes restantes, le joueur courant, l’état de la partie et éventuellement le nombre d’allumettes retirées.

Désormais vous avez toutes les clefs en main pour réaliser cet exercice. Si vous ne savez pas par où commencer, essayez de réfléchir à ce que doit faire votre programme et de définir les fonctions que vous avez besoin de programmer. Si besoin, n’hésitez pas à relire cette partie ou à poser vos questions sur le forum.

Enfin, les captures d’écran ci-dessous peuvent vous inspirer si besoin :

Le jeu des allumettes. (1)
Le jeu des allumettes. (1)
Le jeu des allumettes. (2)
Le jeu des allumettes. (2)
Le jeu des allumettes. (3)
Le jeu des allumettes. (3)

La correction :

import turtle
import sys

#Les constantes
NOMBRE_ALLUMETTE = 19
HAUTEUR_BOIS_ALLUMETTE = 50
HAUTEUR_ROUGE_ALLUMETTE = 10
COULEUR_BOIS_ALLUMETTE = "#CDA88C"
COULEUR_ROUGE_ALLUMETTE = "#DC5844"
COULEUR_FOND = "#8CCDC4"
TITRE = "Jeu des allumettes"
TAILLE_ECRITURE = 26
TAILLE_ECRITURE_2 = 16

#Les autres variables
etat_partie = True
nombre_allumettes = NOMBRE_ALLUMETTE
joueur_courant = 1

#Les fonctions
def deplacer_sans_tracer(x, y = None):
    """Fonction pour se déplacer à un point sans tracer"""
    turtle.up()
    if (isinstance(x, tuple) or isinstance(x, list)) and len(x) == 2:
        turtle.goto(x)
    else:
        turtle.goto(x, y)
    turtle.down()

def initialise_fenetre():
    """Fonction pour initialiser la fenêtre"""
    turtle.hideturtle()
    turtle.setheading(90)
    turtle.title(TITRE)
    turtle.bgcolor(COULEUR_FOND)
    turtle.speed(0)

def dessiner_allumette():
    """Fonction pour dessiner une allumette"""
    turtle.pencolor(COULEUR_BOIS_ALLUMETTE)
    turtle.forward(HAUTEUR_BOIS_ALLUMETTE)
    turtle.pencolor(COULEUR_ROUGE_ALLUMETTE)
    turtle.forward(HAUTEUR_ROUGE_ALLUMETTE)
    
def dessiner_allumettes(nombre_allumettes):
    """Fonction pour dessiner les allumettes"""
    espace_entre_allumettes = 60 if nombre_allumettes < 8 else turtle.window_width()/2//nombre_allumettes
    taille_crayon = 25 if nombre_allumettes < 8 else espace_entre_allumettes//3
    turtle.pensize(taille_crayon)
    position_allumettes = [-nombre_allumettes/2*espace_entre_allumettes, 0]
    deplacer_sans_tracer(position_allumettes)
    for allumette in range(nombre_allumettes):
        dessiner_allumette()
        position_allumettes[0] += espace_entre_allumettes
        deplacer_sans_tracer(tuple(position_allumettes))
    if nombre_allumettes != 1:
        afficher_nombre_allumettes(nombre_allumettes)

def afficher_partie(nombre_allumettes, joueur_courant, nombre_retirees = None):
    """Fonction pour afficher la partie et son état"""
    turtle.clear()
    dessiner_allumettes(nombre_allumettes)
    afficher_qui_joue(joueur_courant)
    if nombre_retirees != None:
        joueur = 1 if joueur_courant == 2 else 2
        affiche_nombre_retire(joueur, nombre_retirees)

def affiche_nombre_retire(joueur, nombre_retirees, pos = (0, -110)):
    """Fonction pour afficher le nombre d'allumettes retirées"""
    deplacer_sans_tracer(pos)
    turtle.write(f"(Le Joueur {joueur} a retiré {nombre_retirees} allumette(s))",
                 align = "center",
                 font = ("Arial", TAILLE_ECRITURE_2, "italic"))

def afficher_nombre_allumettes(nombre_allumettes, pos = (0, -80)):
    """Fonction pour afficher le nombre d'allumettes"""
    deplacer_sans_tracer(pos)
    turtle.write(f"Il y a {nombre_allumettes} allumettes.",
                 align = "center",
                 font = ("Arial", TAILLE_ECRITURE, "normal"))

def afficher_qui_joue(joueur_courant, pos = (0, 100)):
    """Fonction pour afficher qui joue"""
    deplacer_sans_tracer(pos)
    turtle.write(f"C'est au Joueur {joueur_courant} de jouer !",
                 align = "center",
                 font = ("Arial", TAILLE_ECRITURE, "normal"))

def bloque_clavier():
    """Fonction pour désactiver les actions des touches a, z, e"""
    turtle.onkeyrelease(None, "a")
    turtle.onkeyrelease(None, "z")
    turtle.onkeyrelease(None, "e")

def debloque_clavier():
    """Fonction pour associer les touches au nombre retiré"""
    turtle.onkeyrelease(lambda : joue(1), "a")
    turtle.onkeyrelease(lambda : joue(2), "z")
    turtle.onkeyrelease(lambda : joue(3), "e")
    
def joue(nombre_retire = 1):
    """Fonction pour prendre en compte le choix du joueur"""
    bloque_clavier()
    global nombre_allumettes, etat_partie, joueur_courant
    if nombre_retire != 0 and nombre_allumettes-nombre_retire > 0:
        nombre_allumettes -= nombre_retire
    else:
        debloque_clavier()
        return
    if nombre_allumettes != 1:
        joueur_courant = 1 if joueur_courant == 2 else 2
        afficher_partie(nombre_allumettes, joueur_courant, nombre_retire)
    else:
        etat_partie = victoire(joueur_courant)
        if not etat_partie:
            quitter()
        nombre_allumettes = NOMBRE_ALLUMETTE
        afficher_partie(nombre_allumettes, joueur_courant)
        turtle.listen()
    debloque_clavier()

def victoire(joueur_courant):
    """Fonction pour le déroulement de la victoire"""
    turtle.clear()
    dessiner_allumettes(1)
    deplacer_sans_tracer(-35, -100)
    turtle.down()
    turtle.write(f"Le joueur {joueur_courant} a gagné !", align = "center", font = ("Arial", TAILLE_ECRITURE, "normal"))
    if (turtle.textinput("Rejouer ?", "Rejouer ? Veuillez entrer 'oui' si c'est le cas.") == 'oui'):
        return True
    return False

def quitter(x = 0, y = 0):
    """Fonction pour quitter le jeu et fermer le programme"""
    turtle.bye()
    sys.exit(0)
   
def main():
    """Fonction principale"""
    initialise_fenetre()
    afficher_partie(nombre_allumettes, joueur_courant)
    turtle.listen()
    debloque_clavier()
    turtle.onscreenclick(quitter, 3)

if __name__ == "__main__":
    main()
    turtle.mainloop()

Comme vous pouvez le voir avec les noms et les commentaires, le but de chaque fonction est assez explicite. Ici, j’ai particulièrement fait attention à désactiver les événements en provenance du clavier lorsque des traitements étaient en cours (que la partie était en train d’être affichée par exemple), sinon il y aurait eu des problèmes vu que la fonction joue aurait été appelée plusieurs fois de suite. Comme vous pouvez le tester, on relâche bien les touches selon notre choix et le clique droit permet de quitter en utilisant la fonction de turtle permettant de clore la fenêtre : bye.

Vous pouvez améliorer le programme de diverses façons, en rajoutant une IA et en demandant au joueur s’il souhaite affronter un joueur humain ou celle-ci par exemple, ou encore en demandant aux joueurs de saisir leur pseudo.


Voilà, avec cette dynamique nouvelle, nos programmes continuent de s’enrichir. C’est le moment de voir comment paramétrer encore plus notre fenêtre.