[Echecs] Embellification

This commit is contained in:
Geoffrey Frogeye 2014-12-14 16:43:11 +01:00
parent 9c10044275
commit a38bf44a9b
4 changed files with 90 additions and 93 deletions

View file

@ -34,7 +34,11 @@ Le jeu de dame n'est pas implémenté, bien qu'il ne soit nécessaire de ne modi
* 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.
* Documenter et tester les fonctions
Chaque fonction possède une brève description de son fonctionnemenent. L'ajout des types des arguments serait inutile, étant la majorité du temps des int. Le contenu des arguments s'expliquent par leur nom, à défaut par la description de la fonction. De plus, la majorité des fonctions dépendant de l'instance de leur classe, aucune doctest n'a été écrite, car cela aurait nécessité trop de code pour de simples fonctions.
###Objectifs personnels
@ -46,4 +50,4 @@ Bien que le code fonctionne, soit plutôt flexible, relativement documenté et u
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
Les commentaires affublés d'une marque `TODO` signifient que le code pourrait être amélioré à cet endroit, ou une fonctionnalité pourrait y être ajoutée.

View file

@ -3,11 +3,4 @@
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

View file

@ -21,7 +21,7 @@ class PlateauTk:
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.grillePieces = [] # 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
@ -57,7 +57,7 @@ class PlateauTk:
self.redimImages()
self.cDamier()
self.cGrille()
self.remplirGrille(self.logique.grille)
self.remplirGrille()
def nomPiece(self, piece):
@ -161,33 +161,33 @@ class PlateauTk:
self.grilleDamier.append(colonne)
self.coulDamier()
def cPion(self, x, y, piece):
def cPiece(self, x, y, piece):
"""
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.grillePieces[x][y] = self.can.create_image((x + .5) * self.coteCase, (y + .5) * self.coteCase, image=self.imagesRedim[piece])
else:
self.grillePions[x][y] = False
self.grillePieces[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
self.grillePieces = []
for x in range(0, self.logique.CASES_COTE): # Crée self.grillePieces
colonne = []
for y in range(0, self.logique.CASES_COTE):
colonne.append(False)
self.grillePions.append(colonne)
self.grillePieces.append(colonne)
def remplirGrille(self, j_grilleF):
def remplirGrille(self):
"""
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.grillePieces
for y in range(0, self.logique.CASES_COTE):
self.cPion(x, y, j_grilleF[x][y])
self.cPiece(x, y, self.logique.grille[x][y])
# Interaction
def nomJoueur(self, joueur, pluriel=True):
@ -229,18 +229,18 @@ class PlateauTk:
if i['avancement'] < i['total']:
if i['type'] == 'd':
coords = self.animationDCoords(i)
self.can.coords(i['pion'], coords[0], coords[1])
self.can.coords(i['piece'], coords[0], coords[1])
# elif i['type'] == 'f':
# TODO Opacité de i['pion']
# TODO Opacité de i['piece']
# elif i['type'] == 'c':
# TODO Opacité de case
i['avancement'] += self.INTER_ANIM
animationsNv.append(i)
else:
if i['type'] == 'd':
self.can.coords(i['pion'], i['x2'], i['y2'])
self.can.coords(i['piece'], i['x2'], i['y2'])
elif i['type'] == 'f':
self.can.delete(i['pion'])
self.can.delete(i['piece'])
elif i['type'] == 'c':
self.coulCase(i['x'], i['y'], 0)
self.animations = animationsNv
@ -257,14 +257,14 @@ class PlateauTk:
if etaitVide:
self.animation()
def animerD(self, x1, y1, x2, y2, pion):
def animerD(self, x1, y1, x2, y2, piece):
"""
Ajoute une animation pour un déplacment de pion
Ajoute une animation pour un déplacment de piece
"""
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
if i['type'] == 'd' and i['piece'] == piece:
# Si une animation pour cette pièce existe déjà, on la reprend et on la modifie
coords = self.animationDCoords(i)
i['x1'] = coords[0] # TODO Simplifier avec update()
i['y1'] = coords[1]
@ -280,20 +280,20 @@ class PlateauTk:
'y1': y1,
'x2': x2,
'y2': y2,
'pion': pion,
'piece': piece,
'type': 'd',
'total': self.TEMPS_ANIM,
'avancement': 0
}
self.can.tag_raise(pion) # Mise au premier plan
self.can.tag_raise(piece) # Mise au premier plan
self.animer(animation)
def animerF(self, pion):
def animerF(self, piece):
"""
Ajoute une animation pour la suppression d'un pion
Ajoute une animation pour la suppression d'une pièce
"""
animation = {
'pion': pion,
'piece': piece,
'type': 'f',
'total': self.TEMPS_ANIM,
'avancement': 0
@ -321,31 +321,31 @@ class PlateauTk:
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 self.logique.ePiece(pion):
if self.logique.ePionNoir(pion) ^ self.logique.victorieux:
piece = self.logique.grille[x][y]
if self.logique.ePiece(piece):
if self.logique.ePieceNoir(piece) ^ self.logique.victorieux:
self.coulCase(x, y, 2)
else:
self.coulCase(x, y, 3)
def dPion(self, x1, y1, x2, y2):
def dPiece(self, x1, y1, x2, y2):
"""
Déplace le pion aux coordonnées 1 aux coordonnées 2.
Déplace la pièce 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.dPiece(x1, y1, x2, y2)
if test['valide'] == True: # Si déplacement possible
for s in test['supprimer']:
self.animerF(self.grillePions[s[0]][s[1]])
self.animerF(self.grillePieces[s[0]][s[1]])
for d in test['deplacer']:
self.grillePions[d[2]][d[3]], self.grillePions[d[0]][d[1]] = \
self.grillePions[d[0]][d[1]], False
self.grillePieces[d[2]][d[3]], self.grillePieces[d[0]][d[1]] = \
self.grillePieces[d[0]][d[1]], False
self.animerD((d[0] + .5) * self.coteCase, (d[1] + .5) * self.coteCase, \
(d[2] + .5) * self.coteCase, (d[3] + .5) * self.coteCase, \
self.grillePions[d[2]][d[3]])
self.grillePieces[d[2]][d[3]])
else:
self.statut('Déplacment impossible ! (' + test['message'] + ')')
self.statut('Déplacment invalide ! (' + test['message'] + ')')
return test['valide']
def dClic(self, x, y):
@ -361,14 +361,14 @@ class PlateauTk:
self.mvtPossibleSansEchecs = self.logique.mvtsPossibles(self.dx1, self.dy1) # Surbrillance bleue
for i in self.mvtPossibleSansEchecs: # Surbrillances vertes
self.coulCase(i[0], i[1], 2)
self.statut('Poser')
self.statut('Cliquez où déposer la pièce.')
self.dEtape = not self.dEtape
else: # Si pas possible de 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
if self.dPiece(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
if self.logique.partieFinie:

View file

@ -26,42 +26,42 @@ class Logique:
colonne.append(self.PCE_VIDE)
self.grille.append(colonne)
def ePionBlanc(self, pion):
def ePieceBlanc(self, piece):
"""
Indique si la pièce est une pièce blanche.
"""
return pion in self.BLANCS
return piece in self.BLANCS
def ePionNoir(self, pion):
def ePieceNoir(self, piece):
"""
Indique si la pièce est une pièce noire.
"""
return pion in self.NOIRS
return piece 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)
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.ePionBlanc(piece):
if self.ePieceBlanc(piece):
return piece - self.DECALAGE_BLANCS
elif self.ePionNoir(piece):
elif self.ePieceNoir(piece):
return piece - self.DECALAGE_NOIRS
else:
return self.PCE_VIDE
def aSonTour(self, pion):
def aSonTour(self, piece):
"""
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)
return (self.ePieceNoir(piece) and self.joueur == self.NOIR) or \
(self.ePieceBlanc(piece) and self.joueur == self.BLANC)
class LogiqueEchecs(Logique):
@ -93,9 +93,9 @@ class LogiqueEchecs(Logique):
MVT_SELECTION = 'Mauvais tour'
MVT_SUR_PLACE = 'Immobile'
MVT_SAUT_AMI = 'Saut ami'
MVT_PION_INC = 'Pion inconnu'
MVT_PIECE_INC = 'Pièce inconnu'
MVT_N_AUTORISE = 'Non-autorisé'
MVT_OBSTRUCTION = 'Pion en chemin'
MVT_OBSTRUCTION = 'Pièce en chemin'
MVT_ECHEC = 'Échec au roi'
def __init__(self):
@ -121,7 +121,7 @@ class LogiqueEchecs(Logique):
self.grille[i][6] = self.DECALAGE_BLANCS + self.PCE_PION
self.grille[i][7] = self.DECALAGE_BLANCS + speciales[i]
def mvtPossibleSansEchecPion(self, x1, y1, x2, y2):
def deplPossiblePion(self, x1, y1, x2, y2):
"""
Vérifie si le déplacement est possible pour un pion.
"""
@ -143,20 +143,20 @@ class LogiqueEchecs(Logique):
elif abs(x1-x2) == 1: # Saut
if self.joueur:
if y2 == y1 - 1 and \
self.ePionNoir(self.grille[x2][y2]):
self.ePieceNoir(self.grille[x2][y2]):
return self.MVT_OK
else:
return self.MVT_N_AUTORISE
else:
if y2 == y1 + 1 and \
self.ePionBlanc(self.grille[x2][y2]):
self.ePieceBlanc(self.grille[x2][y2]):
return self.MVT_OK
else:
return self.MVT_N_AUTORISE
else:
return self.MVT_N_AUTORISE
def mvtPossibleSansEchecTour(self, x1, y1, x2, y2):
def deplPossibleTour(self, x1, y1, x2, y2):
"""
Vérifie si le déplacement est possible pour une tour.
"""
@ -174,7 +174,7 @@ class LogiqueEchecs(Logique):
return self.MVT_N_AUTORISE
return self.MVT_OK
def mvtPossibleSansEchecFou(self, x1, y1, x2, y2):
def deplPossibleFou(self, x1, y1, x2, y2):
"""
Vérifie si le déplacement est possible pour un fou.
"""
@ -198,7 +198,7 @@ class LogiqueEchecs(Logique):
else:
return self.MVT_N_AUTORISE
def mvtPossibleSansEchecCavalier(self, x1, y1, x2, y2):
def deplPossibleCavalier(self, x1, y1, x2, y2):
"""
Vérifie si le déplacement est possible pour un cavalier.
"""
@ -207,39 +207,39 @@ class LogiqueEchecs(Logique):
else:
return self.MVT_N_AUTORISE
def mvtPossibleSansEchec(self, x1, y1, x2, y2): # TODO Utiliser la gestion d'erreurs ?
def deplPossible(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):
piece = self.grille[x1][y1]
if self.aSonTour(piece):
if (x1 != x2 or y1 != y2):
if not self.aSonTour(self.grille[x2][y2]):
tPion = self.tPiece(pion)
if tPion == self.PCE_PION: # Pion
return self.mvtPossibleSansEchecPion(x1, y1, x2, y2)
elif tPion == self.PCE_TOUR: # Tour
return self.mvtPossibleSansEchecTour(x1, y1, x2, y2)
elif tPion == self.PCE_CAVALIER:
return self.mvtPossibleSansEchecCavalier(x1, y1, x2, y2)
elif tPion == self.PCE_FOU:
return self.mvtPossibleSansEchecFou(x1, y1, x2, y2)
elif tPion == self.PCE_DAME:
tour = self.mvtPossibleSansEchecTour(x1, y1, x2, y2)
fou = self.mvtPossibleSansEchecFou(x1, y1, 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 tPion == self.PCE_ROI:
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_PION_INC
return self.MVT_PIECE_INC
else:
return self.MVT_SAUT_AMI
else:
@ -254,19 +254,19 @@ class LogiqueEchecs(Logique):
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.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.dPionSansEchec(x1, y1, x2, y2)
copie.dPieceSansEchec(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
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] == pionRoi:
if copie.grille[x][y] == pieceRoi:
roi = [x, y]
if roi in mvtsPossiblesTousAdverses: # Si le roi peut être sauté au tour adverse
return self.MVT_ECHEC
@ -282,7 +282,7 @@ class LogiqueEchecs(Logique):
tableau = []
for x2 in range(0, self.CASES_COTE):
for y2 in range(0, self.CASES_COTE):
if self.mvtPossibleSansEchec(x1, y1, x2, y2) == self.MVT_OK:
if self.deplPossible(x1, y1, x2, y2) == self.MVT_OK:
tableau.append([x2, y2])
return tableau
@ -297,13 +297,13 @@ class LogiqueEchecs(Logique):
tableau.append([x2, y2])
return tableau
def dPionSansEchec(self, x1, y1, x2, y2):
def dPieceSansEchec(self, x1, y1, x2, y2):
"""
Déplace le pion aux coordonnées 1 données aux coordonnées 2 données.
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.mvtPossibleSansEchec(x1, y1, x2, y2)
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
@ -323,17 +323,17 @@ class LogiqueEchecs(Logique):
self.victorieux = not self.joueur
return True
def dPion(self, x1, y1, x2, y2):
def dPiece(self, x1, y1, x2, y2):
"""
Déplace le pion aux coordonnées 1 données aux coordonnées 2 données.
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': [], # Pions à déplacer
'supprimer': [], # Pions à supprimer
'deplacer': [], # Pièces à déplacer
'supprimer': [], # Pièces à supprimer
}
if test == self.MVT_OK:
retour['valide'] = True