# ################################## # Pipopipette # Version 3 # nsi.gecif.net # Juin 2026 # ################################## import turtle # --- CONFIGURATION ET VARIABLES GLOBALES --- NB_DOTS = turtle.numinput("Configuration", "Taille de la grille (de 5 à 12 points) :", default=5, minval=5, maxval=12) if NB_DOTS is None: NB_DOTS = 5 else: NB_DOTS = int(NB_DOTS) ESPACE = 60 ORIGINE_X = -((NB_DOTS - 1) * ESPACE) / 2 ORIGINE_Y = ((NB_DOTS - 1) * ESPACE) / 2 - 30 # Rabaissé de 30px pour laisser de la place au texte en haut joueur_actuel = "Rouge" score = {"Rouge": 0, "Bleu": 0} lignes_tracees = set() carres_valides = set() # --- FONCTIONS REQUISITES --- def coord_vers_pixel(cx, cy): """Convertit des coordonnées de grille (0 à NB_DOTS-1) en pixels à l'écran.""" px = ORIGINE_X + (cx * ESPACE) py = ORIGINE_Y - (cy * ESPACE) return px, py def dessiner_grille(): """Dessine tous les points de la grille initiale.""" 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 mettre_a_jour_tableau_scores(): """Efface l'ancien texte et réécrit les scores actuels en haut de la fenêtre.""" turtle.tracer(0) stylo_score.clear() # Efface uniquement le texte écrit par ce stylo stylo_score.penup() # Positionnement tout en haut au centre de la fenêtre stylo_score.goto(0, 340) # Détermination de la couleur du texte indicateur du tour stylo_score.color("red" if joueur_actuel == "Rouge" else "blue") # Formatage de la chaîne de caractères (compatible Python 3.2.5) texte = "Tour : {0} | Score -> Rouge : {1} - Bleu : {2}" stylo_score.write(texte.format(joueur_actuel, score["Rouge"], score["Bleu"]), align="center", font=("Arial", 16, "bold")) turtle.update() def colorier_carre(cx, cy, couleur): """Rempli l'intérieur d'un carré fermé de 1x1 avec la couleur du joueur.""" 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 la ligne tracée ferme un ou deux carrés adjacents.""" 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 def au_clic(px, py): """Gère l'analyse géométrique lors d'un clic de souris à l'écran.""" global joueur_actuel total_carres_possibles = (NB_DOTS - 1) * (NB_DOTS - 1) if len(carres_valides) == total_carres_possibles: return 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: turtle.tracer(0) stylo_score.clear() stylo_score.penup() stylo_score.goto(0, 340) stylo_score.color("black") if score["Rouge"] == score["Bleu"]: res = "PARTIE TERMINÉE : Égalité parfaite !" else: gagnant = "Rouge" if score["Rouge"] > score["Bleu"] else "Bleu" res = "PARTIE TERMINÉE : Victoire du joueur {0} !".format(gagnant) stylo_score.write(res, align="center", font=("Arial", 16, "bold")) turtle.update() # --- INITIALISATION DE L'INTERFACE TURTLE --- turtle.setup(width=800, height=800) turtle.title("La Pipopipette") # Stylo principal (Grille et lignes) stylo = turtle.Turtle() stylo.hideturtle() stylo.speed(0) # Évolution : Second stylo indépendant (Texte des scores uniquement) stylo_score = turtle.Turtle() stylo_score.hideturtle() stylo_score.speed(0) # Tracé initial et premier affichage des scores dessiner_grille() mettre_a_jour_tableau_scores() turtle.onscreenclick(au_clic) turtle.listen() turtle.mainloop()