CASES_COTE = 8 MVT_INCONNU = 'Cause inconnue' MVT_OK = 'Valide' MVT_ROQUE = 'Roque' MVT_SELECTION = 'Mauvais tour' MVT_SUR_PLACE = 'Immobile' MVT_SAUT_AMI = 'Saut ami' MVT_PION_INC = 'Pion inconnu' MVT_N_AUTORISE = 'Non-autorisé' MVT_OBSTRUCTION = 'Pion en chemin' class LogiqueEchecs: def __init__(self): self.grille = [] self.cGrille() self.remplirGrille() self.joueur = True self.nvPartie() @staticmethod def eNoir(xD, yD): return xD % 2 != yD % 2 def cGrille(self): for x in range(CASES_COTE): colonne = [] for y in range(CASES_COTE): colonne.append(0) self.grille.append(colonne) def remplirGrille(self): speciales = [2, 3, 4, 6, 5, 4, 3, 2] for i in range(0, 8): self.grille[i][0] = speciales[i] + 10 self.grille[i][1] = 11 self.grille[i][6] = 1 self.grille[i][7] = speciales[i] def nvPartie(self): self.cGrille() self.remplirGrille() self.joueur = True def cPion(self, x, y, piece): """ """ self.grille[x][y] = piece return True @staticmethod def ePionBlanc(pion): return pion in range(1, 7) @staticmethod def ePionNoir(pion): return pion in range(11, 17) def aSonTour(self, pion): return (self.ePionNoir(pion) and self.joueur == False) or \ (self.ePionBlanc(pion) and self.joueur == True) def mvtPossiblePion(self, x1, y1, x2, y2): if x1 == x2 and self.grille[x2][y2] <= 0: # Avance if self.joueur: if y2 == y1 - 1: return MVT_OK elif y1 == 6 and y2 == 4 and self.grille[x1][5] == 0: return MVT_OK else: return MVT_N_AUTORISE else: if y2 == y1 + 1: return MVT_OK elif y1 == 1 and y2 == 3 and self.grille[x1][2] == 0: return MVT_OK else: return MVT_N_AUTORISE elif abs(x1-x2) == 1: # Saut if self.joueur: if y2 == y1 - 1 and \ self.ePionNoir(self.grille[x2][y2]): return MVT_OK else: return MVT_N_AUTORISE else: if y2 == y1 + 1 and \ self.ePionBlanc(self.grille[x2][y2]): return MVT_OK else: return MVT_N_AUTORISE else: return MVT_N_AUTORISE def mvtPossibleTour(self, x1, y1, x2, y2): if y1 == y2: sens = (x2-x1)//abs(x2-x1) for x in range(x1+sens, x2, sens): if self.grille[x][y1] > 0: return MVT_OBSTRUCTION elif x1 == x2: sens = (y2-y1)//abs(y2-y1) for y in range(y1+sens, y2, sens): if self.grille[x1][y] > 0: return MVT_OBSTRUCTION else: return MVT_N_AUTORISE return MVT_OK def mvtPossibleFou(self, x1, y1, x2, y2): 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.grille[x][y] > 0: if dist == distTot: return MVT_OK # Saut else: return MVT_OBSTRUCTION return MVT_OK # Vide else: return MVT_N_AUTORISE def mvtPossibleCavalier(self, x1, y1, x2, y2): return MVT_PION_INC def mvtPossible(self, x1, y1, x2, y2): pion = self.grille[x1][y1] if self.aSonTour(pion): if (x1 != x2 or y1 != y2): if not self.aSonTour(self.grille[x2][y2]): tPion = pion % 10 if tPion == 1: # Pion return self.mvtPossiblePion(x1, y1, x2, y2) elif tPion == 2: # Tour return self.mvtPossibleTour(x1, y1, x2, y2) elif tPion == 3: # Cavalier return self.mvtPossibleCavalier(x1, y1, x2, y2) elif tPion == 4: # Fou return self.mvtPossibleFou(x1, y1, x2, y2) elif tPion == 5: # Dame tour = self.mvtPossibleTour(x1, y1, x2, y2) fou = self.mvtPossibleFou(x1, y1, x2, y2) if tour == MVT_OK or fou == MVT_OK: return MVT_OK elif tour == MVT_OBSTRUCTION or fou == MVT_OBSTRUCTION: return MVT_OBSTRUCTION else: return MVT_N_AUTORISE elif tPion == 6: # Roi if abs(x2-x1) <= 1 and abs(y2-y1) <= 1: return MVT_OK else: return MVT_N_AUTORISE else: return MVT_PION_INC else: return MVT_SAUT_AMI else: return MVT_SUR_PLACE else: return MVT_SELECTION return MVT_INCONNU def mvtsPossibles(self, x1, y1): tableau = [] for x2 in range(0, CASES_COTE): for y2 in range(0, CASES_COTE): if self.mvtPossible(x1, y1, x2, y2) == MVT_OK: tableau.append([x2, y2]) return tableau def dPion(self, x1, y1, x2, y2): test = self.mvtPossible(x1, y1, x2, y2) if test == MVT_OK: self.grille[x1][y1], self.grille[x2][y2] = 0, self.grille[x1][y1] self.joueur = not self.joueur return test # GUI from tkinter import * DECX = 0 DECY = 0 COTE_CASE = 50 MARGE_PIONS = 5 TEMPS_ANIM = 200 INTER_ANIM = 10 class PlateauTk: def __init__(self): self.fen = None self.can = None self.chaine = None self.grilleDamier = [] self.imagesOriginales = [] self.imagesRedim = [] self.photos = [] self.grillePions = [] self.animations = [] self.dEtape = True self.dx1 = -1 self.dy1 = -1 self.dx2 = -1 self.dy2 = -1 self.mvtsPossibles = [] self.logique = LogiqueEchecs() self.creerFen() self.importerImages() self.redimImages() self.cDamier() self.cGrille() self.remplirGrille(self.logique.grille) def creerFen(self): self.fen = Tk() self.fen.title("Jeu d'Échecs") self.can = Canvas(self.fen, width=COTE_CASE * CASES_COTE, height=COTE_CASE * CASES_COTE, bg="ivory") self.can.grid(row=0, column=1, columnspan=3) self.can.bind('', self.clic) self.chaine = Label(self.fen, text="Aux blancs") self.chaine.grid(row=2, column=2, padx=3, pady=3) # Button(self.fen, text="Nv. Partie", command=f_nvPartie).grid(row=2, \ # column=1, padx=3, pady=3) Button(self.fen, text="Quitter", command=self.fen.destroy).grid(row=2, column=3, padx=3, pady=3) def statut(self, texte, delai=0): self.chaine.config(text=texte) # TODO Timeout effacer si parametre / Liste def importerImages(self): for piece in range(0, 21): nom = 'sprites/' if piece % 10 == 1: nom += 'pion' elif piece % 10 == 2: nom += 'tour' elif piece % 10 == 3: nom += 'cavalier' elif piece % 10 == 4: nom += 'fou' elif piece % 10 == 5: nom += 'dame' elif piece % 10 == 6: nom += 'roi' else: self.imagesOriginales.append('') continue if piece < 10: nom += 'B' else: nom += 'N' nom += '.gif' self.imagesOriginales.append(PhotoImage(file=nom)) def redimImages(self): sample = int(504 / (COTE_CASE - MARGE_PIONS)) for piece in range(0, 21): if self.imagesOriginales[piece] != '': self.imagesRedim.append(self.imagesOriginales[piece]. subsample(sample)) else: self.imagesRedim.append('') # Dessin @staticmethod def caseCouleur(blanc, contexte): if contexte == 1: # Sélectionné return '#a0cefe' if blanc else '#478bd1' elif contexte == 2: # Possible return '#bafea0' if blanc else '#6ed147' elif contexte == 3: # Impossible return '#fea0ab' if blanc else '#d14758' else: # Normal return '#ffce9e' if blanc else '#d18b47' def cCase(self, x, y): couleur = self.caseCouleur(not LogiqueEchecs.eNoir(x, y), 0) return self.can.create_rectangle(x * COTE_CASE, y * COTE_CASE, (x + 1) * COTE_CASE, (y + 1) * COTE_CASE) def coulCase(self, x, y, contexte): couleur = self.caseCouleur(not self.logique.eNoir(x, y), contexte) self.can.itemconfig(self.grilleDamier[x][y], fill=couleur, outline=couleur) def coulDamier(self): for x in range(0, CASES_COTE): for y in range(0, CASES_COTE): self.coulCase(x, y, 0) def cDamier(self): self.grilleDamier = [] for x in range(0, CASES_COTE): colonne = [] for y in range(0, CASES_COTE): colonne.append(self.cCase(x + DECX, y + DECY)) self.grilleDamier.append(colonne) self.coulDamier() def cPion(self, x, y, piece): if piece > 0: self.grillePions[x][y] = self.can.create_image((x + .5) * COTE_CASE, (y + .5) * COTE_CASE, image=self.imagesRedim[piece]) else: self.grillePions[x][y] = False def cGrille(self): self.grillePions = [] for x in range(0, CASES_COTE): # Crée self.grillePions colonne = [] for y in range(0, CASES_COTE): colonne.append(False) self.grillePions.append(colonne) def remplirGrille(self, j_grilleF): for x in range(0, CASES_COTE): # Remplis self.grillePions for y in range(0, CASES_COTE): self.cPion(x, y, j_grilleF[x][y]) # Interaction def statutPrendre(self): if self.logique.joueur: joueur = 'blancs' else: joueur = 'noirs' self.statut('Prendre (' + joueur + ')') @staticmethod def animationDCoords(i): x = i['x1'] + (i['x2']-i['x1']) * (i['avancement']/i['total']) y = i['y1'] + (i['y2']-i['y1']) * (i['avancement']/i['total']) return [x, y] def animation(self): animationsNv = [] for i in self.animations: if i['avancement'] < i['total']: if i['type'] == 'd': coords = self.animationDCoords(i) self.can.coords(i['pion'], coords[0], coords[1]) # elif i['type'] == 'f': # TODO Opacité de i['pion'] # elif i['type'] == 'c': # TODO Opacité de case i['avancement'] += INTER_ANIM animationsNv.append(i) else: if i['type'] == 'd': self.can.coords(i['pion'], i['x2'], i['y2']) elif i['type'] == 'f': self.can.delete(i['pion']) elif i['type'] == 'c': self.coulCase(i['x'], i['y'], 0) self.animations = animationsNv if len(animationsNv): self.fen.after(INTER_ANIM, self.animation) def animer(self, animation): etaitVide = len(self.animations) < 1 self.animations.append(animation) if etaitVide: self.animation() def animerD(self, x1, y1, x2, y2, pion): if len(self.animations): for i in self.animations: if i['pion'] == pion: # Si une animation pour ce pion existe # déjà, on la reprend et on la modifie coords = self.animationDCoords(i) i['x1'] = coords[0] i['y1'] = coords[1] i['x2'] = x2 i['y2'] = y2 # i['total'] = i['total'] - i['avancement'] i['total'] = TEMPS_ANIM i['avancement'] = 0 return animation = { 'x1': x1, 'y1': y1, 'x2': x2, 'y2': y2, 'pion': pion, 'type': 'd', 'total': TEMPS_ANIM, 'avancement': 0 } self.can.tag_raise(pion) # Mise au premier plan self.animer(animation) def animerF(self, pion): # Pion fade-out animation = { 'pion': pion, 'type': 'f', 'total': TEMPS_ANIM, 'avancement': 0 } self.animer(animation) def animerC(self, x ,y): animation = { 'type': 'c', 'x': x, 'y': y, 'total': TEMPS_ANIM, 'avancement': 0 } self.animer(animation) def dPion(self, x1, y1, x2, y2): test = self.logique.dPion(x1, y1, x2, y2) if test == MVT_OK: # Si déplacement possible if self.grillePions[x2][y2]: # Si saut → Animation effacement self.animerF(self.grillePions[x2][y2]) self.grillePions[x2][y2], self.grillePions[x1][y1] = \ self.grillePions[x1][y1], False self.animerD((x1 + .5) * COTE_CASE, (y1 + .5) * COTE_CASE, \ (x2 + .5) * COTE_CASE, (y2 + .5) * COTE_CASE, \ self.grillePions[x2][y2]) return True else: self.statut('Déplacment impossible ! (' + str(test) + ')') return False def dClic(self, x, y): # if not len(self.animations): if self.dEtape: # Prendre self.dx1, self.dy1 = x, y self.coulDamier() # Effacement des surbrillances if self.logique.aSonTour(self.logique.grille[self.dx1][self.dy1]): # Si possible jouer self.coulCase(self.dx1, self.dy1, 1) self.mvtPossibles = self.logique.mvtsPossibles(self.dx1, self.dy1) # Surbrillance bleue for i in self.mvtPossibles: # Surbrillances vertes self.coulCase(i[0], i[1], 2) self.statut('Poser') self.dEtape = not self.dEtape else: # Si pas pssible jouer self.coulCase(self.dx1, self.dy1, 3) self.animerC(self.dx1, self.dy1) else: # Poser self.dx2, self.dy2 = x, y if self.dPion(self.dx1, self.dy1, self.dx2, self.dy2) or (self.dx1 == self.dx2 and self.dy1 == self.dy2): # Si déplacement fait / Annule dépalcement self.coulDamier() # Effacer Surbrillance self.dEtape = not self.dEtape self.statutPrendre() else: # Si mauvais déplacement self.coulCase(self.dx2, self.dy2, 3) self.animerC(self.dx2, self.dy2) def clic(self, event): x = event.x // COTE_CASE y = event.y // COTE_CASE self.dClic(x, y) p = PlateauTk() # TODO Un jeu (canvas) et un frontend (fenetre)