This repository has been archived on 2019-08-09. You can view files and clone it, but cannot push or open issues or pull requests.
s1-tp/S1/Echecs/echecs.py

565 lines
20 KiB
Python
Executable file

import copy
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'
MVT_ECHEC = 'Échec au roi'
class LogiqueEchecs:
def __init__(self):
self.grille = []
self.cGrille()
self.remplirGrille()
self.joueur = True
self.partieFinie = False
self.victorieux = None
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]
self.grille = [[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 4, 0, 0, 0, 0], [0, 2, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 6], [0, 0, 0, 3, 0, 0, 0, 0], [16, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]]
self.joueur = False
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 mvtPossibleSansEchecPion(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 mvtPossibleSansEchecTour(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 mvtPossibleSansEchecFou(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 mvtPossibleSansEchecCavalier(self, x1, y1, x2, y2):
if (abs(x2-x1) == 2 and abs(y2-y1) == 1) or (abs(y2-y1) == 2 and abs(x2-x1) == 1):
return MVT_OK
else:
return MVT_N_AUTORISE
def mvtPossibleSansEchec(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.mvtPossibleSansEchecPion(x1, y1, x2, y2)
elif tPion == 2: # Tour
return self.mvtPossibleSansEchecTour(x1, y1, x2, y2)
elif tPion == 3: # Cavalier
return self.mvtPossibleSansEchecCavalier(x1, y1, x2, y2)
elif tPion == 4: # Fou
return self.mvtPossibleSansEchecFou(x1, y1, x2, y2)
elif tPion == 5: # Dame
tour = self.mvtPossibleSansEchecTour(x1, y1, x2, y2)
fou = self.mvtPossibleSansEchecFou(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 mvtPossible(self, x1, y1, x2, y2):
test = self.mvtPossibleSansEchec(x1, y1, x2, y2)
if test == 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 = []
pionRoi = 6
if not self.joueur:
pionRoi += 10
roi = [-1, -1]
for x in range(0, CASES_COTE):
for y in range(0, CASES_COTE):
mvtsPossiblesTousAdverses += copie.mvtsPossiblesSansEchec(x, y)
if copie.grille[x][y] == pionRoi:
roi = [x, y]
if roi in mvtsPossiblesTousAdverses:
return MVT_ECHEC
else:
return test
else:
return test
def mvtsPossiblesSansEchec(self, x1, y1):
tableau = []
for x2 in range(0, CASES_COTE):
for y2 in range(0, CASES_COTE):
if self.mvtPossibleSansEchec(x1, y1, x2, y2) == MVT_OK:
tableau.append([x2, y2])
return tableau
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 dPionSansEchec(self, x1, y1, x2, y2):
test = self.mvtPossibleSansEchec(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
def vEchecMat(self):
"""
Vérifie si le joueur actuel est en échec et mat et prend les mesures nécessiares.
(CÀD Le joueur actuel ne peut effectuer aucun mouvement)
"""
for x in range(0, CASES_COTE):
for y in range(0, CASES_COTE):
if len(self.mvtsPossibles(x, y)) > 0:
print(x, y, self.mvtsPossibles(x, y))
return False
self.partieFinie = True
self.victorieux = not self.joueur
return True
def dPion(self, x1, y1, x2, y2):
test = self.mvtPossible(x1, y1, x2, y2)
retour = {
'valide': False,
'message': test,
'deplacer': [], # Pions à déplacer
'supprimer': [], # Pions à supprimer
'echecMat': False
}
if test == MVT_OK:
retour['valide'] = True
if self.grille[x2][y2] > 0:
retour['supprimer'].append([x2, y2])
retour['deplacer'].append([x1, y1, x2, y2])
self.grille[x1][y1], self.grille[x2][y2] = 0, self.grille[x1][y1]
self.joueur = not self.joueur
self.vEchecMat()
return retour
# 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('<Button-1>', 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
@staticmethod
def nomJoueur(joueur, pluriel=True):
if joueur:
nom = 'blanc'
else:
nom = 'noir'
if pluriel:
nom += 's'
return nom
def statutPrendre(self):
self.statut('Prendre (' + self.nomJoueur(self.logique.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 victoire(self):
self.statut('Victoire des ' + self.nomJoueur(self.logique.victorieux) + ' !')
self.coulDamier()
def dPion(self, x1, y1, x2, y2):
test = self.logique.dPion(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]])
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.animerD((d[0] + .5) * COTE_CASE, (d[1] + .5) * COTE_CASE, \
(d[2] + .5) * COTE_CASE, (d[3] + .5) * COTE_CASE, \
self.grillePions[d[2]][d[3]])
if test['echecMat']:
print('Win!')
else:
self.statut('Déplacment impossible ! (' + test['message'] + ')')
return test['valide']
def dClic(self, x, y):
if not self.logique.partieFinie:
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.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.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
if self.logique.partieFinie:
self.victoire()
else:
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)