import copy class Logique: """ Logique des jeux sur damier (Ne peut être utilisé seul) """ PCE_VIDE = 0 # Valeur à mettre sur la grille quand il n'y a pas de pièce BLANC = True # Valeur correspondant au joueur blanc NOIR = False # Valeur correspondant au joueur noir # Valeurs devant être remplies par une classe héritante CASES_COTE = 0 # Cases de coté # Valeur à partir de laquelle sont comprises les pièces blanches DECALAGE_BLANCS = 0 # Valeur à partir de laquelle sont comprises les pièces noires DECALAGE_NOIRS = 0 BLANCS = range(0) # Intervalle contenant les pièces blanches NOIRS = range(0) # Intervalle contenant les pièces noires def __init__(self): self.grille = [] # Grille de jeu self.cGrille() self.joueur = self.BLANC # Premier joueur self.partieFinie = False self.victorieux = None @staticmethod def eCaseBlanche(x, y): """ Indique si la case aux coordonnées indiquée est blanche. """ return x % 2 == y % 2 def cGrille(self): """ Crée le tableau self.grille. """ self.grille = [[self.PCE_VIDE for y in range(self.CASES_COTE)] \ for x in range(self.CASES_COTE)] def ePieceBlanc(self, piece): """ Indique si la pièce est une pièce blanche. """ return piece in self.BLANCS def ePieceNoir(self, piece): """ Indique si la pièce est une pièce noire. """ return piece in self.NOIRS def ePiece(self, piece): """ Indique si la pièce donnée est une pièce. """ # return piece != self.PCE_VIDE return self.ePieceBlanc(piece) or self.ePieceNoir(piece) def tPiece(self, piece): """ Retourne le type de pièce de la pièce donnée. """ if self.ePieceBlanc(piece): return piece - self.DECALAGE_BLANCS elif self.ePieceNoir(piece): return piece - self.DECALAGE_NOIRS else: return self.PCE_VIDE def aSonTour(self, piece): """ Indique si la pièce donnée a le droit de jouer. """ return (self.ePieceNoir(piece) and self.joueur == self.NOIR) or \ (self.ePieceBlanc(piece) and self.joueur == self.BLANC) class LogiqueEchecs(Logique): """ Logique du jeu d'Échecs """ CASES_COTE = 8 # Cases de coté # Type de pièces PCE_PION = 1 PCE_TOUR = 2 PCE_CAVALIER = 3 PCE_FOU = 4 PCE_DAME = 5 PCE_ROI = 6 # Valeur à partir de laquelle sont comprises les pièces blanches DECALAGE_BLANCS = 0 # Valeur à partir de laquelle sont comprises les pièces noires DECALAGE_NOIRS = 10 BLANCS = range(DECALAGE_BLANCS + PCE_PION, DECALAGE_BLANCS + PCE_ROI + 1) # Intervalle contenant les pièces blanches NOIRS = range(DECALAGE_NOIRS + PCE_PION, DECALAGE_NOIRS + PCE_ROI + 1) # Intervalle contenant les pièces noires # Codes d'erreur de mouvement MVT_INCONNU = 'Cause inconnue' MVT_OK = 'Valide' MVT_ROQUE = 'Roque' MVT_SELECTION = 'Mauvais tour' MVT_SUR_PLACE = 'Immobile' MVT_SAUT_AMI = 'Saut ami' MVT_PIECE_INC = 'Pièce inconnu' MVT_N_AUTORISE = 'Non-autorisé' MVT_OBSTRUCTION = 'Pièce en chemin' MVT_ECHEC = 'Échec au roi' def __init__(self): """ Constructeur de LogiqueEchecs """ Logique.__init__(self) self.remplirGrille() def remplirGrille(self): """ Remplis la grille avec les pièces nécessaire à une nouvelle partie. """ speciales = [self.PCE_TOUR, self.PCE_CAVALIER, self.PCE_FOU, self.PCE_ROI, self.PCE_DAME] speciales += speciales[2::-1] for i in range(0, 8): self.grille[i][0] = self.DECALAGE_NOIRS + speciales[i] self.grille[i][1] = self.DECALAGE_NOIRS + self.PCE_PION self.grille[i][6] = self.DECALAGE_BLANCS + self.PCE_PION self.grille[i][7] = self.DECALAGE_BLANCS + speciales[i] def deplPossiblePion(self, x1, y1, x2, y2): """ Vérifie si le déplacement est possible pour un pion. """ if x1 == x2 and self.grille[x2][y2] <= 0: # Avance if self.joueur: if y2 == y1 - 1: return self.MVT_OK elif y1 == 6 and y2 == 4 and self.grille[x1][5] == 0: return self.MVT_OK else: return self.MVT_N_AUTORISE else: if y2 == y1 + 1: return self.MVT_OK elif y1 == 1 and y2 == 3 and self.grille[x1][2] == 0: return self.MVT_OK else: return self.MVT_N_AUTORISE elif abs(x1 - x2) == 1: # Saut if self.joueur: if y2 == y1 - 1 and \ self.ePieceNoir(self.grille[x2][y2]): return self.MVT_OK else: return self.MVT_N_AUTORISE else: if y2 == y1 + 1 and \ self.ePieceBlanc(self.grille[x2][y2]): return self.MVT_OK else: return self.MVT_N_AUTORISE else: return self.MVT_N_AUTORISE def deplPossibleTour(self, x1, y1, x2, y2): """ Vérifie si le déplacement est possible pour une tour. """ if y1 == y2: sens = (x2 - x1) // abs(x2 - x1) for x in range(x1 + sens, x2, sens): if self.ePiece(self.grille[x][y1]): return self.MVT_OBSTRUCTION elif x1 == x2: sens = (y2 - y1) // abs(y2 - y1) for y in range(y1 + sens, y2, sens): if self.ePiece(self.grille[x1][y]): return self.MVT_OBSTRUCTION else: return self.MVT_N_AUTORISE return self.MVT_OK def deplPossibleFou(self, x1, y1, x2, y2): """ Vérifie si le déplacement est possible pour un fou. """ if abs(x2 - x1) == abs(y2 - y1): sensX = (x2 - x1) // abs(x2 - x1) sensY = (y2 - y1) // abs(y2 - y1) x = x1 y = y1 dist = 0 distTot = abs(x2 - x1) while dist < distTot: dist += 1 x += sensX y += sensY if self.ePiece(self.grille[x][y]): if dist == distTot: return self.MVT_OK # Saut else: return self.MVT_OBSTRUCTION return self.MVT_OK # Vide else: return self.MVT_N_AUTORISE def deplPossibleCavalier(self, x1, y1, x2, y2): """ Vérifie si le déplacement est possible pour un cavalier. """ if (abs(x2 - x1) == 2 and abs(y2 - y1) == 1) or (abs(y2 - y1) == 2 and abs(x2 - x1) == 1): return self.MVT_OK else: return self.MVT_N_AUTORISE # TODO Utiliser la gestion d'erreurs de Python ? def deplPossible(self, x1, y1, x2, y2): """ Vérifie si le déplacement est possible. """ piece = self.grille[x1][y1] if self.aSonTour(piece): if x1 != x2 or y1 != y2: if not self.aSonTour(self.grille[x2][y2]): tPiece = self.tPiece(piece) if tPiece == self.PCE_PION: # Pion return self.deplPossiblePion(x1, y1, x2, y2) elif tPiece == self.PCE_TOUR: # Tour return self.deplPossibleTour(x1, y1, x2, y2) elif tPiece == self.PCE_CAVALIER: return self.deplPossibleCavalier(x1, y1, x2, y2) elif tPiece == self.PCE_FOU: return self.deplPossibleFou(x1, y1, x2, y2) elif tPiece == self.PCE_DAME: tour = self.deplPossibleTour(x1, y1, x2, y2) fou = self.deplPossibleFou(x1, y1, x2, y2) if tour == self.MVT_OK or fou == self.MVT_OK: return self.MVT_OK elif tour == self.MVT_OBSTRUCTION or fou == self.MVT_OBSTRUCTION: return self.MVT_OBSTRUCTION else: return self.MVT_N_AUTORISE elif tPiece == self.PCE_ROI: if abs(x2 - x1) <= 1 and abs(y2 - y1) <= 1: return self.MVT_OK else: return self.MVT_N_AUTORISE else: return self.MVT_PIECE_INC else: return self.MVT_SAUT_AMI else: return self.MVT_SUR_PLACE else: return self.MVT_SELECTION return self.MVT_INCONNU def mvtPossible(self, x1, y1, x2, y2): """ Vérifie si le mouvement est possible. Contrairement aux fonctions de vérification de déplacement, celle-ci vérifie si le mouvement est réalisable si le roi est en échec. """ test = self.deplPossible(x1, y1, x2, y2) if test == self.MVT_OK: # On copie la partie actuelle pour tester le mouvement et vérifier # l'échec copie = copy.deepcopy(self) copie.dPieceSansEchec(x1, y1, x2, y2) mvtsPossiblesTousAdverses = [] # On cherche la position du roi pieceRoi = self.PCE_ROI + \ self.DECALAGE_BLANCS if self.joueur else self.DECALAGE_NOIRS roi = [-1, -1] for x in range(0, self.CASES_COTE): for y in range(0, self.CASES_COTE): mvtsPossiblesTousAdverses += copie.mvtsPossiblesSansEchec( x, y) if copie.grille[x][y] == pieceRoi: roi = [x, y] # Si le roi peut être sauté au tour adverse if roi in mvtsPossiblesTousAdverses: return self.MVT_ECHEC else: return test else: return test def mvtsPossiblesSansEchec(self, x1, y1): """ Donne la liste des déplacements possible pour la pièce donnée. """ tableau = [] for x2 in range(0, self.CASES_COTE): for y2 in range(0, self.CASES_COTE): if self.deplPossible(x1, y1, x2, y2) == self.MVT_OK: tableau.append([x2, y2]) return tableau def mvtsPossibles(self, x1, y1): """ Donne la liste des mouvements possible pour la pièce donnée. """ tableau = [] for x2 in range(0, self.CASES_COTE): for y2 in range(0, self.CASES_COTE): if self.mvtPossible(x1, y1, x2, y2) == self.MVT_OK: tableau.append([x2, y2]) return tableau def dPieceSansEchec(self, x1, y1, x2, y2): """ Déplace la pièce aux coordonnées 1 données aux coordonnées 2 données. Ne vérifie pas si le mouvement peut conduire à une prise illégale du roi. (Est utilisé dans pour cette détection) """ test = self.deplPossible(x1, y1, x2, y2) if test == self.MVT_OK: self.grille[x1][y1], self.grille[x2][ y2] = self.PCE_VIDE, self.grille[x1][y1] self.joueur = not self.joueur def vEchecMat(self): """ Vérifie si le joueur actuel est en échec et mat et déclenche éventuellement la victoire (Le "pat" est considiéré comme une victoire) """ # Vérifications si aucun mouvement pour aucune pièce n'est possible for x in range(0, self.CASES_COTE): for y in range(0, self.CASES_COTE): if len(self.mvtsPossibles(x, y)) > 0: return False self.partieFinie = True self.victorieux = not self.joueur return True def dPiece(self, x1, y1, x2, y2): """ Déplace la pièce aux coordonnées 1 données aux coordonnées 2 données. Renvoie les détails du mouvement. """ test = self.mvtPossible(x1, y1, x2, y2) retour = { 'valide': False, 'message': test, 'deplacer': [], # Pièces à déplacer 'supprimer': [], # Pièces à supprimer } if test == self.MVT_OK: retour['valide'] = True if self.ePiece(self.grille[x2][y2]): retour['supprimer'].append([x2, y2]) retour['deplacer'].append([x1, y1, x2, y2]) self.grille[x1][y1], self.grille[x2][ y2] = self.PCE_VIDE, self.grille[x1][y1] self.joueur = not self.joueur self.vEchecMat() return retour