# ################################## # Pipopipette # Version 5 # nsi.gecif.net # Juin 2026 # ################################## """ Pour ajouter un bouton "Recommencer" directement dans la fenêtre Turtle, nous allons dessiner un rectangle cliquable dans un coin de l'écran (par exemple, en haut à droite) et modifier la fonction au_clic. Lorsque le joueur clique, le programme vérifie d'abord si les coordonnées du clic se trouvent à l'intérieur de ce rectangle. Si c'est le cas, il réinitialise toutes les variables, efface l'écran et redemande la taille de la grille. """ import turtle # --- CONFIGURATION ET VARIABLES GLOBALES --- NB_DOTS = 5 ESPACE = 60 ORIGINE_X = 0 ORIGINE_Y = 0 joueur_actuel = "Rouge" score = {"Rouge": 0, "Bleu": 0} lignes_tracees = set() carres_valides = set() # Coordonnées de la boîte du bouton "Recommencer" BTN_X1, BTN_Y1 = 220, 300 BTN_X2, BTN_Y2 = 380, 335 # --- FONCTIONS DE CONFIGURATION ET TRACÉ --- def configurer_partie(): """Demande la taille et réinitialise les variables du jeu.""" global NB_DOTS, ORIGINE_X, ORIGINE_Y, joueur_actuel, score, lignes_tracees, carres_valides taille = turtle.numinput("Configuration", "Taille de la grille (de 5 à 12 points) :", default=5, minval=5, maxval=12) if taille is None: NB_DOTS = 5 else: NB_DOTS = int(taille) ORIGINE_X = -((NB_DOTS - 1) * ESPACE) / 2 ORIGINE_Y = ((NB_DOTS - 1) * ESPACE) / 2 - 40 joueur_actuel = "Rouge" score = {"Rouge": 0, "Bleu": 0} lignes_tracees = set() carres_valides = set() # Réinitialisation des crayons stylo.clear() dessiner_grille() dessiner_bouton_recommencer() mettre_a_jour_tableau_scores() def coord_vers_pixel(cx, cy): """Convertit des coordonnées de grille en pixels.""" px = ORIGINE_X + (cx * ESPACE) py = ORIGINE_Y - (cy * ESPACE) return px, py def dessiner_grille(): """Dessine tous les points de la grille.""" turtle.tracer(0) for r in range(NB_DOTS): for c in range(NB_DOTS): px, py = coord_vers_pixel(c, r) stylo.penup() stylo.goto(px, py) stylo.pendown() stylo.dot(10, "#333333") turtle.update() def dessiner_bouton_recommencer(): """Dessine le bouton graphique 'Recommencer' en haut à droite.""" turtle.tracer(0) stylo_score.penup() stylo_score.goto(BTN_X1, BTN_Y1) stylo_score.pendown() stylo_score.color("black", "#e0e0e0") stylo_score.begin_fill() # Dessin du rectangle for _ in range(2): stylo_score.forward(BTN_X2 - BTN_X1) stylo_score.left(90) stylo_score.forward(BTN_Y2 - BTN_Y1) stylo_score.left(90) stylo_score.end_fill() # Texte du bouton stylo_score.penup() stylo_score.goto((BTN_X1 + BTN_X2) / 2, BTN_Y1 + 7) stylo_score.write("Recommencer", align="center", font=("Arial", 10, "bold")) turtle.update() def mettre_a_jour_tableau_scores(): """Met à jour les scores textuels sans effacer le bouton.""" turtle.tracer(0) # On n'utilise pas .clear() pour ne pas effacer le bouton, on redessine par-dessus # Une astuce consiste à effacer une zone spécifique ou réécrire stylo_score.penup() stylo_score.goto(-50, ORIGINE_Y + 40) # Pour simuler le clear du texte uniquement, on nettoie la zone texte avant d'écrire stylo_score.color("white") stylo_score.begin_fill() stylo_score.goto(-300, ORIGINE_Y + 30) for _ in range(2): stylo_score.forward(500) stylo_score.left(90) stylo_score.forward(40) stylo_score.left(90) stylo_score.end_fill() # Écriture du nouveau score stylo_score.penup() stylo_score.goto(-50, ORIGINE_Y + 40) stylo_score.color("red" if joueur_actuel == "Rouge" else "blue") texte = "Tour : {0} | Score -> Rouge : {1} - Bleu : {2}" stylo_score.write(texte.format(joueur_actuel, score["Rouge"], score["Bleu"]), align="center", font=("Arial", 14, "bold")) turtle.update() def colorier_carre(cx, cy, couleur): """Remplit l'intérieur d'un carré fermé.""" px, py = coord_vers_pixel(cx, cy) stylo.penup() stylo.goto(px + 4, py - 4) stylo.color(couleur) stylo.begin_fill() for _ in range(4): stylo.forward(ESPACE - 8) stylo.right(90) stylo.end_fill() def verifier_carrés_fermes(p1, p2, couleur): """Vérifie si des carrés ont été fermés.""" global score carre_cree = False x1, y1 = p1 x2, y2 = p2 carrés_a_tester = [] if y1 == y2: cx = min(x1, x2) carrés_a_tester.append((cx, y1 - 1)) carrés_a_tester.append((cx, y1)) else: cy = min(y1, y2) carrés_a_tester.append((x1 - 1, cy)) carrés_a_tester.append((x1, cy)) for (cx, cy) in carrés_a_tester: if 0 <= cx < NB_DOTS - 1 and 0 <= cy < NB_DOTS - 1: if (cx, cy) not in carres_valides: l_haut = ((cx, cy), (cx + 1, cy)) l_bas = ((cx, cy + 1), (cx + 1, cy + 1)) l_gauche = ((cx, cy), (cx, cy + 1)) l_droite = ((cx + 1, cy), (cx + 1, cy + 1)) if (l_haut in lignes_tracees and l_bas in lignes_tracees and l_gauche in lignes_tracees and l_droite in lignes_tracees): carres_valides.add((cx, cy)) colorier_carre(cx, cy, "#ffb3b3" if couleur == "Rouge" else "#b3d1ff") score[couleur] += 1 carre_cree = True return carre_cree # --- GESTION DU CLIC --- def au_clic(px, py): """Détecte si on clique sur le bouton Recommencer ou sur une ligne du jeu.""" global joueur_actuel # 1. Vérification du clic sur le bouton "Recommencer" if BTN_X1 <= px <= BTN_X2 and BTN_Y1 <= py <= BTN_Y2: stylo_score.clear() configurer_partie() return # Empêche de jouer si la grille est déjà pleine total_carres_possibles = (NB_DOTS - 1) * (NB_DOTS - 1) if len(carres_valides) == total_carres_possibles: return # 2. Logique de détection des lignes du jeu distance_min = 15 ligne_choisie = None for r in range(NB_DOTS): for c in range(NB_DOTS): if c < NB_DOTS - 1: x1, y1 = coord_vers_pixel(c, r) x2, y2 = coord_vers_pixel(c + 1, r) milieu_x, milieu_y = (x1 + x2) / 2, (y1 + y2) / 2 dist = ((px - milieu_x)**2 + (py - milieu_y)**2)**0.5 if dist < distance_min: distance_min = dist ligne_choisie = ((c, r), (c + 1, r)) if r < NB_DOTS - 1: x1, y1 = coord_vers_pixel(c, r) x2, y2 = coord_vers_pixel(c, r + 1) milieu_x, milieu_y = (x1 + x2) / 2, (y1 + y2) / 2 dist = ((px - milieu_x)**2 + (py - milieu_y)**2)**0.5 if dist < distance_min: distance_min = dist ligne_choisie = ((c, r), (c, r + 1)) if ligne_choisie and (ligne_choisie not in lignes_tracees): lignes_tracees.add(ligne_choisie) p1_px, p1_py = coord_vers_pixel(ligne_choisie[0][0], ligne_choisie[0][1]) p2_px, p2_py = coord_vers_pixel(ligne_choisie[1][0], ligne_choisie[1][1]) turtle.tracer(0) stylo.penup() stylo.goto(p1_px, p1_py) stylo.pendown() stylo.pensize(5) stylo.color("red" if joueur_actuel == "Rouge" else "blue") stylo.goto(p2_px, p2_py) turtle.update() un_carre_a_ferme = verifier_carrés_fermes(ligne_choisie[0], ligne_choisie[1], joueur_actuel) if not un_carre_a_ferme: joueur_actuel = "Bleu" if joueur_actuel == "Rouge" else "Rouge" mettre_a_jour_tableau_scores() if len(carres_valides) == total_carres_possibles: # Affichage de fin de partie à la place des scores stylo_score.penup() stylo_score.goto(-50, ORIGINE_Y + 40) stylo_score.color("black") if score["Rouge"] == score["Bleu"]: res = "PARTIE TERMINÉE : Égalité !" else: gagnant = "Rouge" if score["Rouge"] > score["Bleu"] else "Bleu" res = "PARTIE TERMINÉE : Victoire de {0} !".format(gagnant) stylo_score.write(res, align="center", font=("Arial", 14, "bold")) turtle.update() # --- INITIALISATION --- turtle.setup(width=800, height=800, startx=0, starty=0) turtle.title("La Pipopipette") stylo = turtle.Turtle() stylo.hideturtle() stylo.speed(0) stylo_score = turtle.Turtle() stylo_score.hideturtle() stylo_score.speed(0) # Premier lancement du jeu configurer_partie() turtle.onscreenclick(au_clic) turtle.listen() turtle.mainloop()