# ################################## # Pipopipette # Version 6 # nsi.gecif.net # Juin 2026 # ################################## """ Pour transformer ce jeu en une version Humain contre Ordinateur, nous devons programmer une Intelligence Artificielle (IA). Dans la fonction au_clic, dès que l'Humain a terminé de tracer sa ligne (clic gauche standard), si aucun carré n'est fermé, c'est au tour de l'ordinateur. L'ordinateur va alors analyser la grille et choisir une ligne selon une stratégie logique simple mais efficace : - Priorité 1 (Attaque) : Si une ligne permet de fermer un carré et de gagner un point, il la joue immédiatement. - Priorité 2 (Défense) : S'il n'y a pas de point à gagner, il cherche une ligne qui ne donne pas d'opportunité à l'humain (il évite de tracer le 3e côté d'un carré). - Priorité 3 (Défaut) : S'il n'a pas le choix, il choisit une ligne restante au hasard. """ import turtle import random # --- CONFIGURATION ET VARIABLES GLOBALES --- NB_DOTS = 5 ESPACE = 60 ORIGINE_X = 0 ORIGINE_Y = 0 joueur_actuel = "Humain" # L'Humain est Rouge, l'Ordinateur est Bleu 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 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 = "Humain" score = {"Humain": 0, "Ordinateur": 0} lignes_tracees = set() carres_valides = set() stylo.clear() dessiner_grille() dessiner_bouton_recommencer() mettre_a_jour_tableau_scores() 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): """Vérifie si des carrés ont été fermés et met à jour les scores.""" 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): """Effectue le tracé vectoriel à l'écran.""" 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(): """Retourne la liste de toutes les lignes encore disponibles sur le plateau.""" 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): """Compte combien de lignes sont déjà tracées sur un carré de coordonnées (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(): """Moteur logique de choix de l'IA.""" global joueur_actuel lignes_dispo = lister_lignes_restantes() if not lignes_dispo: return ligne_choisie = None # STRATÉGIE 1 : ATTAQUE (Chercher s'il y a un carré qui possède déjà 3 côtés) for l in lignes_dispo: lignes_tracees.add(l) # Simulation temporaire # Si cette ligne ferme un carré, on la valide carrés_potentiels = [] x1, y1 = l[0]; x2, y2 = l[1] if y1 == y2: carrés_potentiels = [(min(x1, x2), y1 - 1), (min(x1, x2), y1)] else: carrés_potentiels = [(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) # Annulation simulation if ligne_choisie: break # STRATÉGIE 2 : DÉFENSE (Choisir une ligne sûre qui ne crée pas de carré à 3 côtés) if not ligne_choisie: lignes_sures = [] for l in lignes_dispo: donne_opportunite = False lignes_tracees.add(l) # Regarder si cette ligne fait passer un carré adjacent à un score de 3 côtés 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 (Si aucun coup n'est sûr, prendre une ligne au hasard) if not ligne_choisie: ligne_choisie = random.choice(lignes_dispo) # Validation et exécution du coup de l'IA 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: # L'ordinateur rejoue (Tour bonus) mettre_a_jour_tableau_scores() verifier_fin_de_partie() # Appel récursif pour qu'elle continue sa série jouer_ordinateur() else: # Rend la main à l'Humain 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 # Bouton Recommencer if BTN_X1 <= px <= BTN_X2 and BTN_Y1 <= py <= BTN_Y2: stylo_score.clear() configurer_partie() return # Bloquer le clic si ce n'est pas le tour de l'humain ou si c'est fini 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: # Passe le tour à l'ordinateur joueur_actuel = "Ordinateur" mettre_a_jour_tableau_scores() verifier_fin_de_partie() # L'ordinateur joue après un léger délai artificiel pour le confort visuel turtle.ontimer(jouer_ordinateur, 400) else: # L'humain a fermé un carré, il garde la main 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()