# ########################################################################### # Projet : Jeu d'échecs # Version 3 : l'IA utilise l'algorithme minimax pour jouer contre l'humain # Programmeurs : Emilion, Dany et Sébastien # Mai 2026 # nsi.gecif.net # ########################################################################### from turtle import * from time import sleep import random # ----------------------------------- # CONFIGURATION GENERALE # ----------------------------------- hideturtle() tracer(0) tour='blanc' dernier_piece_ia = None dernier_coup_ia = None # =================================== # FONCTION DE CLICK # =================================== def selectionner(x, y): """ Fonction appelée à chaque clic souris """ global tour if tour == "fin": return # Vérifie qu'on clique bien dans le plateau if not (-350 <= x <= 290 and -350 <= y <= 290): return # Conversion coordonnées écran -> coordonnées échiquier case_x = int((x + 350) // 80) + 1 case_y = int((y + 350) // 80) + 1 contenu = plateau.grille[case_y-1][case_x-1] # ----------------------------------- # Si on clique sur une case de déplacement possible # ----------------------------------- # ================================= # ROQUE # ================================= if contenu == "roque_droit": roi = plateau.piece_selectionnee roi.deplacer(7, roi.y) tour_piece = plateau.grille[roi.y-1][7] tour_piece.deplacer(6, roi.y) if plateau.piece_selectionnee is not None: plateau.piece_selectionnee.selectionnee = False plateau.piece_selectionnee = None plateau.supprimer_trajectoire() if tour == 'blanc': tour = 'noir' else: tour = 'blanc' if plateau.echec_et_mat(tour): up() goto(-260, 280) down() if plateau.roi_en_echec(tour): write('Echec et mat !', font=("Arial Unicode MS", 40)) else: write('Pat !', font=("Arial Unicode MS", 40)) tour = 'fin' return if tour == 'noir': jouer_bot() return if contenu == "roque_gauche": roi = plateau.piece_selectionnee roi.deplacer(3, roi.y) tour_piece = plateau.grille[roi.y-1][0] tour_piece.deplacer(4, roi.y) if plateau.piece_selectionnee is not None: plateau.piece_selectionnee.selectionnee = False plateau.piece_selectionnee = None plateau.supprimer_trajectoire() if tour == 'blanc': tour = 'noir' else: tour = 'blanc' if plateau.echec_et_mat(tour): up() goto(-260, 280) down() if plateau.roi_en_echec(tour): write('Echec et mat !', font=("Arial Unicode MS", 40)) else: write('Pat !', font=("Arial Unicode MS", 40)) tour = 'fin' return if tour == 'noir': jouer_bot() return # ================================= # PRISE EN PASSANT # ================================= if contenu == "en_passant": pion = plateau.piece_selectionnee ancien_x = pion.x cible = plateau.grille[pion.y-1][case_x-1] if isinstance(cible, Piece): cible.effacer() plateau.grille[pion.y-1][case_x-1] = None pion.deplacer(case_x, case_y) if plateau.piece_selectionnee is not None: plateau.piece_selectionnee.selectionnee = False plateau.piece_selectionnee = None plateau.supprimer_trajectoire() if tour == 'blanc': tour = 'noir' else: tour = 'blanc' if plateau.echec_et_mat(tour): up() goto(-260, 280) down() if plateau.roi_en_echec(tour): write('Echec et mat !', font=("Arial Unicode MS", 40)) else: write('Pat !', font=("Arial Unicode MS", 40)) tour = 'fin' return if tour == 'noir': jouer_bot() return if contenu == ".": piece = plateau.piece_selectionnee if piece: # Supprime les cercles bleus plateau.supprimer_trajectoire() # Déplace la pièce piece.deplacer(case_x, case_y) # Désélection piece.selectionnee = False plateau.piece_selectionnee = None if tour == 'blanc': tour = 'noir' else: tour = 'blanc' if plateau.echec_et_mat(tour): up() goto(-240,280) down() if plateau.roi_en_echec(tour): write('Echec et mat !', font=("Arial Unicode MS", 50)) else : write('Pat !', font=("Arial Unicode MS", 50)) tour = 'fin' return if tour == 'noir': jouer_bot() return if isinstance(contenu, Piece) and plateau.piece_selectionnee and contenu.joueur != plateau.piece_selectionnee.joueur: piece = plateau.piece_selectionnee if contenu.traj : # Supprime les cercles bleus plateau.supprimer_trajectoire() contenu.traj=False # Déplace la pièce piece.deplacer(case_x, case_y) # Désélection piece.selectionnee = False plateau.piece_selectionnee = None if tour == 'blanc': tour = 'noir' else: tour = 'blanc' if plateau.echec_et_mat(tour): up() goto(-260,280) down() write('Echec et mat !', font=("Arial Unicode MS", 55)) tour = 'fin' return elif plateau.pat(tour): up() goto(-260,280) down() write('Pat !', font=("Arial Unicode MS", 55)) tour = 'fin' return if tour == 'noir': jouer_bot() return # ----------------------------------- # Si on clique sur une vraie pièce # ----------------------------------- if contenu is not None and isinstance(contenu, Piece): if contenu.joueur==tour: global dernier_piece_ia if dernier_piece_ia is not None: try: if plateau.grille[dernier_piece_ia.y-1][dernier_piece_ia.x-1] is dernier_piece_ia: dernier_piece_ia.effacer() dernier_piece_ia.dessiner() except Exception: pass dernier_piece_ia = None # Supprime anciens cercles plateau.supprimer_trajectoire() # Désélectionne les autres pièces plateau.deselectionner_toutes() # Sélectionne la nouvelle pièce contenu.selectionner() plateau.piece_selectionnee = contenu # =================================== # CLASSE PLATEAU # =================================== class Plateau: def __init__(self): # Tableau 8x8 contenant les pièces self.grille = [[None for _ in range(8)] for _ in range(8)] # Liste contenant les tortues servant à dessiner les cercles self.cercles = [] # Pièce actuellement sélectionnée self.piece_selectionnee = None self.prise_en_passant = None # Dessin du plateau up() goto(-350, -350) down() couleur = "chocolate" for j in range(8): for i in range(8): begin_fill() fillcolor(couleur) for _ in range(4): fd(80) lt(90) end_fill() # Alterne les couleurs couleur = "wheat" if couleur == "chocolate" else "chocolate" fd(80) couleur = "wheat" if couleur == "chocolate" else "chocolate" lt(90) fd(80) lt(90) fd(640) seth(0) def coups_piece(self, piece): coups = [] # ===================================== # PION # ===================================== if isinstance(piece, Pion): direction = 1 if piece.joueur == "blanc" else -1 # avance d'une case nx = piece.x ny = piece.y + direction if 1 <= ny <= 8 and self.grille[ny-1][nx-1] is None: coups.append((nx, ny)) # double avance if piece.joueur == "blanc" and piece.y == 2: if self.grille[2][nx-1] is None and self.grille[3][nx-1] is None: coups.append((nx, 4)) if piece.joueur == "noir" and piece.y == 7: if self.grille[5][nx-1] is None and self.grille[4][nx-1] is None: coups.append((nx, 5)) # captures diagonales for dx in [-1, 1]: nx = piece.x + dx ny = piece.y + direction if 1 <= nx <= 8 and 1 <= ny <= 8: cible = self.grille[ny-1][nx-1] if isinstance(cible, Piece) and cible.joueur != piece.joueur: coups.append((nx, ny)) # prise en passant pion_ep = self.prise_en_passant if isinstance(pion_ep, Pion) and pion_ep.joueur != piece.joueur: if piece.joueur == "blanc" and piece.y == 5 and pion_ep.y == 5 and abs(pion_ep.x - piece.x) == 1: coups.append((pion_ep.x, 6)) if piece.joueur == "noir" and piece.y == 4 and pion_ep.y == 4 and abs(pion_ep.x - piece.x) == 1: coups.append((pion_ep.x, 3)) # ===================================== # CAVALIER # ===================================== elif isinstance(piece, Cavalier): mouvements = [ (2,1),(2,-1),(-2,1),(-2,-1), (1,2),(1,-2),(-1,2),(-1,-2) ] for dx, dy in mouvements: nx = piece.x + dx ny = piece.y + dy if 1 <= nx <= 8 and 1 <= ny <= 8: cible = self.grille[ny-1][nx-1] if cible is None or (isinstance(cible, Piece) and cible.joueur != piece.joueur): coups.append((nx, ny)) # ===================================== # TOUR / FOU / REINE # ===================================== elif isinstance(piece, (Tour, Fou, Reine)): directions = [] if isinstance(piece, Tour) or isinstance(piece, Reine): directions.extend([(0, 1), (0, -1), (1, 0), (-1, 0)]) if isinstance(piece, Fou) or isinstance(piece, Reine): directions.extend([(1, 1), (-1, -1), (1, -1), (-1, 1)]) for dx, dy in directions: nx = piece.x + dx ny = piece.y + dy while 1 <= nx <= 8 and 1 <= ny <= 8: contenu = self.grille[ny-1][nx-1] if contenu is None: coups.append((nx, ny)) elif isinstance(contenu, Piece): if contenu.joueur != piece.joueur: coups.append((nx, ny)) break else: break nx += dx ny += dy # ===================================== # ROI # ===================================== elif isinstance(piece, Roi): mouvements = [ (0, 1), (0, -1), (1, 0), (-1, 0), (1, 1), (-1, -1), (1, -1), (-1, 1) ] for dx, dy in mouvements: nx = piece.x + dx ny = piece.y + dy if 1 <= nx <= 8 and 1 <= ny <= 8: contenu = self.grille[ny-1][nx-1] if contenu is None or (isinstance(contenu, Piece) and contenu.joueur != piece.joueur): coups.append((nx, ny)) return coups # ----------------------------------- # Trouve le roi d'un joueur # ----------------------------------- def trouver_roi(self, joueur): for ligne in self.grille: for piece in ligne: if isinstance(piece, Roi) and piece.joueur == joueur: return piece return None # ----------------------------------- # Vérifie si une case est attaquée # ----------------------------------- def case_attaquee(self, x, y, joueur): """ Retourne True si une pièce ennemie peut attaquer la case (x,y) """ for ligne in self.grille: for piece in ligne: if isinstance(piece, Piece) and piece.joueur != joueur: # TOUR / REINE lignes-colonnes if isinstance(piece, (Tour, Reine)): directions = [ (0,1), (0,-1), (1,0), (-1,0) ] for dx, dy in directions: cx = piece.x + dx cy = piece.y + dy while 1 <= cx <= 8 and 1 <= cy <= 8: contenu = self.grille[cy-1][cx-1] if cx == x and cy == y: return True if contenu is not None: break cx += dx cy += dy # FOU / REINE diagonales if isinstance(piece, (Fou, Reine)): directions = [ (1,1), (-1,-1), (1,-1), (-1,1) ] for dx, dy in directions: cx = piece.x + dx cy = piece.y + dy while 1 <= cx <= 8 and 1 <= cy <= 8: contenu = self.grille[cy-1][cx-1] if cx == x and cy == y: return True if contenu is not None: break cx += dx cy += dy # CAVALIER if isinstance(piece, Cavalier): mouvements = [ (2,1), (2,-1), (-2,1), (-2,-1), (1,2), (1,-2), (-1,2), (-1,-2) ] for dx, dy in mouvements: if piece.x + dx == x and piece.y + dy == y: return True # ROI if isinstance(piece, Roi): mouvements = [ (0,1), (0,-1), (1,0), (-1,0), (1,1), (-1,-1), (1,-1), (-1,1) ] for dx, dy in mouvements: if piece.x + dx == x and piece.y + dy == y: return True # PION if isinstance(piece, Pion): if piece.joueur=='blanc' : mouvements = [ (1,1), (-1,1) ] else : mouvements = [ (1,-1), (-1,-1) ] for dx, dy in mouvements: if piece.x + dx == x and piece.y + dy == y: return True return False # ----------------------------------- # Vérifie si le roi est en échec # ----------------------------------- def roi_en_echec(self, joueur): roi = self.trouver_roi(joueur) if roi is None: return False return self.case_attaquee(roi.x, roi.y, joueur) # ----------------------------------- # Simule un coup # ----------------------------------- def coup_met_roi_en_echec(self, piece, nx, ny): ancienne_x = piece.x ancienne_y = piece.y cible = self.grille[ny-1][nx-1] # Simulation self.grille[ancienne_y-1][ancienne_x-1] = None self.grille[ny-1][nx-1] = piece piece.x = nx piece.y = ny # Les "." ne doivent pas compter comme des pièces sauvegarde_points = [] for yy in range(8): for xx in range(8): if self.grille[yy][xx] == ".": sauvegarde_points.append((xx, yy)) self.grille[yy][xx] = None resultat = self.roi_en_echec(piece.joueur) # restauration des "." for xx, yy in sauvegarde_points: self.grille[yy][xx] = "." # Annule simulation piece.x = ancienne_x piece.y = ancienne_y self.grille[ancienne_y-1][ancienne_x-1] = piece self.grille[ny-1][nx-1] = cible return resultat # ---------------------------- # Désélectionne toutes les pièces # ---------------------------- def deselectionner_toutes(self): for ligne in self.grille: for piece in ligne: if isinstance(piece, Piece) and piece.selectionnee: piece.selectionner() # ---------------------------- # Dessine un cercle bleu # ---------------------------- def dessiner_cercle(self, x, y): t = Turtle() t.hideturtle() t.speed(0) t.penup() # Centre de la case centre_x = -350 + (x-1)*80 + 40 centre_y = -350 + (y-1)*80 + 40 t.goto(centre_x, centre_y-15) t.color("blue") t.begin_fill() t.circle(15) t.end_fill() self.cercles.append(t) # ---------------------------- # Supprime tous les cercles # ---------------------------- def supprimer_trajectoire(self, graphique=True): if graphique: # Efface graphiquement for c in self.cercles: c.clear() self.cercles = [] # Supprime les "." de la grille for y in range(8): for x in range(8): if self.grille[y][x] in ['.','roque_droit','roque_gauche','en_passant']: self.grille[y][x] = None if isinstance(self.grille[y][x],Piece) : self.grille[y][x].traj=False def echec_et_mat(self, joueur): if not self.roi_en_echec(joueur): return False coups = tous_les_coups(joueur) return len(coups) == 0 def pat(self, joueur): if self.roi_en_echec(joueur): return False coups = tous_les_coups(joueur) return len(coups) == 0 # =================================== # CLASSE GENERALE PIECE # =================================== class Piece: def __init__(self, x, y, joueur, plateau): self.x = x self.y = y self.joueur = joueur self.plateau = plateau self.symbole = "" self.traj=False self.a_deja_bougée=False # True si sélectionnée self.selectionnee = False # Place la pièce dans la grille self.plateau.grille[y-1][x-1] = self # ---------------------------- # Dessine la pièce # ---------------------------- def dessiner(self, couleur="black"): up() goto(-350 + (self.x-1)*80, -350 + (self.y-1)*80) down() color(couleur) write(self.symbole, font=("Arial Unicode MS", 55)) # ---------------------------- # Efface la pièce # ---------------------------- def effacer(self): up() goto(-350 + (self.x-1)*80, -350 + (self.y-1)*80) down() # Retrouve couleur normale de la case if (self.x + self.y) % 2 == 0: couleur = "chocolate" else: couleur = "wheat" color("black", couleur) begin_fill() for _ in range(4): fd(80) lt(90) end_fill() # ---------------------------- # Sélection d'une pièce # ---------------------------- def selectionner(self): # inverse sélection self.selectionnee = not self.selectionnee up() goto(-350 + (self.x-1)*80, -350 + (self.y-1)*80) down() # Si sélectionnée if self.selectionnee: couleur = "blue" self.plateau.piece_selectionnee = self else: # Supprime anciens déplacements self.plateau.supprimer_trajectoire() # Couleur normale couleur = "chocolate" if (self.x + self.y) % 2 == 0 else "wheat" begin_fill() fillcolor(couleur) for _ in range(4): fd(80) lt(90) end_fill() color("black") # Redessine la pièce self.dessiner() if self.selectionnee : # Crée les déplacements possibles self.creer_trajectoire() # ---------------------------- # Déplacement # ---------------------------- def deplacer(self, nx, ny, couleur="black"): """ Déplace une pièce vers une nouvelle case et supprime correctement une pièce ennemie si capture """ ancien_y = self.y # Efface ancienne position visuelle self.effacer() # Supprime ancienne position dans la grille self.plateau.grille[self.y-1][self.x-1] = None # Vérifie si une pièce est présente sur la case cible cible = self.plateau.grille[ny-1][nx-1] if isinstance(cible, Piece): # Efface graphiquement la pièce mangée cible.effacer() # Supprime la pièce de la grille self.plateau.grille[ny-1][nx-1] = None # Met à jour coordonnées self.x = nx self.y = ny # Place la pièce à sa nouvelle position self.plateau.grille[ny-1][nx-1] = self # Supprime les cercles restants self.plateau.supprimer_trajectoire() # Redessine la pièce self.dessiner() self.a_deja_bougée=True if isinstance(self, Pion) : if self.joueur=='blanc' and self.y==8 : self.effacer() self.plateau.grille[ny-1][nx-1] = Reine(nx,8, "blanc", plateau) if isinstance(self, Pion) : if self.joueur=='noir' and self.y==1 : self.effacer() self.plateau.grille[ny-1][nx-1] = Reine(nx,1, "noir", plateau) # ========================= # Gestion prise en passant # ========================= # Reset par défaut self.plateau.prise_en_passant = None # Si un pion avance de 2 cases if isinstance(self, Pion): if abs(ny - ancien_y) == 2: self.plateau.prise_en_passant = self class Tour(Piece): def __init__(self, x, y, joueur, plateau): super().__init__(x, y, joueur, plateau) self.symbole = '\u2656' if joueur == "blanc" else '\u265C' self.dessiner() def creer_trajectoire(self): """ Déplacements possibles de la tour : - avance sur cases vides - capture une pièce ennemie - s'arrête après une pièce """ directions = [ (0, 1), # haut (0, -1), # bas (1, 0), # droite (-1, 0) # gauche ] for dx, dy in directions: x = self.x + dx y = self.y + dy while 1 <= x <= 8 and 1 <= y <= 8: contenu = self.plateau.grille[y-1][x-1] # ------------------------- # CASE VIDE # ------------------------- if contenu is None: if not(self.plateau.coup_met_roi_en_echec(self,x,y)) : self.plateau.grille[y-1][x-1] = "." self.plateau.dessiner_cercle(x, y) # ------------------------- # PIECE PRESENTE # ------------------------- elif isinstance(contenu, Piece) and contenu.joueur!= self.joueur : if not(self.plateau.coup_met_roi_en_echec(self,x,y)) : self.plateau.grille[y-1][x-1].traj=True self.plateau.dessiner_cercle(x, y) # Dans tous les cas on arrête ici break # sécurité else: break x += dx y += dy class Cavalier(Piece): def __init__(self, x, y, joueur, plateau): super().__init__(x, y, joueur, plateau) self.symbole = '\u2658' if joueur == "blanc" else '\u265E' self.dessiner() def creer_trajectoire(self): """ Déplacements possibles du cavalier """ # Les 8 déplacements possibles en L mouvements = [ (2, 1), (2, -1), (-2, 1), (-2, -1), (1, 2), (1, -2), (-1, 2), (-1, -2) ] for dx, dy in mouvements: x = self.x + dx y = self.y + dy # Vérifie que la case reste dans l'échiquier if 1 <= x <= 8 and 1 <= y <= 8: contenu = self.plateau.grille[y-1][x-1] # ------------------------- # Case vide # ------------------------- if contenu is None: if not(self.plateau.coup_met_roi_en_echec(self,x,y)) : self.plateau.grille[y-1][x-1] = "." self.plateau.dessiner_cercle(x, y) # ------------------------- # Pièce ennemie -> capture possible # ------------------------- elif isinstance(contenu, Piece): if not(self.plateau.coup_met_roi_en_echec(self,x,y)) : if contenu.joueur != self.joueur: contenu.traj = True self.plateau.dessiner_cercle(x, y) class Fou(Piece): def __init__(self, x, y, joueur, plateau): super().__init__(x, y, joueur, plateau) self.symbole = '\u2657' if joueur == "blanc" else '\u265D' self.dessiner() def creer_trajectoire(self): """ Déplacements possibles de la tour : - avance sur cases vides - capture une pièce ennemie - s'arrête après une pièce """ directions = [ (1, 1), # haut-droite (-1, -1), # bas-gauche (1, -1), # bas-droite (-1, 1) # haut-gauche ] for dx, dy in directions: x = self.x + dx y = self.y + dy while 1 <= x <= 8 and 1 <= y <= 8: contenu = self.plateau.grille[y-1][x-1] # ------------------------- # CASE VIDE # ------------------------- if contenu is None: if not(self.plateau.coup_met_roi_en_echec(self,x,y)) : self.plateau.grille[y-1][x-1] = "." self.plateau.dessiner_cercle(x, y) # ------------------------- # PIECE PRESENTE # ------------------------- elif isinstance(contenu, Piece) and contenu.joueur!= self.joueur : if not(self.plateau.coup_met_roi_en_echec(self,x,y)) : self.plateau.grille[y-1][x-1].traj=True self.plateau.dessiner_cercle(x, y) # Dans tous les cas on arrête ici break # sécurité else: break x += dx y += dy class Reine(Piece): def __init__(self, x, y, joueur, plateau): super().__init__(x, y, joueur, plateau) self.symbole = '\u2655' if joueur == "blanc" else '\u265B' self.dessiner() def creer_trajectoire(self): """ Déplacements possibles de la tour : - avance sur cases vides - capture une pièce ennemie - s'arrête après une pièce """ directions = [ (0, 1), # haut (0, -1), # bas (1, 0), # droite (-1, 0), # gauche (1, 1), # haut-droite (-1, -1), # bas-gauche (1, -1), # bas-droite (-1, 1) # haut-gauche ] for dx, dy in directions: x = self.x + dx y = self.y + dy while 1 <= x <= 8 and 1 <= y <= 8: contenu = self.plateau.grille[y-1][x-1] # ------------------------- # CASE VIDE # ------------------------- if contenu is None: if not(self.plateau.coup_met_roi_en_echec(self,x,y)) : self.plateau.grille[y-1][x-1] = "." self.plateau.dessiner_cercle(x, y) # ------------------------- # PIECE PRESENTE # ------------------------- elif isinstance(contenu, Piece) and contenu.joueur!= self.joueur : if not(self.plateau.coup_met_roi_en_echec(self,x,y)) : self.plateau.grille[y-1][x-1].traj=True self.plateau.dessiner_cercle(x, y) # Dans tous les cas on arrête ici break # sécurité else: break x += dx y += dy class Roi(Piece): def __init__(self, x, y, joueur, plateau): super().__init__(x, y, joueur, plateau) self.symbole = '\u2654' if joueur == "blanc" else '\u265A' self.dessiner() def creer_trajectoire(self): """ Déplacements possibles du roi """ mouvements = [ (0, 1), # haut (0, -1), # bas (1, 0), # droite (-1, 0), # gauche (1, 1), # haut-droite (-1, -1), # bas-gauche (1, -1), # bas-droite (-1, 1) # haut-gauche ] for dx, dy in mouvements: x = self.x + dx y = self.y + dy # Vérifie que la case reste dans l'échiquier if 1 <= x <= 8 and 1 <= y <= 8: contenu = self.plateau.grille[y-1][x-1] # ------------------------- # Case vide # ------------------------- if contenu is None: if not(self.plateau.coup_met_roi_en_echec(self,x,y)) : self.plateau.grille[y-1][x-1] = "." self.plateau.dessiner_cercle(x, y) # ------------------------- # Pièce ennemie -> capture possible # ------------------------- elif isinstance(contenu, Piece): if not(self.plateau.coup_met_roi_en_echec(self,x,y)) : if contenu.joueur != self.joueur: contenu.traj = True self.plateau.dessiner_cercle(x, y) self.roques() def roques(self) : # Le roi ne doit jamais avoir bougé if self.a_deja_bougée: return # Le roi ne doit pas être en échec if self.plateau.roi_en_echec(self.joueur): return y = self.y # ================================= # PETIT ROQUE # ================================= tour_droite = self.plateau.grille[y-1][7] if isinstance(tour_droite, Tour): if not tour_droite.a_deja_bougée: # Cases entre roi et tour vides if (self.plateau.grille[y-1][5] in [None, "."] and self.plateau.grille[y-1][6] in [None, "."]): # Les cases traversées ne doivent pas être attaquées if (not self.plateau.case_attaquee(6, y, self.joueur) and not self.plateau.case_attaquee(7, y, self.joueur)): # Affiche rond noir t = Turtle() t.hideturtle() t.speed(0) t.penup() centre_x = -350 + (7-1)*80 + 40 centre_y = -350 + (y-1)*80 + 40 t.goto(centre_x, centre_y-15) t.color("black") t.begin_fill() t.circle(15) t.end_fill() self.plateau.cercles.append(t) # Case spéciale roque self.plateau.grille[y-1][6] = "roque_droit" # ================================= # GRAND ROQUE # ================================= tour_gauche = self.plateau.grille[y-1][0] if isinstance(tour_gauche, Tour): if not tour_gauche.a_deja_bougée: # Cases entre roi et tour vides if (self.plateau.grille[y-1][1] in [None, "."] and self.plateau.grille[y-1][2] in [None, "."] and self.plateau.grille[y-1][3] in [None, "."]): # Cases traversées non attaquées if (not self.plateau.case_attaquee(4, y, self.joueur) and not self.plateau.case_attaquee(3, y, self.joueur)): # Affiche rond noir t = Turtle() t.hideturtle() t.speed(0) t.penup() centre_x = -350 + (3-1)*80 + 40 centre_y = -350 + (y-1)*80 + 40 t.goto(centre_x, centre_y-15) t.color("black") t.begin_fill() t.circle(15) t.end_fill() self.plateau.cercles.append(t) # Case spéciale roque self.plateau.grille[y-1][2] = "roque_gauche" class Pion(Piece): def __init__(self, x, y, joueur, plateau): super().__init__(x, y, joueur, plateau) self.symbole = '\u2659' if joueur == "blanc" else '\u265F' self.dessiner() def creer_trajectoire(self): """ Déplacements possibles du pion """ mouvements=[] if self.joueur=='blanc' : if self.plateau.grille[self.y][self.x-1]==None : mouvements = [ (0, 1), # haut ] if self.y==2 : if(self.plateau.grille[self.y+1][self.x-1]==None) : mouvements.append((0,2)) #2 cases au début #manger if not(self.x==8) : if isinstance(self.plateau.grille[self.y][self.x],Piece) : if self.plateau.grille[self.y][self.x].joueur=='noir' : mouvements.append((1,1)) if isinstance(self.plateau.grille[self.y][self.x-2],Piece) : if self.plateau.grille[self.y][self.x-2].joueur=='noir' : mouvements.append((-1,1)) else : if self.plateau.grille[self.y-2][self.x-1]==None : mouvements = [ (0, -1), # haut ] if self.y==7 : if(self.plateau.grille[self.y-3][self.x-1]==None) : mouvements.append((0,-2)) #2 cases au début #manger if not(self.x==8) : if isinstance(self.plateau.grille[self.y-2][self.x],Piece) : if self.plateau.grille[self.y-2][self.x].joueur=='blanc' : mouvements.append((1,-1)) if not(self.x==1) : if isinstance(self.plateau.grille[self.y-2][self.x-2],Piece) : if self.plateau.grille[self.y-2][self.x-2].joueur=='blanc' : mouvements.append((-1,-1)) for dx, dy in mouvements: x = self.x + dx y = self.y + dy # Vérifie que la case reste dans l'échiquier if 1 <= x <= 8 and 1 <= y <= 8: contenu = self.plateau.grille[y-1][x-1] # ------------------------- # Case vide # ------------------------- if contenu is None: if not(self.plateau.coup_met_roi_en_echec(self,x,y)) : self.plateau.grille[y-1][x-1] = "." self.plateau.dessiner_cercle(x, y) # ------------------------- # Pièce ennemie -> capture possible # ------------------------- elif isinstance(contenu, Piece): if not(self.plateau.coup_met_roi_en_echec(self,x,y)) : if contenu.joueur != self.joueur: contenu.traj = True self.plateau.dessiner_cercle(x, y) # ===================================== # PRISE EN PASSANT # ===================================== pion_ep = self.plateau.prise_en_passant if isinstance(pion_ep, Pion): # Blanc mange un noir if self.joueur == "blanc": if self.y == 5: # pion ennemi juste à droite if pion_ep.x == self.x + 1 and pion_ep.y == 5: self.plateau.grille[5][pion_ep.x-1] = "en_passant" t = Turtle() t.hideturtle() t.speed(0) t.penup() centre_x = -350 + (pion_ep.x-1)*80 + 40 centre_y = -350 + 5*80 + 40 t.goto(centre_x, centre_y-15) t.color("black") t.begin_fill() t.circle(15) t.end_fill() self.plateau.cercles.append(t) # pion ennemi juste à gauche if pion_ep.x == self.x - 1 and pion_ep.y == 5: self.plateau.grille[5][pion_ep.x-1] = "en_passant" t = Turtle() t.hideturtle() t.speed(0) t.penup() centre_x = -350 + (pion_ep.x-1)*80 + 40 centre_y = -350 + 5*80 + 40 t.goto(centre_x, centre_y-15) t.color("black") t.begin_fill() t.circle(15) t.end_fill() self.plateau.cercles.append(t) # Noir mange un blanc else: if self.y == 4: if pion_ep.x == self.x + 1 and pion_ep.y == 4: self.plateau.grille[2][pion_ep.x-1] = "en_passant" t = Turtle() t.hideturtle() t.speed(0) t.penup() centre_x = -350 + (pion_ep.x-1)*80 + 40 centre_y = -350 + 2*80 + 40 t.goto(centre_x, centre_y-15) t.color("black") t.begin_fill() t.circle(15) t.end_fill() self.plateau.cercles.append(t) if pion_ep.x == self.x - 1 and pion_ep.y == 4: self.plateau.grille[2][pion_ep.x-1] = "en_passant" t = Turtle() t.hideturtle() t.speed(0) t.penup() centre_x = -350 + (pion_ep.x-1)*80 + 40 centre_y = -350 + 2*80 + 40 t.goto(centre_x, centre_y-15) t.color("black") t.begin_fill() t.circle(15) t.end_fill() self.plateau.cercles.append(t) # =================================== # INITIALISATION # =================================== plateau = Plateau() # =================================== # BOT ALEATOIRE # =================================== def tous_les_coups(joueur): plateau.supprimer_trajectoire(graphique=False) coups = [] for ligne in plateau.grille: for piece in ligne: if isinstance(piece, Piece): if piece.joueur == joueur: liste = plateau.coups_piece(piece) for nx, ny in liste: if not plateau.coup_met_roi_en_echec(piece, nx, ny): coups.append((piece, nx, ny)) return coups def colorer_case(x, y, couleur): up() goto(-350 + (x-1)*80, -350 + (y-1)*80) down() fillcolor(couleur) begin_fill() for _ in range(4): fd(80) lt(90) end_fill() def jouer_bot(): """ Le bot joue un coup calculé avec minimax """ global tour global dernier_piece_ia global dernier_coup_ia if tour != "noir": return coups = tous_les_coups("noir") if len(coups) == 0: up() goto(-260, 280) down() if plateau.roi_en_echec("noir"): write("Echec et mat ! Blanc gagne", font=("Arial Unicode MS", 40)) else: write("Pat !", font=("Arial Unicode MS", 40)) tour = "fin" return coups.sort(key=score_coup, reverse=True) nb_coups = len(coups) # ========================= # PROFONDEUR ADAPTATIVE # ========================= if nb_coups > 15: profondeur = 3 elif nb_coups > 7: profondeur = 4 else: profondeur = 5 piece, x, y = minimaxroot(profondeur, coups) # ========================= # CASE ROUGE AVANT LE COUP # ========================= colorer_case(x, y, "red") # Redessine les pièces for ligne in plateau.grille: for p in ligne: if isinstance(p, Piece): p.dessiner() update() sleep(1) contenu = plateau.grille[y-1][x-1] dernier_coup_ia = (piece, x, y) # ----------------------------- # ROQUE # ----------------------------- if contenu == "roque_droit": roi = piece roi.deplacer(7, roi.y) tour_piece = plateau.grille[roi.y-1][7] tour_piece.deplacer(6, roi.y) elif contenu == "roque_gauche": roi = piece roi.deplacer(3, roi.y) tour_piece = plateau.grille[roi.y-1][0] tour_piece.deplacer(4, roi.y) # ----------------------------- # PRISE EN PASSANT # ----------------------------- elif contenu == "en_passant": pion = piece cible = plateau.grille[pion.y-1][x-1] if isinstance(cible, Piece): cible.effacer() plateau.grille[pion.y-1][x-1] = None pion.deplacer(x, y) # ----------------------------- # COUP NORMAL # ----------------------------- else: piece.deplacer(x, y) # ----------------------------- # METTRE LA PIECE IA EN ROUGE # ----------------------------- dernier_piece_ia = piece piece.effacer() piece.dessiner("red") plateau.supprimer_trajectoire() tour = "blanc" if plateau.echec_et_mat(tour): up() goto(-260, 280) down() if plateau.roi_en_echec(tour): write('Echec et mat ! Le bot gagne', font=("Arial Unicode MS", 40)) else: write('Pat !', font=("Arial Unicode MS", 40)) tour = "fin" return elif plateau.pat(tour): up() goto(-260, 280) down() write('Pat !', font=("Arial Unicode MS", 40)) tour = "fin" return def score_coup(coup): global dernier_coup_ia piece, x, y = coup cible = plateau.grille[y-1][x-1] bonus = 0 # ========================= # EVITER LES ALLERS-RETOURS # ========================= if dernier_coup_ia is not None: ancienne_piece, ancien_x, ancien_y = dernier_coup_ia if piece == ancienne_piece: if x == ancien_x and y == ancien_y: bonus -= 200 return bonus def evaluation(): # ========================= # ECHEC ET MAT # ========================= if plateau.echec_et_mat("blanc"): return 100000 if plateau.echec_et_mat("noir"): return -100000 score = 0 valeurs = { Pion: 100, Cavalier: 320, Fou: 330, Tour: 500, Reine: 1000, Roi: 20000 } for ligne in plateau.grille: for piece in ligne: if isinstance(piece, Piece): valeur = valeurs[type(piece)] # ========================= # BONUS CENTRE # ========================= if piece.x in [4, 5]: valeur += 15 if piece.y in [4, 5]: valeur += 15 # ========================= # DEVELOPPEMENT # ========================= if not isinstance(piece, (Pion, Roi)): if piece.a_deja_bougée: valeur += 10 # ========================= # ROI EN DANGER # ========================= if isinstance(piece, Roi): if plateau.case_attaquee(piece.x, piece.y, piece.joueur): valeur -= 80 # ========================= # SCORE FINAL # ========================= if piece.joueur == "noir": score += valeur else: score -= valeur # ========================= # BONUS ECHEC # ========================= if plateau.roi_en_echec("blanc"): score += 300 if plateau.roi_en_echec("noir"): score -= 300 # ========================= # MOBILITE ROI BLANC # ========================= roi_blanc = plateau.trouver_roi("blanc") if roi_blanc: mobilite = 0 mouvements = [ (0,1),(0,-1),(1,0),(-1,0), (1,1),(-1,-1),(1,-1),(-1,1) ] for dx, dy in mouvements: x = roi_blanc.x + dx y = roi_blanc.y + dy if 1 <= x <= 8 and 1 <= y <= 8: contenu = plateau.grille[y-1][x-1] if contenu is None: if not plateau.case_attaquee(x, y, "blanc"): mobilite += 1 score += (8 - mobilite) * 25 return score def minimaxroot(profondeur, liste_coups): meilleurScore = -float('inf') meilleurMouv = None for coup in liste_coups: sauvegarde = placeTempMouv(coup) score = minimax( profondeur - 1, False, -float('inf'), float('inf') ) retirerTempMouv(sauvegarde) if score > meilleurScore: meilleurScore = score meilleurMouv = coup return meilleurMouv def minimax(profondeur, isMaximizing, alpha, beta): # profondeur max atteinte if profondeur == 0: return evaluation() # joueur noir = MAX if isMaximizing: maxEval = -float('inf') coups = tous_les_coups("noir") if len(coups) == 0: return evaluation() coups.sort(key=score_coup, reverse=True) for coup in coups: sauvegarde = placeTempMouv(coup) score = minimax( profondeur - 1, False, alpha, beta ) retirerTempMouv(sauvegarde) maxEval = max(maxEval, score) alpha = max(alpha, score) if beta <= alpha: break return maxEval # joueur blanc = MIN else: minEval = float('inf') coups = tous_les_coups("blanc") if len(coups) == 0: return evaluation() coups.sort(key=score_coup) for coup in coups: sauvegarde = placeTempMouv(coup) score = minimax( profondeur - 1, True, alpha, beta ) retirerTempMouv(sauvegarde) minEval = min(minEval, score) beta = min(beta, score) if beta <= alpha: break return minEval def placeTempMouv(coup): piece, nx, ny = coup ancienne_x = piece.x ancienne_y = piece.y cible = plateau.grille[ny-1][nx-1] plateau.grille[ancienne_y-1][ancienne_x-1] = None plateau.grille[ny-1][nx-1] = piece piece.x = nx piece.y = ny return ( piece, ancienne_x, ancienne_y, cible ) def retirerTempMouv(data): piece, ancienne_x, ancienne_y, cible = data plateau.grille[piece.y-1][piece.x-1] = cible piece.x = ancienne_x piece.y = ancienne_y plateau.grille[ancienne_y-1][ancienne_x-1] = piece def initialisation(): global plateau global tour clear() plateau = Plateau() tour = 'blanc' # ---------------------------- # Pièces blanches # ---------------------------- Tour(1, 1, "blanc", plateau) Tour(8, 1, "blanc", plateau) Cavalier(2, 1, "blanc", plateau) Cavalier(7, 1, "blanc", plateau) Fou(3, 1, "blanc", plateau) Fou(6, 1, "blanc", plateau) Reine(4, 1, "blanc", plateau) Roi(5, 1, "blanc", plateau) for i in range(1, 9): Pion(i, 2, "blanc", plateau) # ---------------------------- # Pièces noires # ---------------------------- Tour(1, 8, "noir", plateau) Tour(8, 8, "noir", plateau) Cavalier(2, 8, "noir", plateau) Cavalier(7, 8, "noir", plateau) Fou(3, 8, "noir", plateau) Fou(6, 8, "noir", plateau) Reine(4, 8, "noir", plateau) Roi(5, 8, "noir", plateau) for i in range(1, 9): Pion(i, 7, "noir", plateau) initialisation() #-----------Teste bouton restart-------------- onkeypress(initialisation,"End") # bouton centre # =================================== # ACTIVATION DU CLICK # ==================================== onscreenclick(selectionner) onkeypress(bye,"Delete") listen() done()