# ################################## # Pipopipette # Version 7 # nsi.gecif.net # Juin 2026 # ################################## """ Pour intégrer le choix du premier joueur en début de partie, nous allons utiliser une seconde boîte de dialogue native (textinput) juste après celle de la taille. L'utilisateur pourra y taper "H" pour commencer ou "I" pour laisser l'ordinateur ouvrir la partie. Si l'ordinateur est choisi, le programme déclenche automatiquement son premier coup dès la fin de l'initialisation. """ import turtle import random # --- CONFIGURATION ET VARIABLES GLOBALES --- NB_DOTS = 5 ESPACE = 60 ORIGINE_X = 0 ORIGINE_Y = 0 joueur_actuel = "Humain" score = {"Humain": 0, "Ordinateur": 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, le premier joueur et réinitialise le jeu.""" global NB_DOTS, ORIGINE_X, ORIGINE_Y, joueur_actuel, score, lignes_tracees, carres_valides # 1. Choix de la taille 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) # 2. ÉVOLUTION : Choix de qui commence choix = turtle.textinput("Configuration", "Qui commence ? Entrez 'H' pour Humain ou 'I' pour IA :") if choix and choix.strip().upper() == "I": joueur_actuel = "Ordinateur" else: joueur_actuel = "Humain" # Par défaut ou si 'H' ORIGINE_X = -((NB_DOTS - 1) * ESPACE) / 2 ORIGINE_Y = ((NB_DOTS - 1) * ESPACE) / 2 - 40 score = {"Humain": 0, "Ordinateur": 0} lignes_tracees = set() carres_valides = set() stylo.clear() dessiner_grille() dessiner_bouton_recommencer() mettre_a_jour_tableau_scores() # 3. Si l'IA doit commencer, on lance son premier coup après un court instant if joueur_actuel == "Ordinateur": turtle.ontimer(jouer_ordinateur, 600) def coord_vers_pixel(cx, cy): px = ORIGINE_X + (cx * ESPACE) py = ORIGINE_Y - (cy * ESPACE) return px, py def dessiner_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(): 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() 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() 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(): turtle.tracer(0) stylo_score.penup() stylo_score.goto(-50, ORIGINE_Y + 40) 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() stylo_score.penup() stylo_score.goto(-50, ORIGINE_Y + 40) stylo_score.color("red" if joueur_actuel == "Humain" else "blue") texte = "Tour : {0} | Score -> Humain : {1} - IA : {2}" stylo_score.write(texte.format(joueur_actuel, score["Humain"], score["Ordinateur"]), align="center", font=("Arial", 14, "bold")) turtle.update() def colorier_carre(cx, cy, couleur): px, py = coord_vers_pixel(cx, cy) stylo.penup() stylo.goto(px + 4, py - 4) stylo.color("#ffb3b3" if couleur == "Humain" else "#b3d1ff") 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): 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, couleur) score[couleur] += 1 carre_cree = True return carre_cree def tracer_ligne_graphique(ligne, couleur): p1_px, p1_py = coord_vers_pixel(ligne[0][0], ligne[0][1]) p2_px, p2_py = coord_vers_pixel(ligne[1][0], ligne[1][1]) turtle.tracer(0) stylo.penup() stylo.goto(p1_px, p1_py) stylo.pendown() stylo.pensize(5) stylo.color("red" if couleur == "Humain" else "blue") stylo.goto(p2_px, p2_py) turtle.update() # --- INTELLIGENCE ARTIFICIELLE (IA) --- def lister_lignes_restantes(): dispo = [] for r in range(NB_DOTS): for c in range(NB_DOTS): if c < NB_DOTS - 1: l = ((c, r), (c + 1, r)) if l not in lignes_tracees: dispo.append(l) if r < NB_DOTS - 1: l = ((c, r), (c, r + 1)) if l not in lignes_tracees: dispo.append(l) return dispo def compter_cotes_carre(cx, cy): 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)) compte = 0 if l_haut in lignes_tracees: compte += 1 if l_bas in lignes_tracees: compte += 1 if l_gauche in lignes_tracees: compte += 1 if l_droite in lignes_tracees: compte += 1 return compte def jouer_ordinateur(): global joueur_actuel lignes_dispo = lister_lignes_restantes() if not lignes_dispo: return ligne_choisie = None # STRATÉGIE 1 : ATTAQUE for l in lignes_dispo: lignes_tracees.add(l) x1, y1 = l[0]; x2, y2 = l[1] carrés_potentiels = [(min(x1, x2), y1 - 1), (min(x1, x2), y1)] if y1 == y2 else [(x1 - 1, min(y1, y2)), (x1, min(y1, y2))] for (cx, cy) in carrés_potentiels: if 0 <= cx < NB_DOTS - 1 and 0 <= cy < NB_DOTS - 1: if compter_cotes_carre(cx, cy) == 4 and (cx, cy) not in carres_valides: ligne_choisie = l lignes_tracees.remove(l) if ligne_choisie: break # STRATÉGIE 2 : DÉFENSE if not ligne_choisie: lignes_sures = [] for l in lignes_dispo: donne_opportunite = False lignes_tracees.add(l) x1, y1 = l[0]; x2, y2 = l[1] carrés_potentiels = [(min(x1, x2), y1 - 1), (min(x1, x2), y1)] if y1 == y2 else [(x1 - 1, min(y1, y2)), (x1, min(y1, y2))] for (cx, cy) in carrés_potentiels: if 0 <= cx < NB_DOTS - 1 and 0 <= cy < NB_DOTS - 1: if compter_cotes_carre(cx, cy) == 3: donne_opportunite = True lignes_tracees.remove(l) if not donne_opportunite: lignes_sures.append(l) if lignes_sures: ligne_choisie = random.choice(lignes_sures) # STRATÉGIE 3 : PAR DÉFAUT if not ligne_choisie: ligne_choisie = random.choice(lignes_dispo) lignes_tracees.add(ligne_choisie) tracer_ligne_graphique(ligne_choisie, "Ordinateur") un_carre_a_ferme = verifier_carrés_fermes(ligne_choisie[0], ligne_choisie[1], "Ordinateur") if un_carre_a_ferme: mettre_a_jour_tableau_scores() verifier_fin_de_partie() jouer_ordinateur() else: joueur_actuel = "Humain" mettre_a_jour_tableau_scores() def verifier_fin_de_partie(): total_carres_possibles = (NB_DOTS - 1) * (NB_DOTS - 1) if len(carres_valides) == total_carres_possibles: stylo_score.penup() stylo_score.goto(-50, ORIGINE_Y + 40) stylo_score.color("black") if score["Humain"] == score["Ordinateur"]: res = "PARTIE TERMINÉE : Égalité !" else: gagnant = "Humain" if score["Humain"] > score["Ordinateur"] else "L'IA" res = "PARTIE TERMINÉE : Victoire de {0} !".format(gagnant) stylo_score.write(res, align="center", font=("Arial", 14, "bold")) turtle.update() # --- GESTION DU CLIC --- def au_clic(px, py): global joueur_actuel if BTN_X1 <= px <= BTN_X2 and BTN_Y1 <= py <= BTN_Y2: stylo_score.clear() configurer_partie() return if joueur_actuel != "Humain": return if len(carres_valides) == (NB_DOTS - 1) * (NB_DOTS - 1): 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) dist = ((px - (x1+x2)/2)**2 + (py - (y1+y2)/2)**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) dist = ((px - (x1+x2)/2)**2 + (py - (y1+y2)/2)**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) tracer_ligne_graphique(ligne_choisie, "Humain") un_carre_a_ferme = verifier_carrés_fermes(ligne_choisie[0], ligne_choisie[1], "Humain") if not un_carre_a_ferme: joueur_actuel = "Ordinateur" mettre_a_jour_tableau_scores() verifier_fin_de_partie() turtle.ontimer(jouer_ordinateur, 400) else: mettre_a_jour_tableau_scores() verifier_fin_de_partie() # --- INITIALISATION --- turtle.setup(width=800, height=800, startx=0, starty=0) turtle.title("La Pipopipette vs Ordinateur") stylo = turtle.Turtle(); stylo.hideturtle(); stylo.speed(0) stylo_score = turtle.Turtle(); stylo_score.hideturtle(); stylo_score.speed(0) configurer_partie() turtle.onscreenclick(au_clic) turtle.listen() turtle.mainloop()