[Echecs] Ajout de documentation

This commit is contained in:
Geoffrey Frogeye 2014-12-14 00:47:49 +01:00
parent 87635bb8a6
commit 9c10044275
4 changed files with 258 additions and 77 deletions

View file

@ -15,25 +15,35 @@ python app.py
(en supposant que `python` est dans la variable d'environnement `path` (ou `$PATH` sous Windows) et point vers l'éxecutable de Python 3) (en supposant que `python` est dans la variable d'environnement `path` (ou `$PATH` sous Windows) et point vers l'éxecutable de Python 3)
##Fonctions non-implémentées ##Objectifs non-réalisés
###Règles des échecs ###Objectifs de TP
* Le Roque * Implémenter le jeu d'échecs entier
* La promotion du pion
* Prise en passant
* Détection du "pat" (le joueur ne pouvant pas jouer est considéré comme perdant, même s'il n'est pas en échec)
###Consignes de TP Le jeu d'échec n'est pas totalement implémenté, voici la liste non-exhaustive des règles qui ne sont pas implémentées
* Le Roque
* La promotion du pion
* Prise en passant
* Détection du "pat" (le joueur ne pouvant pas jouer est considéré comme perdant, même s'il n'est pas en échec)
* Pouvoir choisir entre jeu de dame et jeu d'échecs * Pouvoir choisir entre jeu de dame et jeu d'échecs
Le jeu de dame n'est pas implémenté, cependant les autres classes sont compatibles avec lui car il suffit de changer la classe Logique**** utilisée. Le jeu de dame n'est pas implémenté, bien qu'il ne soit nécessaire de ne modifier aucune partie du code déjà existant.
* Redimensionnement de la fenêtre * Redimensionnement de la fenêtre
Il suffirait d'appeler PlateauTk.redimCan(min(xMax, yMax)) à chaque redimensionnement de la fenêtre. Cependant j'ai un peu de mal à comprendre comment fonctionne Tk. Il suffirait d'appeler PlateauTk.redimCan(min(xMax, yMax)) à chaque redimensionnement de la fenêtre. Cependant j'ai un peu de mal à comprendre comment fonctionne Tk.
* Documentation et tests ###Objectifs personnels
Étant donné que les fonctions sont plutôt expliquables d'elle-même par leur nom, que je suis seul à travailler sur ce projet, et que j'ai passé pas mal de temps à découper les fonctions, elles ne sont ni documentées ni testées. * Qualité du code
Bien que le code fonctionne, soit plutôt flexible, relativement documenté et utilise la notion d'objet, il est loin d'être irréprochable. Certaines fonctions font probablement plus de calcul que nécessaire, la création de certaines variables pourraient être évité. L'ajout de plus de constante au lieu de valeurs arbitraire contribuerait à la lisibilité du code. Certaines instructions ne sont probablement pas sémantiquement correcte.
* Originalité
Le jeu actuel ne possède aucun point fort (au contraire) qui pourrait le démarquer des jeux d'Échecs existant. Très peu d'utilisateurs pourraient l'utiliser dans une optique autre que le développement ou le test.
Les commentaires affublés d'une marque `TODO` signifient qu'une

View file

@ -5,3 +5,9 @@ from guiTk import FenetreTk
if __name__ == '__main__': if __name__ == '__main__':
# p = PlateauTk(LogiqueEchecs()) # p = PlateauTk(LogiqueEchecs())
f = FenetreTk() f = FenetreTk()
# TODO pion → piece Quand nécessaire
# TODO mvtSansEchec → depl
# TODO Entrée / Sortie ?
# TODO Doctest
# TODO cls au lieu de self quand nécessaires

View file

