# ############################################### # Puissance 4 avec Turtle # Version 1 # IA Minimax # Compatible Python 3.2.5+ # Juin 2026 # nsi.gecif.net # ############################################### """ Pour transformer l'intelligence artificielle de aléatoire à redoutable, nous allons implémenter l'algorithme Minimax couplé à un système d'évaluation de la grille (l'heuristique). Pour que le code reste fluide et réactif (car le Puissance 4 possède des milliards de combinaisons), nous allons limiter la recherche à une profondeur donnée (par exemple, 4 ou 5 coups d'avance) et ajouter une fonction qui attribue des points aux alignements prometteurs (3 pions alignés avec une case vide, etc.). Voici le code complet et amélioré. Les fonctions evaluer_fenetre, evaluer_grille, minimax et la mise à jour de coup_ordinateur ont été intégrées de manière transparente. Ce qui a changé : 1 - Fonction annuler_coup : Elle permet à l'algorithme de simuler un coup dans la grille (en posant un pion fictif), de voir ce qui se passe, puis de retirer le pion pour tester une autre colonne sans casser la vraie partie. 2 - Fonction evaluer_fenetre & evaluer_grille : L'ordinateur "note" la grille. 4 pions alignés = jackpot. 3 pions de l'ordinateur + 1 vide = très bon coup. 3 pions du joueur humain + 1 vide = danger de défaite, l'algorithme attribue une note très basse pour forcer l'ordinateur à bloquer le joueur à cet endroit. 3 - Fonction minimax : C'est le cœur de l'arbre de décision. Elle simule l'alternance des coups (l'ordinateur cherche à maximiser sa note, le joueur cherche à la minimiser). 4 - Fluidité Turtle : J'ai temporairement désactivé les clics de souris avec fenetre.onclick(None) pendant que l'ordinateur réfléchit, afin d'éviter qu'un clic frénétique de l'utilisateur pendant le calcul ne fasse planter le jeu.""" 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 de recherche Minimax (4 ou 5 est un bon compromis vitesse/intelligence) PROFONDEUR_MAX = 4 fenetre = turtle.Screen() fenetre.title("Puissance 4 - IA Minimax - nsi.gecif.net") dessin = turtle.Turtle() dessin.hideturtle() dessin.speed(0) message = turtle.Turtle() message.hideturtle() message.penup() message.goto(0, -230) 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 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): """Enlève le pion supérieur de la colonne spécifiée.""" 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 : MINIMAX & HEURISTIQUE # ========================================== def evaluer_fenetre(fenetre_4_pions): """Attribue un score à une section de 4 cases alignées.""" score = 0 nb_ia = fenetre_4_pions.count(ordinateur) nb_joueur = fenetre_4_pions.count(joueur) nb_vide = fenetre_4_pions.count(0) # Scores pour l'ordinateur (Maximise) if nb_ia == 4: score += 1000 elif nb_ia == 3 and nb_vide == 1: score += 10 elif nb_ia == 2 and nb_vide == 2: score += 2 # Scores pour contrer le joueur (Minimise le danger) if nb_joueur == 3 and nb_vide == 1: score -= 80 # Priorité haute au blocage return score def evaluer_grille(): """Analyse l'ensemble de la grille pour donner une note globale.""" score_total = 0 # Favoriser le centre de la grille (stratégie clé au Puissance 4) colonne_centre = [grille[l][COLONNES // 2] for l in range(LIGNES)] score_total += colonne_centre.count(ordinateur) * 4 # Évaluation Horizontale for l in range(LIGNES): ligne_complete = grille[l] for c in range(COLONNES - 3): fenetre_4 = ligne_complete[c:c+4] score_total += evaluer_fenetre(fenetre_4) # Évaluation Verticale for c in range(COLONNES): colonne_complete = [grille[l][c] for l in range(LIGNES)] for l in range(LIGNES - 3): fenetre_4 = colonne_complete[l:l+4] score_total += evaluer_fenetre(fenetre_4) # Évaluation Diagonale montante for l in range(LIGNES - 3): for c in range(COLONNES - 3): fenetre_4 = [grille[l+i][c+i] for i in range(4)] score_total += evaluer_fenetre(fenetre_4) # Évaluation Diagonale descendante for l in range(3, LIGNES): for c in range(COLONNES - 3): fenetre_4 = [grille[l-i][c+i] for i in range(4)] score_total += evaluer_fenetre(fenetre_4) return score_total def minimax(profondeur, maximisation): """Algorithme Minimax de base pour déterminer le meilleur score.""" est_terminal = victoire(joueur) or victoire(ordinateur) or grille_pleine() if profondeur == 0 or est_terminal: if est_terminal: if victoire(ordinateur): return 100000 + profondeur # Préférer gagner rapidement elif victoire(joueur): return -100000 - profondeur # Préférer perdre le plus tard possible else: return 0 # Match nul else: return evaluer_grille() # Liste des coups possibles coups_possibles = [c for c in range(COLONNES) if colonne_valide(c)] if maximisation: valeur_max = float('-inf') for c in coups_possibles: jouer(c, ordinateur) score = minimax(profondeur - 1, False) annuler_coup(c) valeur_max = max(valeur_max, score) return valeur_max else: valeur_min = float('inf') for c in coups_possibles: jouer(c, joueur) score = minimax(profondeur - 1, True) annuler_coup(c) valeur_min = min(valeur_min, score) return valeur_min def coup_ordinateur(): """Sélectionne le meilleur coup en utilisant Minimax.""" meilleur_score = float('-inf') meilleures_colonnes = [] coups_possibles = [c for c in range(COLONNES) if colonne_valide(c)] # Si c'est le tout premier coup du jeu, jouer au centre est optimal if len(coups_possibles) == COLONNES and grille[0][3] == 0: jouer(3, ordinateur) return for c in coups_possibles: jouer(c, ordinateur) score_coup = minimax(PROFONDEUR_MAX - 1, 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: # S'il y a des égalités de score, on pioche au hasard parmi elles jouer(random.choice(meilleures_colonnes), ordinateur) # ========================================== 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: return if 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 # Désactiver temporairement les clics pendant que l'IA réfléchit fenetre.onclick(None) afficher("L'ordinateur réfléchit...") # --- TOUR DE L'ORDINATEUR --- coup_ordinateur() dessiner_grille() if victoire(ordinateur): afficher("L'ordinateur a gagné.") jeu_termine = True return if grille_pleine(): afficher("Match nul.") jeu_termine = True return # Réactiver les clics une fois le coup de l'IA joué afficher("À votre tour ! Cliquez dans une colonne") fenetre.onclick(clic) fenetre.setup(500, 500) fenetre.tracer(0) dessiner_grille() afficher("Cliquez dans une colonne") fenetre.onclick(clic) fenetre.mainloop()