[Echecs] Ajout de documentation
This commit is contained in:
parent
87635bb8a6
commit
9c10044275
|
@ -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)
|
||||
|
||||
##Fonctions non-implémentées
|
||||
##Objectifs non-réalisés
|
||||
|
||||
###Règles des échecs
|
||||
###Objectifs de TP
|
||||
|
||||
* 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)
|
||||
* Implémenter le jeu d'échecs entier
|
||||
|
||||
###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
|
||||
|
||||
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
|
||||
|
||||
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
|
|
@ -5,3 +5,9 @@ from guiTk import FenetreTk
|
|||
if __name__ == '__main__':
|
||||
# p = PlateauTk(LogiqueEchecs())
|
||||
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
|
||||
|
|
|
@ -2,35 +2,41 @@ from tkinter import *
|
|||
from logique import LogiqueEchecs
|
||||
|
||||
class PlateauTk:
|
||||
"""
|
||||
Plateau de jeu de damier en canvas Tk
|
||||
"""
|
||||
|
||||
RATIO_PCE_CASE = .95
|
||||
TEMPS_ANIM = 200
|
||||
INTER_ANIM = 10
|
||||
RATIO_PCE_CASE = .95 # Place que prend la pièce sur la case
|
||||
TEMPS_ANIM = 200 # Temps en ms d'une animation
|
||||
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):
|
||||
"""
|
||||
Constructeur de PlateauTk
|
||||
"""
|
||||
|
||||
self.can = can
|
||||
self.fen = fen
|
||||
self.statut = statut
|
||||
self.chaine = None
|
||||
self.grilleDamier = []
|
||||
self.imagesOriginales = {}
|
||||
self.imagesRedim = {}
|
||||
self.photos = []
|
||||
self.grillePions = []
|
||||
self.animations = []
|
||||
self.can = can # Canvas
|
||||
self.fen = fen # Fenêtre Tk (nécessaire pour lancer un timeout pour l'animation)
|
||||
self.statut = statut # Fonction qui envoie un message à l'utilisateur
|
||||
self.grilleDamier = [] # Tableau contenant les références aux cases du damier
|
||||
self.grillePions = [] # Tableau contenant les références aux pièces
|
||||
self.photos = [] # Liste contenant les différentes photos utilisées (afin qu'elles ne soient pas recyclées)
|
||||
self.imagesOriginales = {} # Dictionnaire associant chaque pièce à son image originale
|
||||
self.imagesRedim = {} # Dictionnaire associant chaque pièce à son image redimensionnée
|
||||
self.animations = [] # Liste contenant les animations en cours
|
||||
|
||||
self.dEtape = True
|
||||
self.dx1 = -1
|
||||
self.dy1 = -1
|
||||
self.dx2 = -1
|
||||
self.dy2 = -1
|
||||
self.mvtsPossibles = []
|
||||
self.logique = logique
|
||||
self.dEtape = True # Étape du déplacement (True : prendre la pièce, False : poser la pièce)
|
||||
self.dx1 = -1 # Coordonnées X de la pièce sélectionnée
|
||||
self.dy1 = -1 # Coordonnées Y de la pièce sélectionnée
|
||||
self.dx2 = -1 # Coordonnées X de la destination
|
||||
self.dy2 = -1 # Coordonnées Y de la destination
|
||||
self.mvtsPossibles = [] # Liste des mouvements possibles pour la pièce sélectionnée
|
||||
self.coteCase = 0 # Coté d'une case
|
||||
|
||||
self.coteCase = 0
|
||||
self.logique = logique # Logique du jeu
|
||||
|
||||
|
||||
self.importerImages()
|
||||
self.redimCan(self.COTE_TOUT_DEFAUT)
|
||||
|
@ -39,6 +45,9 @@ class PlateauTk:
|
|||
self.statutPrendre()
|
||||
|
||||
def redimCan(self, cote):
|
||||
"""
|
||||
Redimensionne le plateau de jeu
|
||||
"""
|
||||
self.can.delete(ALL)
|
||||
self.coteCase = cote // self.logique.CASES_COTE
|
||||
cote = self.logique.CASES_COTE * self.coteCase
|
||||
|
@ -52,6 +61,9 @@ class PlateauTk:
|
|||
|
||||
|
||||
def nomPiece(self, piece):
|
||||
"""
|
||||
Renvoi le nom de fichier de la pièce donnée
|
||||
"""
|
||||
tPiece = self.logique.tPiece(piece)
|
||||
if tPiece == self.logique.PCE_PION:
|
||||
return 'pion'
|
||||
|
@ -68,20 +80,32 @@ class PlateauTk:
|
|||
else:
|
||||
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'
|
||||
self.imagesOriginales.update({piece:PhotoImage(file=nom)})
|
||||
|
||||
def importerImages(self):
|
||||
"""
|
||||
Importe les images des pièces du jeu
|
||||
"""
|
||||
for piece in self.logique.BLANCS:
|
||||
self.importerImage(piece, 'B')
|
||||
for piece in self.logique.NOIRS:
|
||||
self.importerImage(piece, 'N')
|
||||
|
||||
def redimImage(self, piece, sample):
|
||||
"""
|
||||
Redimensionne l'image de la pièce donnée
|
||||
"""
|
||||
self.imagesRedim.update({piece:self.imagesOriginales[piece].subsample(sample)})
|
||||
|
||||
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
|
||||
for piece in self.logique.BLANCS:
|
||||
self.redimImage(piece, sample)
|
||||
|
@ -90,7 +114,10 @@ class PlateauTk:
|
|||
|
||||
# Dessin
|
||||
@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é
|
||||
return '#a0cefe' if blanc else '#478bd1'
|
||||
elif contexte == 2: # Possible / Victoire
|
||||
|
@ -101,21 +128,33 @@ class PlateauTk:
|
|||
return '#ffce9e' if blanc else '#d18b47'
|
||||
|
||||
def cCase(self, x, y):
|
||||
"""
|
||||
Crée la case aux coordonnées données
|
||||
"""
|
||||
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)
|
||||
|
||||
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)
|
||||
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 y in range(0, self.logique.CASES_COTE):
|
||||
self.coulCase(x, y, 0)
|
||||
self.coulCase(x, y, contexte)
|
||||
|
||||
def cDamier(self):
|
||||
"""
|
||||
Génère le damier
|
||||
"""
|
||||
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 = []
|
||||
for y in range(0, self.logique.CASES_COTE):
|
||||
colonne.append(self.cCase(x, y))
|
||||
|
@ -123,12 +162,18 @@ class PlateauTk:
|
|||
self.coulDamier()
|
||||
|
||||
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])
|
||||
else:
|
||||
self.grillePions[x][y] = False
|
||||
|
||||
def cGrille(self):
|
||||
"""
|
||||
Crée le tableau contenant les images de pièces
|
||||
"""
|
||||
self.grillePions = []
|
||||
for x in range(0, self.logique.CASES_COTE): # Crée self.grillePions
|
||||
colonne = []
|
||||
|
@ -137,12 +182,18 @@ class PlateauTk:
|
|||
self.grillePions.append(colonne)
|
||||
|
||||
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 y in range(0, self.logique.CASES_COTE):
|
||||
self.cPion(x, y, j_grilleF[x][y])
|
||||
|
||||
# Interaction
|
||||
def nomJoueur(self, joueur, pluriel=True):
|
||||
"""
|
||||
Retourne le nom du joueur donné
|
||||
"""
|
||||
if joueur == self.logique.BLANC:
|
||||
nom = 'blanc'
|
||||
elif joueur == self.logique.NOIR:
|
||||
|
@ -154,15 +205,25 @@ class PlateauTk:
|
|||
return nom
|
||||
|
||||
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
|
||||
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'])
|
||||
y = i['y1'] + (i['y2']-i['y1']) * (i['avancement']/i['total'])
|
||||
return [x, y]
|
||||
|
||||
def animation(self):
|
||||
"""
|
||||
Effectue les changements graphique nécessaire à la création d'une image d'animation.
|
||||
"""
|
||||
animationsNv = []
|
||||
for i in self.animations:
|
||||
if i['avancement'] < i['total']:
|
||||
|
@ -183,22 +244,29 @@ class PlateauTk:
|
|||
elif i['type'] == 'c':
|
||||
self.coulCase(i['x'], i['y'], 0)
|
||||
self.animations = animationsNv
|
||||
if len(animationsNv):
|
||||
self.fen.after(self.INTER_ANIM, self.animation)
|
||||
if len(animationsNv): # Si il y aura encore des animations à faire
|
||||
self.fen.after(self.INTER_ANIM, self.animation) # On prévoit une image (boucle)
|
||||
|
||||
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
|
||||
self.animations.append(animation)
|
||||
if etaitVide:
|
||||
self.animation()
|
||||
|
||||
def animerD(self, x1, y1, x2, y2, pion):
|
||||
"""
|
||||
Ajoute une animation pour un déplacment de pion
|
||||
"""
|
||||
if len(self.animations):
|
||||
for i in self.animations:
|
||||
if i['type'] == 'd' and 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['x1'] = coords[0] # TODO Simplifier avec update()
|
||||
i['y1'] = coords[1]
|
||||
i['x2'] = x2
|
||||
i['y2'] = y2
|
||||
|
@ -220,7 +288,10 @@ class PlateauTk:
|
|||
self.can.tag_raise(pion) # Mise au premier plan
|
||||
self.animer(animation)
|
||||
|
||||
def animerF(self, pion): # Pion fade-out
|
||||
def animerF(self, pion):
|
||||
"""
|
||||
Ajoute une animation pour la suppression d'un pion
|
||||
"""
|
||||
animation = {
|
||||
'pion': pion,
|
||||
'type': 'f',
|
||||
|
@ -230,6 +301,9 @@ class PlateauTk:
|
|||
self.animer(animation)
|
||||
|
||||
def animerC(self, x ,y):
|
||||
"""
|
||||
Ajoute une animation pour l'effacement de la surbrillance d'une case
|
||||
"""
|
||||
animation = {
|
||||
'type': 'c',
|
||||
'x': x,
|
||||
|
@ -240,18 +314,25 @@ class PlateauTk:
|
|||
self.animer(animation)
|
||||
|
||||
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.coulDamier()
|
||||
for x in range(0, self.logique.CASES_COTE):
|
||||
for y in range(0, self.logique.CASES_COTE):
|
||||
pion = self.logique.grille[x][y]
|
||||
if pion > 0:
|
||||
if self.logique.ePiece(pion):
|
||||
if self.logique.ePionNoir(pion) ^ self.logique.victorieux:
|
||||
self.coulCase(x, y, 2)
|
||||
else:
|
||||
self.coulCase(x, y, 3)
|
||||
|
||||
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)
|
||||
if test['valide'] == True: # Si déplacement possible
|
||||
for s in test['supprimer']:
|
||||
|
@ -268,6 +349,9 @@ class PlateauTk:
|
|||
return test['valide']
|
||||
|
||||
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 self.dEtape: # Prendre
|
||||
self.dx1, self.dy1 = x, y
|
||||
|
@ -296,26 +380,38 @@ class PlateauTk:
|
|||
self.animerC(self.dx2, self.dy2)
|
||||
|
||||
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):
|
||||
self.dClic(event.x // self.coteCase, event.y // self.coteCase)
|
||||
"""
|
||||
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)
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
Constructeur de FenetreTk
|
||||
"""
|
||||
|
||||
self.fen = None
|
||||
self.can = None
|
||||
self.chaine = ''
|
||||
self.fen = None # Objet Fenêtre
|
||||
self.can = None # Objet Canvas
|
||||
self.chaine = None # Objet Label de statut
|
||||
|
||||
self.plateau = None
|
||||
self.plateau = None # Plateau de jeu
|
||||
|
||||
self.creerFen()
|
||||
self.fen.mainloop()
|
||||
|
||||
def creerFen(self):
|
||||
"""
|
||||
Crée la fenêtre et ses éléments
|
||||
"""
|
||||
self.fen = Tk()
|
||||
self.fen.title("Jeu de plateau")
|
||||
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)
|
||||
|
||||
def statut(self, texte, delai=0):
|
||||
"""
|
||||
Change le message affiché.
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
Démarre une nouvelle partie.
|
||||
"""
|
||||
del self.plateau
|
||||
self.plateau = PlateauTk(self.fen, self.can, self.statut, LogiqueEchecs())
|
||||
|
|
|
@ -5,36 +5,50 @@ class Logique:
|
|||
Logique des jeux sur damier
|
||||
"""
|
||||
|
||||
PCE_VIDE = 0
|
||||
BLANC = True
|
||||
NOIR = False
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
def eCaseBlanche(xD, yD):
|
||||
return xD % 2 == yD % 2
|
||||
def eCaseBlanche(x, y):
|
||||
"""
|
||||
Indique si la case aux coordonnées indiquée est blanche.
|
||||
"""
|
||||
return x % 2 == y % 2
|
||||
|
||||
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 = []
|
||||
for y in range(self.CASES_COTE):
|
||||
colonne.append(self.PCE_VIDE)
|
||||
self.grille.append(colonne)
|
||||
|
||||
def cPion(self, x, y, piece):
|
||||
self.grille[x][y] = piece
|
||||
return True
|
||||
|
||||
def ePionBlanc(self, pion):
|
||||
"""
|
||||
Indique si la pièce est une pièce blanche.
|
||||
"""
|
||||
return pion in self.BLANCS
|
||||
|
||||
def ePionNoir(self, pion):
|
||||
"""
|
||||
Indique si la pièce est une pièce noire.
|
||||
"""
|
||||
return pion in self.NOIRS
|
||||
|
||||
def ePiece(self, piece):
|
||||
"""
|
||||
Indique si la pièce est une pièce.
|
||||
"""
|
||||
# return piece != self.PCE_VIDE
|
||||
return self.ePionBlanc(piece) or self.ePionNoir(piece)
|
||||
|
||||
def tPiece(self, piece):
|
||||
"""
|
||||
Retourne le type de pièce de la pièce donnée.
|
||||
"""
|
||||
if self.ePionBlanc(piece):
|
||||
return piece - self.DECALAGE_BLANCS
|
||||
elif self.ePionNoir(piece):
|
||||
|
@ -43,6 +57,9 @@ class Logique:
|
|||
return self.PCE_VIDE
|
||||
|
||||
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 \
|
||||
(self.ePionBlanc(pion) and self.joueur == self.BLANC)
|
||||
|
||||
|
@ -52,9 +69,9 @@ class LogiqueEchecs(Logique):
|
|||
Logique du jeu d'Échecs
|
||||
"""
|
||||
|
||||
CASES_COTE = 8
|
||||
CASES_COTE = 8 # Cases de coté
|
||||
|
||||
# Pièces
|
||||
# Type de pièces
|
||||
PCE_PION = 1
|
||||
PCE_TOUR = 2
|
||||
PCE_CAVALIER = 3
|
||||
|
@ -62,14 +79,14 @@ class LogiqueEchecs(Logique):
|
|||
PCE_DAME = 5
|
||||
PCE_ROI = 6
|
||||
|
||||
DECALAGE_BLANCS = 0
|
||||
DECALAGE_NOIRS = 10
|
||||
DECALAGE_BLANCS = 0 # Valeur à partir de laquelle sont compris les pièces blanches
|
||||
DECALAGE_NOIRS = 10 # Valeur à partir de laquelle sont compris les pièces noires
|
||||
|
||||
BLANCS = range(DECALAGE_BLANCS+PCE_PION, DECALAGE_BLANCS+PCE_ROI+1)
|
||||
NOIRS = range(DECALAGE_NOIRS+PCE_PION, DECALAGE_NOIRS+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) # Intervalle contenant les pièces noirs
|
||||
|
||||
|
||||
# Codes du mouvement
|
||||
# Codes d'erreur de mouvement
|
||||
MVT_INCONNU = 'Cause inconnue'
|
||||
MVT_OK = 'Valide'
|
||||
MVT_ROQUE = 'Roque'
|
||||
|
@ -82,15 +99,20 @@ class LogiqueEchecs(Logique):
|
|||
MVT_ECHEC = 'Échec au roi'
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.grille = []
|
||||
"""
|
||||
Constructeur de LogiqueEchecs
|
||||
"""
|
||||
self.grille = [] # Grille de jeu
|
||||
self.cGrille()
|
||||
self.remplirGrille()
|
||||
self.joueur = self.BLANC
|
||||
self.joueur = self.BLANC # Premier joueur
|
||||
self.partieFinie = False
|
||||
self.victorieux = None
|
||||
|
||||
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):
|
||||
|
@ -100,6 +122,9 @@ class LogiqueEchecs(Logique):
|
|||
self.grille[i][7] = self.DECALAGE_BLANCS + speciales[i]
|
||||
|
||||
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 self.joueur:
|
||||
if y2 == y1 - 1:
|
||||
|
@ -132,6 +157,9 @@ class LogiqueEchecs(Logique):
|
|||
return self.MVT_N_AUTORISE
|
||||
|
||||
def mvtPossibleSansEchecTour(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):
|
||||
|
@ -147,6 +175,9 @@ class LogiqueEchecs(Logique):
|
|||
return self.MVT_OK
|
||||
|
||||
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):
|
||||
sensX = (x2-x1)//abs(x2-x1)
|
||||
sensY = (y2-y1)//abs(y2-y1)
|
||||
|
@ -168,12 +199,18 @@ class LogiqueEchecs(Logique):
|
|||
return self.MVT_N_AUTORISE
|
||||
|
||||
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):
|
||||
return self.MVT_OK
|
||||
else:
|
||||
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]
|
||||
if self.aSonTour(pion):
|
||||
if (x1 != x2 or y1 != y2):
|
||||
|
@ -212,12 +249,18 @@ class LogiqueEchecs(Logique):
|
|||
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.mvtPossibleSansEchec(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.dPionSansEchec(x1, y1, x2, y2)
|
||||
mvtsPossiblesTousAdverses = []
|
||||
# On cherche la position du roi
|
||||
pionRoi = self.PCE_ROI + self.DECALAGE_BLANCS if self.joueur else self.DECALAGE_NOIRS
|
||||
roi = [-1, -1]
|
||||
for x in range(0, self.CASES_COTE):
|
||||
|
@ -225,7 +268,7 @@ class LogiqueEchecs(Logique):
|
|||
mvtsPossiblesTousAdverses += copie.mvtsPossiblesSansEchec(x, y)
|
||||
if copie.grille[x][y] == pionRoi:
|
||||
roi = [x, y]
|
||||
if roi in mvtsPossiblesTousAdverses:
|
||||
if roi in mvtsPossiblesTousAdverses: # Si le roi peut être sauté au tour adverse
|
||||
return self.MVT_ECHEC
|
||||
else:
|
||||
return test
|
||||
|
@ -233,6 +276,9 @@ class LogiqueEchecs(Logique):
|
|||
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):
|
||||
|
@ -241,6 +287,9 @@ class LogiqueEchecs(Logique):
|
|||
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):
|
||||
|
@ -249,6 +298,11 @@ class LogiqueEchecs(Logique):
|
|||
return tableau
|
||||
|
||||
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)
|
||||
if test == self.MVT_OK:
|
||||
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):
|
||||
"""
|
||||
Vérifie si le joueur actuel est en échec et mat et prend les mesures nécessiares.
|
||||
(Ici vrai en cas de "pat")
|
||||
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:
|
||||
|
@ -268,6 +324,10 @@ class LogiqueEchecs(Logique):
|
|||
return True
|
||||
|
||||
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)
|
||||
retour = {
|
||||
'valide': False,
|
||||
|
|
Reference in a new issue