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 ePieceBlanche(self, piece): """ Indique si la pièce est une pièce blanche. """ return piece in self.BLANCS def ePieceNoire(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.ePieceBlanche(piece) or self.ePieceNoire(piece) def tPiece(self, piece): """ Retourne le type de pièce de la pièce donnée. """ if self.ePieceBlanche(piece): return piece - self.DECALAGE_BLANCS elif self.ePieceNoire(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.ePieceNoire(piece) and self.joueur == self.NOIR) or \ (self.ePieceBlanche(piece) and self.joueur == self.BLANC) class LogiqueDames(Logique): """ Logique du jeu de Dames (International) """ CASES_COTE = 10 RANGEES = 4 PCE_PION = 1 PCE_DAME = 2 DECALAGE_BLANCS = 0 DECALAGE_NOIRS = 10 BLANCS = range(DECALAGE_BLANCS + PCE_PION, DECALAGE_BLANCS + PCE_DAME + 1) NOIRS = range(DECALAGE_NOIRS + PCE_PION, DECALAGE_NOIRS + PCE_DAME + 1) MVT_INCONNU = 'Cause inconnue' MVT_OK = 'Valide' MVT_SAUT = 'Saut' MVT_SELECTION = 'Mauvais tour' MVT_SUR_PLACE = 'Immobile' MVT_N_AUTORISE = 'Non-autorisé' MVT_CASE_BLANCHE = 'Case blanche' MVT_PRISE_ARRIERE = 'Prise arrière' MVT_SAUT_AMI = 'Saut ami' MVT_DEST_OCC = 'Destination occupée' MVT_OBSTRUCTION = 'Pièce en chemin' MVT_SOUFFLER = 'Souffler n\'est pas jouer' MVT_PIECE_INC = 'Pièce inconnue' PRISE_ARRIERE = True DEPL_LONG_DAME = True def __init__(self): """ Constructeur de LogiqueDames """ Logique.__init__(self) self.remplirGrille() self.rafle = False def remplirGrille(self): """ Remplis la grille avec les pièces nécessaire à une nouvelle partie. """ for y in range(0, self.RANGEES): for x in range(0, self.CASES_COTE): if not self.eCaseBlanche(x, y): self.grille[x][y] = self.DECALAGE_NOIRS + self.PCE_PION for y in range(self.CASES_COTE-self.RANGEES, self.CASES_COTE): for x in range(0, self.CASES_COTE): if not self.eCaseBlanche(x, y): self.grille[x][y] = self.DECALAGE_BLANCS + self.PCE_PION def deplPossiblePion(self, x1, y1, x2, y2): """ Vérifie si le déplacement est possible pour un pion. """ if abs(x2-x1) == 1 and abs(y2-y1) == 1 and not self.rafle: if (self.joueur == self.BLANC and y1 > y2) or (self.joueur == self.NOIR and y1 < y2): return self.MVT_OK else: return self.MVT_N_AUTORISE elif abs(x2-x1) == 2 and abs(y2-y1) == 2: xS = x1+(x2-x1)//2 yS = y1+(y2-y1)//2 if self.ePiece(self.grille[xS][yS]): if not self.PRISE_ARRIERE and (self.joueur == self.BLANC and y1 < y2) or \ (self.joueur == self.NOIR and y1 > y2): return self.MVT_PRISE_ARRIERE if self.aSonTour(self.grille[xS][yS]): return self.MVT_SAUT_AMI else: return self.MVT_SAUT else: return self.MVT_N_AUTORISE else: return self.MVT_N_AUTORISE def deplPossibleDame(self, x1, y1, x2, y2): """ Vérifie si le déplacement est possible pour une dame. """ if self.DEPL_LONG_DAME: 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 - 1 and not self.aSonTour(self.grille[x][y]): return self.MVT_SAUT else: return self.MVT_OBSTRUCTION if self.rafle: return self.MVT_SOUFFLER else: return self.MVT_OK else: return self.MVT_N_AUTORISE else: if abs(x2-x1) == 1 and abs(y2-y1) == 1 and not self.rafle: return self.MVT_OK elif abs(x2-x1) == 2 and abs(y2-y1) == 2: xS = x1+(x2-x1)//2 yS = y1+(y2-y1)//2 if self.ePiece(self.grille[xS][yS]): if self.aSonTour(self.grille[xS][yS]): return self.MVT_SAUT_AMI else: return self.MVT_SAUT else: return self.MVT_N_AUTORISE 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.ePiece(self.grille[x2][y2]): if not self.eCaseBlanche(x2, y2): tPiece = self.tPiece(piece) if tPiece == self.PCE_PION: # Pion return self.deplPossiblePion(x1, y1, x2, y2) elif tPiece == self.PCE_DAME: return self.deplPossibleDame(x1, y1, x2, y2) else: return self.MVT_PIECE_INC else: return self.MVT_CASE_BLANCHE else: return self.MVT_DEST_OCC 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 le "Soufler n'est pas jouer". """ test = self.deplPossible(x1, y1, x2, y2) if test == self.MVT_OK: if self.sautEstPossibleTous(): return self.MVT_SOUFFLER else: return test else: return test def deplsPossibles(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 \ or self.mvtPossible(x1, y1, x2, y2) == self.MVT_SAUT: tableau.append([x2, y2]) return tableau def sautsPossibles(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.deplPossible(x1, y1, x2, y2) == self.MVT_SAUT: tableau.append([x2, y2]) return tableau def sautEstPossibleTous(self): """ Indique si le joueur peut faire un saut. """ for x in range(0, self.CASES_COTE): for y in range(0, self.CASES_COTE): if self.aSonTour(self.grille[x][y]): if len(self.sautsPossibles(x, y)) > 0: return True return False 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 \ or self.mvtPossible(x1, y1, x2, y2) == self.MVT_SAUT: tableau.append([x2, y2]) return tableau def vPartieFinie(self): """ Vérifie si le joueur actuel ne peut plus jouer. """ # 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, 'ajouter': [], # Pièces à ajouter 'deplacer': [], # Pièces à déplacer 'supprimer': [], # Pièces à supprimer } if test in [self.MVT_OK, self.MVT_SAUT]: if test == self.MVT_SAUT: xS = x2-(x2-x1)//abs(x2-x1) yS = y2-(y2-y1)//abs(y2-y1) self.grille[xS][yS] = self.PCE_VIDE retour['supprimer'].append([xS, yS]) retour['valide'] = True retour['deplacer'].append([x1, y1, x2, y2]) self.grille[x1][y1], self.grille[x2][y2] = self.PCE_VIDE, self.grille[x1][y1] if test == self.MVT_SAUT and len(self.sautsPossibles(x2, y2)) > 0: self.rafle = True else: self.rafle = False piece = self.grille[x2][y2] if self.tPiece(piece) == self.PCE_PION and ((self.ePieceBlanche(piece) and \ y2 == 0) or (self.ePieceNoire(piece) and y2 == self.CASES_COTE - 1)): self.grille[x2][y2] = (self.DECALAGE_BLANCS if self.ePieceBlanche(piece) else \ self.DECALAGE_NOIRS) + self.PCE_DAME retour['deplacer'] = [] retour['supprimer'].append([x1, y1]) retour['ajouter'].append([x2, y2, self.grille[x2][y2]]) self.joueur = not self.joueur self.vPartieFinie() return retour class LogiqueDamesAnglaises(LogiqueDames): """ Logique du jeu de Dames (Anglaises) """ CASES_COTE = 8 RANGEES = 3 PRISE_ARRIERE = False DEPL_LONG_DAME = False def __init__(self): """ Constructeur de LogiqueDamesAnglaises """ LogiqueDames.__init__(self) 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 inconnue' 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 x in range(0, 8): self.grille[x][0] = self.DECALAGE_NOIRS + speciales[x] self.grille[x][1] = self.DECALAGE_NOIRS + self.PCE_PION self.grille[x][6] = self.DECALAGE_BLANCS + self.PCE_PION self.grille[x][7] = self.DECALAGE_BLANCS + speciales[x] 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.ePieceNoire(self.grille[x2][y2]): return self.MVT_OK else: return self.MVT_N_AUTORISE else: if y2 == y1 + 1 and \ self.ePieceBlanche(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.deplsPossibles( 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 deplsPossibles(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, 'ajouter': [], # Pièces à ajouter '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