@ -2,35 +2,41 @@ from tkinter import *
from logique import LogiqueEchecs from logique import LogiqueEchecs
class PlateauTk: class PlateauTk:
"""
Plateau de jeu de damier en canvas Tk
"""
RATIO_PCE_CASE = .95 RATIO_PCE_CASE = .95 # Place que prend la pièce sur la case
TEMPS_ANIM = 200 TEMPS_ANIM = 200 # Temps en ms d'une animation
INTER_ANIM = 10 INTER_ANIM = 10 # Temps en ms entre deux actualisations d'animation
COTE_TOUT_DEFAUT = 500 COTE_TOUT_DEFAUT = 500 # Coté par défaut du plateau
def __init__(self, fen, can, statut, logique): def __init__(self, fen, can, statut, logique):
"""
Constructeur de PlateauTk
"""
self.can = can self.can = can # Canvas
self.fen = fen self.fen = fen # Fenêtre Tk (nécessaire pour lancer un timeout pour l'animation)
self.statut = statut self.statut = statut # Fonction qui envoie un message à l'utilisateur
self.chaine = None self.grilleDamier = [] # Tableau contenant les références aux cases du damier
self.grilleDamier = [] self.grillePions = [] # Tableau contenant les références aux pièces
self.imagesOriginales = {} self.photos = [] # Liste contenant les différentes photos utilisées (afin qu'elles ne soient pas recyclées)
self.imagesRedim = {} self.imagesOriginales = {} # Dictionnaire associant chaque pièce à son image originale
self.photos = [] self.imagesRedim = {} # Dictionnaire associant chaque pièce à son image redimensionnée
self.grillePions = [] self.animations = [] # Liste contenant les animations en cours
self.animations = []
self.dEtape = True self.dEtape = True # Étape du déplacement (True : prendre la pièce, False : poser la pièce)
self.dx1 = -1 self.dx1 = -1 # Coordonnées X de la pièce sélectionnée
self.dy1 = -1 self.dy1 = -1 # Coordonnées Y de la pièce sélectionnée
self.dx2 = -1 self.dx2 = -1 # Coordonnées X de la destination
self.dy2 = -1 self.dy2 = -1 # Coordonnées Y de la destination
self.mvtsPossibles = [] self.mvtsPossibles = [] # Liste des mouvements possibles pour la pièce sélectionnée
self.logique = logique self.coteCase = 0 # Coté d'une case
self.logique = logique # Logique du jeu
self.coteCase = 0
self.importerImages() self.importerImages()
self.redimCan(self.COTE_TOUT_DEFAUT) self.redimCan(self.COTE_TOUT_DEFAUT)
@ -39,6 +45,9 @@ class PlateauTk:
self.statutPrendre() self.statutPrendre()
def redimCan(self, cote): def redimCan(self, cote):
"""
Redimensionne le plateau de jeu
"""
self.can.delete(ALL) self.can.delete(ALL)
self.coteCase = cote // self.logique.CASES_COTE self.coteCase = cote // self.logique.CASES_COTE
cote = self.logique.CASES_COTE * self.coteCase cote = self.logique.CASES_COTE * self.coteCase
@ -52,6 +61,9 @@ class PlateauTk:
def nomPiece(self, piece): def nomPiece(self, piece):
"""
Renvoi le nom de fichier de la pièce donnée
"""
tPiece = self.logique.tPiece(piece) tPiece = self.logique.tPiece(piece)
if tPiece == self.logique.PCE_PION: if tPiece == self.logique.PCE_PION:
return 'pion' return 'pion'
@ -68,20 +80,32 @@ class PlateauTk:
else: else:
return False return False
def importerImage(self, piece, couleur): def importerImage(self, piece, couleur): # TODO piece stocke déjà la couleur
"""
Importe l'image de la pièce donnée
"""
nom = 'sprites/'+self.nomPiece(piece)+couleur+'.gif' nom = 'sprites/'+self.nomPiece(piece)+couleur+'.gif'
self.imagesOriginales.update({piece:PhotoImage(file=nom)}) self.imagesOriginales.update({piece:PhotoImage(file=nom)})
def importerImages(self): def importerImages(self):
"""
Importe les images des pièces du jeu
"""
for piece in self.logique.BLANCS: for piece in self.logique.BLANCS:
self.importerImage(piece, 'B') self.importerImage(piece, 'B')
for piece in self.logique.NOIRS: for piece in self.logique.NOIRS:
self.importerImage(piece, 'N') self.importerImage(piece, 'N')
def redimImage(self, piece, sample): def redimImage(self, piece, sample):
"""
Redimensionne l'image de la pièce donnée
"""
self.imagesRedim.update({piece:self.imagesOriginales[piece].subsample(sample)}) self.imagesRedim.update({piece:self.imagesOriginales[piece].subsample(sample)})
def redimImages(self): def redimImages(self):
"""
Redimensionne l'image des pièces du jeu
"""
sample = int(504 // (self.coteCase * self.RATIO_PCE_CASE)) # TODO S'en sort pour être toujours plus grand sample = int(504 // (self.coteCase * self.RATIO_PCE_CASE)) # TODO S'en sort pour être toujours plus grand
for piece in self.logique.BLANCS: for piece in self.logique.BLANCS:
self.redimImage(piece, sample) self.redimImage(piece, sample)
@ -90,7 +114,10 @@ class PlateauTk:
# Dessin # Dessin
@staticmethod @staticmethod
def caseCouleur(blanc, contexte): def caseCouleur(blanc, contexte=0):
"""
Retourne la couleur hexadécimale de la case de couleur et de contexte donnée
"""
if contexte == 1: # Sélectionné if contexte == 1: # Sélectionné
return '#a0cefe' if blanc else '#478bd1' return '#a0cefe' if blanc else '#478bd1'
elif contexte == 2: # Possible / Victoire elif contexte == 2: # Possible / Victoire
@ -101,21 +128,33 @@ class PlateauTk:
return '#ffce9e' if blanc else '#d18b47' return '#ffce9e' if blanc else '#d18b47'
def cCase(self, x, y): def cCase(self, x, y):
"""
Crée la case aux coordonnées données
"""
couleur = self.caseCouleur(self.logique.eCaseBlanche(x, y), 0) couleur = self.caseCouleur(self.logique.eCaseBlanche(x, y), 0)
return self.can.create_rectangle(x * self.coteCase, y * self.coteCase, (x + 1) * self.coteCase, (y + 1) * self.coteCase) return self.can.create_rectangle(x * self.coteCase, y * self.coteCase, (x + 1) * self.coteCase, (y + 1) * self.coteCase)
def coulCase(self, x, y, contexte): def coulCase(self, x, y, contexte=0):
"""
Colorie la case aux coordonnées données selon le contexte donné
"""
couleur = self.caseCouleur(self.logique.eCaseBlanche(x, y), contexte) couleur = self.caseCouleur(self.logique.eCaseBlanche(x, y), contexte)
self.can.itemconfig(self.grilleDamier[x][y], fill=couleur, outline=couleur) self.can.itemconfig(self.grilleDamier[x][y], fill=couleur, outline=couleur)
def coulDamier(self): def coulDamier(self, contexte=0):
"""
Colorie toutes les cases selon le contexte donné
"""
for x in range(0, self.logique.CASES_COTE): for x in range(0, self.logique.CASES_COTE):
for y in range(0, self.logique.CASES_COTE): for y in range(0, self.logique.CASES_COTE):
self.coulCase(x, y, 0) self.coulCase(x, y, contexte)
def cDamier(self): def cDamier(self):
"""
Génère le damier
"""
self.grilleDamier = [] self.grilleDamier = []
for x in range(0, self.logique.CASES_COTE): for x in range(0, self.logique.CASES_COTE): # TODO Peut être amélioré
colonne = [] colonne = []
for y in range(0, self.logique.CASES_COTE): for y in range(0, self.logique.CASES_COTE):
colonne.append(self.cCase(x, y)) colonne.append(self.cCase(x, y))
@ -123,12 +162,18 @@ class PlateauTk:
self.coulDamier() self.coulDamier()
def cPion(self, x, y, piece): def cPion(self, x, y, piece):
if piece > 0: """
Crée la pièce aux coordonnées données
"""
if self.logique.ePiece(piece):
self.grillePions[x][y] = self.can.create_image((x + .5) * self.coteCase, (y + .5) * self.coteCase, image=self.imagesRedim[piece]) self.grillePions[x][y] = self.can.create_image((x + .5) * self.coteCase, (y + .5) * self.coteCase, image=self.imagesRedim[piece])
else: else:
self.grillePions[x][y] = False self.grillePions[x][y] = False
def cGrille(self): def cGrille(self):
"""
Crée le tableau contenant les images de pièces
"""
self.grillePions = [] self.grillePions = []
for x in range(0, self.logique.CASES_COTE): # Crée self.grillePions for x in range(0, self.logique.CASES_COTE): # Crée self.grillePions
colonne = [] colonne = []
@ -137,12 +182,18 @@ class PlateauTk:
self.grillePions.append(colonne) self.grillePions.append(colonne)
def remplirGrille(self, j_grilleF): def remplirGrille(self, j_grilleF):
"""
Remplis la grille des images de pièce selon la grille des pièces logiques.
"""
for x in range(0, self.logique.CASES_COTE): # Remplis self.grillePions for x in range(0, self.logique.CASES_COTE): # Remplis self.grillePions
for y in range(0, self.logique.CASES_COTE): for y in range(0, self.logique.CASES_COTE):
self.cPion(x, y, j_grilleF[x][y]) self.cPion(x, y, j_grilleF[x][y])
# Interaction # Interaction
def nomJoueur(self, joueur, pluriel=True): def nomJoueur(self, joueur, pluriel=True):
"""
Retourne le nom du joueur donné
"""
if joueur == self.logique.BLANC: if joueur == self.logique.BLANC:
nom = 'blanc' nom = 'blanc'
elif joueur == self.logique.NOIR: elif joueur == self.logique.NOIR:
@ -154,15 +205,25 @@ class PlateauTk:
return nom return nom
def statutPrendre(self): def statutPrendre(self):
self.statut('Prendre (' + self.nomJoueur(self.logique.joueur) + ')') """
Change le statut indiquant au joueur en cours de jouer.
"""
self.statut('Aux ' + self.nomJoueur(self.logique.joueur, pluriel=True) + ' de sélectionner une pièce.')
@staticmethod @staticmethod
def animationDCoords(i): def animationDCoords(i):
"""
Retourne les coordonnées auxquelles doit se placer une pièce en cours d'animation
"""
# TODO Faire autre chose qu'une animation linéaire constante
x = i['x1'] + (i['x2']-i['x1']) * (i['avancement']/i['total']) x = i['x1'] + (i['x2']-i['x1']) * (i['avancement']/i['total'])
y = i['y1'] + (i['y2']-i['y1']) * (i['avancement']/i['total']) y = i['y1'] + (i['y2']-i['y1']) * (i['avancement']/i['total'])
return [x, y] return [x, y]
def animation(self): def animation(self):
"""
Effectue les changements graphique nécessaire à la création d'une image d'animation.
"""
animationsNv = [] animationsNv = []
for i in self.animations: for i in self.animations:
if i['avancement'] < i['total']: if i['avancement'] < i['total']:
@ -183,22 +244,29 @@ class PlateauTk:
elif i['type'] == 'c': elif i['type'] == 'c':
self.coulCase(i['x'], i['y'], 0) self.coulCase(i['x'], i['y'], 0)
self.animations = animationsNv self.animations = animationsNv
if len(animationsNv): if len(animationsNv): # Si il y aura encore des animations à faire
self.fen.after(self.INTER_ANIM, self.animation) self.fen.after(self.INTER_ANIM, self.animation) # On prévoit une image (boucle)
def animer(self, animation): def animer(self, animation):
"""
Ajoute une animation à la liste des animations en cours, et lance
la fonction animation() si la boucle n'est déjà pas lancée.
"""
etaitVide = len(self.animations) < 1 etaitVide = len(self.animations) < 1
self.animations.append(animation) self.animations.append(animation)
if etaitVide: if etaitVide:
self.animation() self.animation()
def animerD(self, x1, y1, x2, y2, pion): def animerD(self, x1, y1, x2, y2, pion):
"""
Ajoute une animation pour un déplacment de pion
"""
if len(self.animations): if len(self.animations):
for i in self.animations: for i in self.animations:
if i['type'] == 'd' and i['pion'] == pion: if i['type'] == 'd' and i['pion'] == pion:
# Si une animation pour ce pion existe déjà, on la reprend et on la modifie # Si une animation pour ce pion existe déjà, on la reprend et on la modifie
coords = self.animationDCoords(i) coords = self.animationDCoords(i)
i['x1'] = coords[0] i['x1'] = coords[0] # TODO Simplifier avec update()
i['y1'] = coords[1] i['y1'] = coords[1]
i['x2'] = x2 i['x2'] = x2
i['y2'] = y2 i['y2'] = y2
@ -220,7 +288,10 @@ class PlateauTk:
self.can.tag_raise(pion) # Mise au premier plan self.can.tag_raise(pion) # Mise au premier plan
self.animer(animation) self.animer(animation)
def animerF(self, pion): # Pion fade-out def animerF(self, pion):
"""
Ajoute une animation pour la suppression d'un pion
"""
animation = { animation = {
'pion': pion, 'pion': pion,
'type': 'f', 'type': 'f',
@ -230,6 +301,9 @@ class PlateauTk:
self.animer(animation) self.animer(animation)
def animerC(self, x ,y): def animerC(self, x ,y):
"""
Ajoute une animation pour l'effacement de la surbrillance d'une case
"""
animation = { animation = {
'type': 'c', 'type': 'c',
'x': x, 'x': x,
@ -240,18 +314,25 @@ class PlateauTk:
self.animer(animation) self.animer(animation)
def victoire(self): def victoire(self):
"""
Indique à l'utilisateur la victoire et décore le plateau pour l'occasion
"""
self.statut('Victoire des ' + self.nomJoueur(self.logique.victorieux) + ' !') self.statut('Victoire des ' + self.nomJoueur(self.logique.victorieux) + ' !')
self.coulDamier() self.coulDamier()
for x in range(0, self.logique.CASES_COTE): for x in range(0, self.logique.CASES_COTE):
for y in range(0, self.logique.CASES_COTE): for y in range(0, self.logique.CASES_COTE):
pion = self.logique.grille[x][y] pion = self.logique.grille[x][y]
if pion > 0: if self.logique.ePiece(pion):
if self.logique.ePionNoir(pion) ^ self.logique.victorieux: if self.logique.ePionNoir(pion) ^ self.logique.victorieux:
self.coulCase(x, y, 2) self.coulCase(x, y, 2)
else: else:
self.coulCase(x, y, 3) self.coulCase(x, y, 3)
def dPion(self, x1, y1, x2, y2): def dPion(self, x1, y1, x2, y2):
"""
Déplace le pion aux coordonnées 1 aux coordonnées 2.
(Vérifie la possibilité du mouvement et crée les animations nécessaires)
"""
test = self.logique.dPion(x1, y1, x2, y2) test = self.logique.dPion(x1, y1, x2, y2)
if test['valide'] == True: # Si déplacement possible if test['valide'] == True: # Si déplacement possible
for s in test['supprimer']: for s in test['supprimer']:
@ -268,6 +349,9 @@ class PlateauTk:
return test['valide'] return test['valide']
def dClic(self, x, y): def dClic(self, x, y):
"""
Réagit en fonction d'un clic sur une case aux coordonnées données
"""
if not self.logique.partieFinie: if not self.logique.partieFinie:
if self.dEtape: # Prendre if self.dEtape: # Prendre
self.dx1, self.dy1 = x, y self.dx1, self.dy1 = x, y
@ -296,26 +380,38 @@ class PlateauTk:
self.animerC(self.dx2, self.dy2) self.animerC(self.dx2, self.dy2)
def clic(self, event): def clic(self, event):
if event.x in range(0, self.coteCase * self.logique.CASES_COTE) and event.y in range(0, self.coteCase * self.logique.CASES_COTE): """
Réagit en fonction d'un clic
"""
# if event.x in range(0, self.coteCase * self.logique.CASES_COTE) and event.y in range(0, self.coteCase * self.logique.CASES_COTE):
self.dClic(event.x // self.coteCase, event.y // self.coteCase) self.dClic(event.x // self.coteCase, event.y // self.coteCase)
class FenetreTk: class FenetreTk:
"""
Fenêtre pour jouer à des jeux sur damier
"""
PLACEHOLDER_DIMENSIONS = 300 PLACEHOLDER_DIMENSIONS = 300 # Coté du canvas vide du début
def __init__(self): def __init__(self):
"""
Constructeur de FenetreTk
"""
self.fen = None self.fen = None # Objet Fenêtre
self.can = None self.can = None # Objet Canvas
self.chaine = '' self.chaine = None # Objet Label de statut
self.plateau = None self.plateau = None # Plateau de jeu
self.creerFen() self.creerFen()
self.fen.mainloop() self.fen.mainloop()
def creerFen(self): def creerFen(self):
"""
Crée la fenêtre et ses éléments
"""
self.fen = Tk() self.fen = Tk()
self.fen.title("Jeu de plateau") self.fen.title("Jeu de plateau")
self.can = Canvas(self.fen, width=self.PLACEHOLDER_DIMENSIONS, height=self.PLACEHOLDER_DIMENSIONS, bg="ivory") self.can = Canvas(self.fen, width=self.PLACEHOLDER_DIMENSIONS, height=self.PLACEHOLDER_DIMENSIONS, bg="ivory")
@ -326,8 +422,17 @@ class FenetreTk:
Button(self.fen, text="Quitter", command=self.fen.destroy).grid(row=2, column=3, 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): def statut(self, texte, delai=0):
"""
Change le message affiché.
"""
self.chaine.config(text=texte) self.chaine.config(text=texte)
# TODO Timeout effacer si parametre / Liste # TODO Messages permanents et messages temporaires
# (exemple permanent : "Aux blancs de jouer", exemple
# temporaire "Vous ne pouvez pas jouer ici !")
def nvPartie(self): def nvPartie(self):
"""
Démarre une nouvelle partie.
"""
del self.plateau
self.plateau = PlateauTk(self.fen, self.can, self.statut, LogiqueEchecs()) self.plateau = PlateauTk(self.fen, self.can, self.statut, LogiqueEchecs())

View file

@ -5,36 +5,50 @@ class Logique:
Logique des jeux sur damier Logique des jeux sur damier
""" """
PCE_VIDE = 0 PCE_VIDE = 0 # Valeur à mettre sur la grille quand il n'y a pas de pièce
BLANC = True BLANC = True # Valeur correspondant au joueur blanc
NOIR = False NOIR = False # Valeur correspondant au joueur noir
@staticmethod @staticmethod
def eCaseBlanche(xD, yD): def eCaseBlanche(x, y):
return xD % 2 == yD % 2 """
Indique si la case aux coordonnées indiquée est blanche.
"""
return x % 2 == y % 2
def cGrille(self): def cGrille(self):
for x in range(self.CASES_COTE): """
Crée le tableau self.grille.
"""
for x in range(self.CASES_COTE): # TODO Peut être amélioré
colonne = [] colonne = []
for y in range(self.CASES_COTE): for y in range(self.CASES_COTE):
colonne.append(self.PCE_VIDE) colonne.append(self.PCE_VIDE)
self.grille.append(colonne) self.grille.append(colonne)
def cPion(self, x, y, piece):
self.grille[x][y] = piece
return True
def ePionBlanc(self, pion): def ePionBlanc(self, pion):
"""
Indique si la pièce est une pièce blanche.
"""
return pion in self.BLANCS return pion in self.BLANCS
def ePionNoir(self, pion): def ePionNoir(self, pion):
"""
Indique si la pièce est une pièce noire.
"""
return pion in self.NOIRS return pion in self.NOIRS
def ePiece(self, piece): def ePiece(self, piece):
"""
Indique si la pièce est une pièce.
"""
# return piece != self.PCE_VIDE # return piece != self.PCE_VIDE
return self.ePionBlanc(piece) or self.ePionNoir(piece) return self.ePionBlanc(piece) or self.ePionNoir(piece)
def tPiece(self, piece): def tPiece(self, piece):
"""
Retourne le type de pièce de la pièce donnée.
"""
if self.ePionBlanc(piece): if self.ePionBlanc(piece):
return piece - self.DECALAGE_BLANCS return piece - self.DECALAGE_BLANCS
elif self.ePionNoir(piece): elif self.ePionNoir(piece):
@ -43,6 +57,9 @@ class Logique:
return self.PCE_VIDE return self.PCE_VIDE
def aSonTour(self, pion): def aSonTour(self, pion):
"""
Indique si la pièce donnée a le droit de jouer.
"""
return (self.ePionNoir(pion) and self.joueur == self.NOIR) or \ return (self.ePionNoir(pion) and self.joueur == self.NOIR) or \
(self.ePionBlanc(pion) and self.joueur == self.BLANC) (self.ePionBlanc(pion) and self.joueur == self.BLANC)
@ -52,9 +69,9 @@ class LogiqueEchecs(Logique):
Logique du jeu d'Échecs Logique du jeu d'Échecs
""" """
CASES_COTE = 8 CASES_COTE = 8 # Cases de coté
# Pièces # Type de pièces
PCE_PION = 1 PCE_PION = 1
PCE_TOUR = 2 PCE_TOUR = 2
PCE_CAVALIER = 3 PCE_CAVALIER = 3
@ -62,14 +79,14 @@ class LogiqueEchecs(Logique):
PCE_DAME = 5 PCE_DAME = 5
PCE_ROI = 6 PCE_ROI = 6
DECALAGE_BLANCS = 0 DECALAGE_BLANCS = 0 # Valeur à partir de laquelle sont compris les pièces blanches
DECALAGE_NOIRS = 10 DECALAGE_NOIRS = 10 # Valeur à partir de laquelle sont compris les pièces noires
BLANCS = range(DECALAGE_BLANCS+PCE_PION, DECALAGE_BLANCS+PCE_ROI+1) BLANCS = range(DECALAGE_BLANCS+PCE_PION, DECALAGE_BLANCS+PCE_ROI+1) # Intervalle contenant les pièces blancs
NOIRS = range(DECALAGE_NOIRS+PCE_PION, DECALAGE_NOIRS+PCE_ROI+1) NOIRS = range(DECALAGE_NOIRS+PCE_PION, DECALAGE_NOIRS+PCE_ROI+1) # Intervalle contenant les pièces noirs
# Codes du mouvement # Codes d'erreur de mouvement
MVT_INCONNU = 'Cause inconnue' MVT_INCONNU = 'Cause inconnue'
MVT_OK = 'Valide' MVT_OK = 'Valide'
MVT_ROQUE = 'Roque' MVT_ROQUE = 'Roque'
@ -82,15 +99,20 @@ class LogiqueEchecs(Logique):
MVT_ECHEC = 'Échec au roi' MVT_ECHEC = 'Échec au roi'
def __init__(self): def __init__(self):
"""
self.grille = [] Constructeur de LogiqueEchecs
"""
self.grille = [] # Grille de jeu
self.cGrille() self.cGrille()
self.remplirGrille() self.remplirGrille()
self.joueur = self.BLANC self.joueur = self.BLANC # Premier joueur
self.partieFinie = False self.partieFinie = False
self.victorieux = None self.victorieux = None
def remplirGrille(self): 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 = [self.PCE_TOUR, self.PCE_CAVALIER, self.PCE_FOU, self.PCE_ROI, self.PCE_DAME]
speciales += speciales[2::-1] speciales += speciales[2::-1]
for i in range(0, 8): for i in range(0, 8):
@ -100,6 +122,9 @@ class LogiqueEchecs(Logique):
self.grille[i][7] = self.DECALAGE_BLANCS + speciales[i] self.grille[i][7] = self.DECALAGE_BLANCS + speciales[i]
def mvtPossibleSansEchecPion(self, x1, y1, x2, y2): def mvtPossibleSansEchecPion(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 x1 == x2 and self.grille[x2][y2] <= 0: # Avance
if self.joueur: if self.joueur:
if y2 == y1 - 1: if y2 == y1 - 1:
@ -132,6 +157,9 @@ class LogiqueEchecs(Logique):
return self.MVT_N_AUTORISE return self.MVT_N_AUTORISE
def mvtPossibleSansEchecTour(self, x1, y1, x2, y2): def mvtPossibleSansEchecTour(self, x1, y1, x2, y2):
"""
Vérifie si le déplacement est possible pour une tour.
"""
if y1 == y2: if y1 == y2:
sens = (x2-x1)//abs(x2-x1) sens = (x2-x1)//abs(x2-x1)
for x in range(x1+sens, x2, sens): for x in range(x1+sens, x2, sens):
@ -147,6 +175,9 @@ class LogiqueEchecs(Logique):
return self.MVT_OK return self.MVT_OK
def mvtPossibleSansEchecFou(self, x1, y1, x2, y2): def mvtPossibleSansEchecFou(self, x1, y1, x2, y2):
"""
Vérifie si le déplacement est possible pour un fou.
"""
if abs(x2-x1) == abs(y2-y1): if abs(x2-x1) == abs(y2-y1):
sensX = (x2-x1)//abs(x2-x1) sensX = (x2-x1)//abs(x2-x1)
sensY = (y2-y1)//abs(y2-y1) sensY = (y2-y1)//abs(y2-y1)
@ -168,12 +199,18 @@ class LogiqueEchecs(Logique):
return self.MVT_N_AUTORISE return self.MVT_N_AUTORISE
def mvtPossibleSansEchecCavalier(self, x1, y1, x2, y2): def mvtPossibleSansEchecCavalier(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): if (abs(x2-x1) == 2 and abs(y2-y1) == 1) or (abs(y2-y1) == 2 and abs(x2-x1) == 1):
return self.MVT_OK return self.MVT_OK
else: else:
return self.MVT_N_AUTORISE return self.MVT_N_AUTORISE
def mvtPossibleSansEchec(self, x1, y1, x2, y2): def mvtPossibleSansEchec(self, x1, y1, x2, y2): # TODO Utiliser la gestion d'erreurs ?
"""
Vérifie si le déplacement est possible.
"""
pion = self.grille[x1][y1] pion = self.grille[x1][y1]
if self.aSonTour(pion): if self.aSonTour(pion):
if (x1 != x2 or y1 != y2): if (x1 != x2 or y1 != y2):
@ -212,12 +249,18 @@ class LogiqueEchecs(Logique):
return self.MVT_INCONNU return self.MVT_INCONNU
def mvtPossible(self, x1, y1, x2, y2): 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.mvtPossibleSansEchec(x1, y1, x2, y2) test = self.mvtPossibleSansEchec(x1, y1, x2, y2)
if test == self.MVT_OK: if test == self.MVT_OK:
# On copie la partie actuelle pour tester le mouvement et vérifier l'échec # On copie la partie actuelle pour tester le mouvement et vérifier l'échec
copie = copy.deepcopy(self); copie = copy.deepcopy(self);
copie.dPionSansEchec(x1, y1, x2, y2) copie.dPionSansEchec(x1, y1, x2, y2)
mvtsPossiblesTousAdverses = [] mvtsPossiblesTousAdverses = []
# On cherche la position du roi
pionRoi = self.PCE_ROI + self.DECALAGE_BLANCS if self.joueur else self.DECALAGE_NOIRS pionRoi = self.PCE_ROI + self.DECALAGE_BLANCS if self.joueur else self.DECALAGE_NOIRS
roi = [-1, -1] roi = [-1, -1]
for x in range(0, self.CASES_COTE): for x in range(0, self.CASES_COTE):
@ -225,7 +268,7 @@ class LogiqueEchecs(Logique):
mvtsPossiblesTousAdverses += copie.mvtsPossiblesSansEchec(x, y) mvtsPossiblesTousAdverses += copie.mvtsPossiblesSansEchec(x, y)
if copie.grille[x][y] == pionRoi: if copie.grille[x][y] == pionRoi:
roi = [x, y] roi = [x, y]
if roi in mvtsPossiblesTousAdverses: if roi in mvtsPossiblesTousAdverses: # Si le roi peut être sauté au tour adverse
return self.MVT_ECHEC return self.MVT_ECHEC
else: else:
return test return test
@ -233,6 +276,9 @@ class LogiqueEchecs(Logique):
return test return test
def mvtsPossiblesSansEchec(self, x1, y1): def mvtsPossiblesSansEchec(self, x1, y1):
"""
Donne la liste des déplacements possible pour la pièce donnée.
"""
tableau = [] tableau = []
for x2 in range(0, self.CASES_COTE): for x2 in range(0, self.CASES_COTE):
for y2 in range(0, self.CASES_COTE): for y2 in range(0, self.CASES_COTE):
@ -241,6 +287,9 @@ class LogiqueEchecs(Logique):
return tableau return tableau
def mvtsPossibles(self, x1, y1): def mvtsPossibles(self, x1, y1):
"""
Donne la liste des mouvements possible pour la pièce donnée.
"""
tableau = [] tableau = []
for x2 in range(0, self.CASES_COTE): for x2 in range(0, self.CASES_COTE):
for y2 in range(0, self.CASES_COTE): for y2 in range(0, self.CASES_COTE):
@ -249,6 +298,11 @@ class LogiqueEchecs(Logique):
return tableau return tableau
def dPionSansEchec(self, x1, y1, x2, y2): def dPionSansEchec(self, x1, y1, x2, y2):
"""
Déplace le pion 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.mvtPossibleSansEchec(x1, y1, x2, y2) test = self.mvtPossibleSansEchec(x1, y1, x2, y2)
if test == self.MVT_OK: if test == self.MVT_OK:
self.grille[x1][y1], self.grille[x2][y2] = self.PCE_VIDE, self.grille[x1][y1] self.grille[x1][y1], self.grille[x2][y2] = self.PCE_VIDE, self.grille[x1][y1]
@ -256,9 +310,11 @@ class LogiqueEchecs(Logique):
def vEchecMat(self): def vEchecMat(self):
""" """
Vérifie si le joueur actuel est en échec et mat et prend les mesures nécessiares. Vérifie si le joueur actuel est en échec et mat et déclenche
(Ici vrai en cas de "pat") é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 x in range(0, self.CASES_COTE):
for y in range(0, self.CASES_COTE): for y in range(0, self.CASES_COTE):
if len(self.mvtsPossibles(x, y)) > 0: if len(self.mvtsPossibles(x, y)) > 0:
@ -268,6 +324,10 @@ class LogiqueEchecs(Logique):
return True return True
def dPion(self, x1, y1, x2, y2): def dPion(self, x1, y1, x2, y2):
"""
Déplace le pion 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) test = self.mvtPossible(x1, y1, x2, y2)
retour = { retour = {
'valide': False, 'valide': False,