# ############################################### # Puissance 4 avec Turtle et avec indicateur de coup # Version 3 # IA Minimax avec Élagage Alpha-Bêta # Compatible Python 3.2.5+ # Juin 2026 # nsi.gecif.net # ############################################### """Problème corrigé : Quand l'IA calcule plusieurs coups d'avance, elle joue parfois de manière très subtile à l'autre bout de la grille, et on peut vite rater son coup. Pour mettre en valeur la colonne où l'ordinateur vient de poser son pion, nous allons ajouter un indicateur visuel (une flèche ou un marqueur coloré) juste au-dessus de la colonne concernée. À chaque fois que l'ordinateur joue, le marqueur se déplace au-dessus de son dernier coup. Voici le code mis à jour. J'ai ajouté une tortue dédiée uniquement à ce marqueur pour ne pas ralentir le reste des graphismes. Qu'est-ce qui a changé ? La fonction marquer_colonne_ia(colonne) : Elle calcule précisément l'axe de la colonne que l'IA vient de choisir et dessine un petit triangle jaune pointant vers le bas juste au-dessus du plateau. Le retour de coup_ordinateur() : La fonction renvoie désormais le numéro de la colonne choisie pour que l'interface graphique sache immédiatement où placer le triangle. Agrandissement de la fenêtre : J'ai légèrement augmenté la hauteur de la fenêtre (fenetre.setup(500, 560)) pour que le marqueur visuel soit parfaitement visible au-dessus de la ligne du haut sans être coupé par le bord de l'écran. Maintenant, vous saurez exactement où l'ordinateur place ses pions pour vous bloquer ! """ import turtle import random LIGNES = 6 COLONNES = 7 TAILLE = 60 grille = [[0 for j in range(COLONNES)] for i in range(LIGNES)] joueur = 1 # humain (Rouge) ordinateur = 2 # IA (Jaune) PROFONDEUR_MAX = 6 fenetre = turtle.Screen() fenetre.title("Puissance 4 - IA Expert avec Indicateur - nsi.gecif.net") dessin = turtle.Turtle() dessin.hideturtle() dessin.speed(0) message = turtle.Turtle() message.hideturtle() message.penup() message.goto(0, -230) # Nouvelle tortue dédiée uniquement à l'indicateur du dernier coup de l'IA indicateur = turtle.Turtle() indicateur.hideturtle() indicateur.speed(0) jeu_termine = False def dessiner_grille(): dessin.clear() for ligne in range(LIGNES): for colonne in range(COLONNES): x = colonne * TAILLE - 210 y = ligne * TAILLE - 180 dessin.penup() dessin.goto(x, y) if grille[ligne][colonne] == 0: couleur = "white" elif grille[ligne][colonne] == 1: couleur = "red" else: couleur = "yellow" dessin.fillcolor(couleur) dessin.begin_fill() for _ in range(4): dessin.pendown() dessin.forward(TAILLE) dessin.left(90) dessin.end_fill() fenetre.update() def marquer_colonne_ia(colonne): """Dessine une flèche jaune pointant vers le bas au-dessus de la colonne choisie par l'IA.""" indicateur.clear() # Calcul de la position X centrée sur la colonne x_centre = (colonne * TAILLE - 210) + (TAILLE / 2) # Position Y juste au-dessus de la grille y_haut = (LIGNES * TAILLE - 180) + 15 indicateur.penup() indicateur.goto(x_centre-6, y_haut+30) indicateur.setheading(0) # Pointe vers la droite # Dessin d'une flèche/triangle indicateur.fillcolor("yellow") indicateur.pencolor("black") indicateur.pensize(2) indicateur.begin_fill() indicateur.pendown() # Forme de la flèche indicateur.forward(15) indicateur.right(90) indicateur.forward(25) indicateur.left(90) indicateur.forward(10) indicateur.right(135) indicateur.forward(25) indicateur.right(90) indicateur.forward(25) indicateur.right(135) indicateur.forward(11) indicateur.left(90) indicateur.forward(25) indicateur.end_fill() fenetre.update() def jouer(colonne, pion): for ligne in range(LIGNES): if grille[ligne][colonne] == 0: grille[ligne][colonne] = pion return True return False def annuler_coup(colonne): for ligne in reversed(range(LIGNES)): if grille[ligne][colonne] != 0: grille[ligne][colonne] = 0 break def colonne_valide(colonne): return grille[LIGNES - 1][colonne] == 0 def victoire(pion): # Horizontal for l in range(LIGNES): for c in range(COLONNES - 3): if (grille[l][c] == pion and grille[l][c+1] == pion and grille[l][c+2] == pion and grille[l][c+3] == pion): return True # Vertical for l in range(LIGNES - 3): for c in range(COLONNES): if (grille[l][c] == pion and grille[l+1][c] == pion and grille[l+2][c] == pion and grille[l+3][c] == pion): return True # Diagonale montante for l in range(LIGNES - 3): for c in range(COLONNES - 3): if (grille[l][c] == pion and grille[l+1][c+1] == pion and grille[l+2][c+2] == pion and grille[l+3][c+3] == pion): return True # Diagonale descendante for l in range(3, LIGNES): for c in range(COLONNES - 3): if (grille[l][c] == pion and grille[l-1][c+1] == pion and grille[l-2][c+2] == pion and grille[l-3][c+3] == pion): return True return False def grille_pleine(): for c in range(COLONNES): if colonne_valide(c): return False return True # ========================================== # PARTIE IA # ========================================== def evaluer_fenetre(fenetre_4_pions): score = 0 nb_ia = fenetre_4_pions.count(ordinateur) nb_joueur = fenetre_4_pions.count(joueur) nb_vide = fenetre_4_pions.count(0) if nb_ia == 4: score += 10000 elif nb_ia == 3 and nb_vide == 1: score += 100 elif nb_ia == 2 and nb_vide == 2: score += 10 if nb_joueur == 3 and nb_vide == 1: score -= 200 elif nb_joueur == 2 and nb_vide == 2: score -= 20 return score def evaluer_grille(): score_total = 0 colonne_centre = [grille[l][3] for l in range(LIGNES)] score_total += colonne_centre.count(ordinateur) * 6 for l in range(LIGNES): ligne_complete = grille[l] for c in range(COLONNES - 3): score_total += evaluer_fenetre(ligne_complete[c:c+4]) for c in range(COLONNES): colonne_complete = [grille[l][c] for l in range(LIGNES)] for l in range(LIGNES - 3): score_total += evaluer_fenetre(colonne_complete[l:l+4]) for l in range(LIGNES - 3): for c in range(COLONNES - 3): score_total += evaluer_fenetre([grille[l+i][c+i] for i in range(4)]) score_total += evaluer_fenetre([grille[l+3-i][c+i] for i in range(4)]) return score_total def obtenir_coups_ordonnes(): ordre_ideal = [3, 2, 4, 1, 5, 0, 6] return [c for c in ordre_ideal if colonne_valide(c)] def minimax_alphabeta(profondeur, alpha, beta, maximisation): est_terminal = victoire(joueur) or victoire(ordinateur) or grille_pleine() if profondeur == 0 or est_terminal: if est_terminal: if victoire(ordinateur): return 1000000 + profondeur elif victoire(joueur): return -1000000 - profondeur else: return 0 return evaluer_grille() coups_possibles = obtenir_coups_ordonnes() if maximisation: valeur_max = float('-inf') for c in coups_possibles: jouer(c, ordinateur) score = minimax_alphabeta(profondeur - 1, alpha, beta, False) annuler_coup(c) valeur_max = max(valeur_max, score) alpha = max(alpha, score) if beta <= alpha: break return valeur_max else: valeur_min = float('inf') for c in coups_possibles: jouer(c, joueur) score = minimax_alphabeta(profondeur - 1, alpha, beta, True) annuler_coup(c) valeur_min = min(valeur_min, score) beta = min(beta, score) if beta <= alpha: break return valeur_min def coup_ordinateur(): meilleur_score = float('-inf') meilleures_colonnes = [] coups_possibles = obtenir_coups_ordonnes() if len(coups_possibles) == COLONNES and grille[0][3] == 0: jouer(3, ordinateur) return 3 # Retourne la colonne jouée alpha = float('-inf') beta = float('inf') for c in coups_possibles: jouer(c, ordinateur) score_coup = minimax_alphabeta(PROFONDEUR_MAX - 1, alpha, beta, False) annuler_coup(c) if score_coup > meilleur_score: meilleur_score = score_coup meilleures_colonnes = [c] elif score_coup == meilleur_score: meilleures_colonnes.append(c) if meilleures_colonnes: choix = random.choice(meilleures_colonnes) jouer(choix, ordinateur) return choix # Renvoie la colonne sélectionnée par l'IA return None # ========================================== def afficher(texte): message.clear() message.write(texte, align="center", font=("Arial", 16, "normal")) def clic(x, y): global jeu_termine if jeu_termine: return colonne = int((x + 210) // TAILLE) if colonne < 0 or colonne >= COLONNES or not colonne_valide(colonne): return # --- TOUR DU JOUEUR --- jouer(colonne, joueur) dessiner_grille() if victoire(joueur): afficher("Vous avez gagné !") jeu_termine = True return if grille_pleine(): afficher("Match nul.") jeu_termine = True return fenetre.onclick(None) afficher("L'ordinateur réfléchit...") # --- TOUR DE L'ORDINATEUR --- colonne_ia = coup_ordinateur() dessiner_grille() # Si l'IA a joué, on affiche le marqueur au-dessus de sa colonne if colonne_ia is not None: marquer_colonne_ia(colonne_ia) if victoire(ordinateur): afficher("L'ordinateur a gagné.") jeu_termine = True return if grille_pleine(): afficher("Match nul.") jeu_termine = True return afficher("À votre tour ! Cliquez dans une colonne") fenetre.onclick(clic) fenetre.setup(500, 560) # Légère augmentation de la hauteur pour laisser place au curseur fenetre.tracer(0) dessiner_grille() afficher("Cliquez dans une colonne") fenetre.onclick(clic) fenetre.mainloop()