|
Trouver une ressource
Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !
TOUR DE HANOI AVEC TKINTER
Information sur la source
Description
Un classique des classiques, mais je tenais à vous soumettre ce code pour me faire part des erreurs ou rectifications qu'il serait bon d'y apporter.
Source
- from Tkinter import *
- from boite_dialogTk import *
- from PIL import Image,ImageTk
-
- class Hanoi(Frame):
- def __init__(self):
- Frame.__init__(self,bg='ivory')
- self.master.resizable(0, 0)
- dim = '530x560+247+0'
- self.master.wm_geometry(newGeometry=dim)
- #différentes positions pour les boites de dialogue
- self.pos = ['+280+180','+140+180','+270+180']
- #--- Icône ---#
- try:
- f=open('icone_hanoi.ico','r')
- f.close()
- self.icone = 'icone_hanoi.ico'
-
- except:
- message = " L'icône de l'application est manquante !"
- boite = boite_dialogTk(self,title=' Erreur de chargement !',pos=self.pos[0],
- mess=message,buttons=['ok'],bg_f='orange',bg_l='grey',fg_l='red',
- bg_b='black',fg_b='ivory')
- boite.go()
- self.icone = None
-
- self.master.iconbitmap(self.icone)
- self.master.title("Tours de Hanoï")
- self.can = Canvas(self,width=520,height=400,bg='navy',relief="raised", borderwidth=3)
-
- #--- Image de fond ---#
- self.err_chargement = 0
- try:
- image = Image.open("fond.bmp")
- self.fond = ImageTk.PhotoImage(image)
- except:
- message = " L'image de fond 'fond.bmp' n'est pas présente \n\
- dans le répertoire courant de l'application ! \n\n\
- 'Ignorer' pour continuer sans l'image de fond \n\
- 'Quitter' pour quitter le programme\n"
-
- boite = boite_dialogTk(self,title=' Erreur de chargement !',icone=self.icone,
- pos=self.pos[0],mess=message,buttons=['Ignorer','Quitter'],
- bg_f='orange',bg_l='grey',fg_l='red',bg_b='black',fg_b='ivory')
- if(boite.go()):
- self.err_chargement = 0
- self.fond=""
- else:
- self.err_chargement = 1
- return
-
-
- ##------------------------------ L'interface --------------------------------------##
- #--- le cadre principal --- #
- cadre_interface = Frame(self,relief="ridge", borderwidth=3,bg='light green')
-
- #--- les sous-cadres ---#
- cadre_nbDisc = Frame(cadre_interface,bg='light blue',relief="ridge", borderwidth=3)
- cadre_vitesse = Frame(cadre_interface,bg='light green',relief="ridge", borderwidth=3)
- cadre_bouttons = Frame(cadre_interface,bg='light green',relief="ridge", borderwidth=3)
-
- #--- la règle nb disques ---#
- Label(cadre_nbDisc,text="Nombre de disques :",bg='light blue',
- font="Arial 11 bold").grid(row=1,column=1)
- self.nb_disques = IntVar()
- disques = Scale(cadre_nbDisc,from_=1,to=8,length=420,orient=HORIZONTAL,
- tickinterval=1,variable=self.nb_disques,command=self.initialisation,
- bg='light blue',
- activebackground='blue',
- troughcolor='ivory')
- disques.grid(row=2,column=1)
-
- #--- le choix de la vitesse ---#
- Label(cadre_vitesse,text="Vitesse de déplacement :",
- font="Arial 11 bold",bg='light green').grid(row=1,column=2)
- self.deux_vitesses = [('Lente',5),('Rapide',10)]
- self.vitesse = IntVar()
- self.vitesse.set(5)
- i=1
- for texte,vit in self.deux_vitesses:
- rad_bout = Radiobutton(cadre_vitesse,text=texte,variable=self.vitesse,
- bg='dark green',fg='ivory',selectcolor='maroon',
- activebackground='ivory',activeforeground='maroon',
- value=vit,indicatoron=0,font="Arial 13 bold")
- rad_bout.grid(row=2,column=i)
- i += 2
-
- #--- les 3 boutons de droite ---#
- Button(cadre_bouttons,text="Initialiser",
- command=self.initialisation,font="Arial 11 bold",bg='blue',fg='ivory',
- activebackground='ivory',activeforeground='blue').grid(row=1,column=1,padx=2
- ,pady=2, sticky="nsew")
- self.etat_but_sol = IntVar()
- self.sol_but = Checkbutton(cadre_bouttons,text='Solution',bg='red',fg='ivory',
- activebackground='ivory',activeforeground='red',
- selectcolor='red',
- variable=self.etat_but_sol,command=self.solution,
- indicatoron=0,font='Arial 11 bold')
- self.sol_but.grid(row=2,column=1,padx=2,pady=2,sticky="nsew")
-
- Button(cadre_bouttons,text="Quitter",bg='black',fg='ivory',
- activebackground='ivory',activeforeground='black',
- command=self.quitter,font="Arial 11 bold").grid(row=3,column=1,padx=2,pady=2,
- sticky="nsew")
- Button(cadre_bouttons,text="Règles",bg='dark violet',fg='ivory',
- activebackground='ivory',activeforeground='dark violet',
- command=self.regle,font="Arial 11 bold").grid(row=4,column=1,padx=2,pady=2,
- sticky="nsew")
- cadre_nbDisc.grid(row=1,column=1)
- cadre_vitesse.grid(row=2,column=1)
- cadre_bouttons.grid(row=1,column=2,rowspan=2)
-
- cadre_interface.pack()
- self.can.pack()
-
- #--- Création du canevas et des item 'texte'(coups mini, coups effectués)
- self.can.create_image(264,204,image=self.fond)
- mess = "Nombre minimum de mouvements : "
- self.coups_mini = self.can.create_text(250,340,text=mess,font="Arial 13 bold ",
- fill='light blue')
- self.coups = self.can.create_text(250,360,text="Nombre de déplacements effectués : "
- +str(0),font="Arial 13 bold ",fill='light green')
-
- #--- Tableau des 3 tours ---#
- self.tours = [[],[],[]] # chacune d'elle est un tab à 4 dim :
- t1 = [[],[],[],[]] # 1ere -> l'id de la tour
- t2 = [[],[],[],[]] # 2eme -> tab d'id des disques
- t3 = [[],[],[],[]] # 3eme -> id de la tige
- # 4eme -> tab des coord de la base
- # tn = [id tn,[idD1,idD2,...],id tige,[coord_tn_xmin,coord_tn_ymin,coord_tn_xmax,...]]
- self.tours[0] = t1
- self.tours[1] = t2
- self.tours[2] = t3
-
- #--- Les bases ---#
- long_base = 120
- t1[0] = self.can.create_rectangle(40,300,40+long_base,310,fill='gold')
- t2[0] = self.can.create_rectangle(200,300,200+long_base,310,fill='gold')
- t3[0] = self.can.create_rectangle(360,300,360+long_base,310,fill='gold')
- #--- Les tiges ---#
- t1[2] = self.can.create_rectangle(95,300,105,100,fill='gold')
- t2[2] = self.can.create_rectangle(255,300,265,100,fill='gold')
- t3[2] = self.can.create_rectangle(415,300,425,100,fill='gold')
- #--- Toutes les coordonnées relatives à chaque base ---#
- self.coord_t1 = self.can.coords(self.tours[0][0])
- self.coord_t1_xmin = self.coord_t1[0]
- self.coord_t1_ymin = self.coord_t1[1]
- self.coord_t1_xmax = self.coord_t1[2]
- self.coord_t1_ymax = self.coord_t1[3]
- self.coord_t1_mil = self.coord_t1_xmin+(self.coord_t1_xmax-self.coord_t1_xmin)/2
- self.tours[0][3] = [self.coord_t1_xmin,self.coord_t1_ymin,
- self.coord_t1_xmax,self.coord_t1_ymax,self.coord_t1_mil]
-
- self.coord_t2 = self.can.coords(self.tours[1][0])
- self.coord_t2_xmin = self.coord_t2[0]
- self.coord_t2_ymin = self.coord_t2[1]
- self.coord_t2_xmax = self.coord_t2[2]
- self.coord_t2_ymax = self.coord_t2[3]
- self.coord_t2_mil = self.coord_t2_xmin+(self.coord_t2_xmax-self.coord_t2_xmin)/2
- self.tours[1][3] = [self.coord_t2_xmin,self.coord_t2_ymin,
- self.coord_t2_xmax,self.coord_t2_ymax,self.coord_t2_mil]
-
- self.coord_t3 = self.can.coords(self.tours[2][0])
- self.coord_t3_xmin = self.coord_t3[0]
- self.coord_t3_ymin = self.coord_t3[1]
- self.coord_t3_xmax = self.coord_t3[2]
- self.coord_t3_ymax = self.coord_t3[3]
- self.coord_t3_mil = self.coord_t3_xmin+(self.coord_t3_xmax-self.coord_t3_xmin)/2
- self.tours[2][3] = [self.coord_t3_xmin,self.coord_t3_ymin,
- self.coord_t3_xmax,self.coord_t3_ymax,self.coord_t3_mil]
-
- ##-----------------------------------------------------------------------------------##
- #--- Démarage ---#
- self.initialisation()
-
- ##---------------------- Init de toutes les variables ---------------------------------------##
- def initialisation(self,x=1):
-
- self.init = 1 # var qui permet de sortir de la boucle de mouvement si =1
- self.flag = 1 # var qui met l'animation en pause si le bouton règle est pressé
- for t in self.tours:
- for disque in t[1]:
- self.can.delete(disque) # effacement des disques du canevas
- t[1] = [] # et du tableau des tours
-
- self.nb_disc = self.nb_disques.get() # récupération du nombre de disques
- self.creer_disques(self.nb_disc) # création des disques
-
- self.vit = self.vitesse.get() # récupération de la vitesse de déplacement(5 ou 10 pix)
- self.dx,self.dy = 0,-self.vit # direction du déplacement
-
-
- self.tour_dep=[] # tableau qui contiendra la tour de depart selectionnée
- self.tour_arr=[] # pareil pour la tour d'arrivée
-
- self.tab = [] # tableau qui contiendra les couples (tour dep,tour arr) pour la solution
- self.index = 0 # var pour se deplacer dans le ce tableau après chaque mouvement
- self.sol = 0 # var qui indique que l'on est(=1) ou pas(=0) en mode solution
-
- self.mouv = 0 # var qui contient le nombre de mouvements effectués
-
- self.can.bind('<Button-1>',self.selection_tour)
-
- self.mini_mouv = (2**self.nb_disc)-1 # nombre de mouvements minimum
- mess = "Nombre minimum de mouvements : %s"%(str(self.mini_mouv))
- self.can.itemconfigure(self.coups_mini,text=mess) # modif du Label
- self.can.itemconfigure(self.coups,text="Nombre de déplacements effectués : "+str(0))
-
- self.can.itemconfigure(self.tours[0][0],fill='gold')
- self.can.itemconfigure(self.tours[0][2],fill='gold')
- self.can.itemconfigure(self.tours[1][0],fill='gold')
- self.can.itemconfigure(self.tours[1][2],fill='gold')
- self.can.itemconfigure(self.tours[2][0],fill='gold')
- self.can.itemconfigure(self.tours[2][2],fill='gold')
-
- def creer_disques(self,nb):
- # Création de nb disques choisi par l'utilisateur
- for i in range(nb): # i compris entre 0 et nb-1
- coord_d_xmin = self.coord_t1_mil - (nb-i)*10 # i étant croissant les pos en xmin et
- coord_d_xmax = self.coord_t1_mil + (nb-i)*10 # xmax diminuent et sont multiple de 10
- coord_d_y = self.coord_t1_ymin - (i*20) # pos en y = i fois l'épaisseur d'un disque
- self.tours[0][1].append(self.can.create_rectangle(coord_d_xmin,coord_d_y
- ,coord_d_xmax,coord_d_y-20
- ,fill='maroon'))
-
- #---------- Départ de l'algorithme de solution ----------#
- #--- Phase 1 : Initialisation ---#
- def solution(self):
- self.initialisation() # Appel à l'initialisation générale
- self.init = 0
- self.sol = 1 # mode solution : après chaque pose de disque on reviendra sur la phase 3
- self.recursivite(self.nb_disc,0,1,2) # 0,1,2 -> les indexs des tours dans self.tours
- self.deplace_auto()
-
- #--- Phase 2 : Remplissage du tableau de couple (t_dep,t_arr) par récursivité ---#
- def recursivite(self,n,td,tt,ta):
- if n>0:
- self.recursivite(n-1,td,ta,tt)
- self.tab.append((td,ta))
- self.recursivite(n-1,tt,td,ta)
-
- #--- Phase 3 : Appel successif de la fonction mouvement avec chaque couple(t_dep,t_arr) ---#
- def deplace_auto(self):
- self.can.unbind('<Button-1>')
-
- couple = self.tab[self.index]
-
- self.tour_dep = self.tours[couple[0]]
- self.tour_arr = self.tours[couple[1]]
- self.vit = self.vitesse.get()
- self.deplacement()
- self.index += 1
- #-----------------------------------------------------#
-
- #--- Selection à la souris des tours de départ et d'arrivée ---#
- def selection_tour(self,event):
- x,y = event.x,event.y
- tour_selec = []
-
- if((x>self.coord_t1_xmin and x<self.coord_t1_xmax) and
- (y>100 and y<self.coord_t1_ymax)):
- tour_selec = self.tours[0]
-
- elif((x>self.coord_t2_xmin and x<self.coord_t2_xmax) and
- (y>100 and y<self.coord_t2_ymax)):
- tour_selec = self.tours[1]
-
- elif((x>self.coord_t3_xmin and x<self.coord_t3_xmax) and
- (y>100 and y<self.coord_t3_ymax)):
- tour_selec = self.tours[2]
- else:
- return
-
- if self.tour_dep == []:
- self.tour_dep = tour_selec
- self.can.itemconfigure(self.tour_dep[0],fill='red')
- self.can.itemconfigure(self.tour_dep[2],fill='red')
- if self.tour_dep[1] == []: # tour de départ vide : violation d'une règle
- message = " Cette tour de départ ne contient aucun disque ! \n"
- boite = boite_dialogTk(self,title='Tours de Hanoï',icone=self.icone,
- pos=self.pos[2],mess=message,buttons=['ok'],bg_f='orange',
- bg_l='pink',bg_b='black',fg_b='ivory')
- boite.go()
- self.can.itemconfigure(self.tour_dep[0],fill='gold')
- self.can.itemconfigure(self.tour_dep[2],fill='gold')
- self.tour_dep = []
-
- elif self.tour_dep != []:
- self.tour_arr = tour_selec
- self.can.itemconfigure(self.tour_arr[0],fill='green')
- self.can.itemconfigure(self.tour_arr[2],fill='green')
- if self.tour_arr == self.tour_dep:
- self.can.itemconfigure(self.tour_dep[0],fill='gold')
- self.can.itemconfigure(self.tour_dep[2],fill='gold')
- self.tour_dep=[]
- self.tour_arr = []
- if self.tour_dep != [] and self.tour_arr != []:
- self.verif()
-
- #--- Vérification du respect de la règle pas de disque sur un plus petit ---#
- def verif(self):
- self.init = 0
-
- if self.tour_arr[1] != []:
- coord_d_dep = self.can.coords(self.tour_dep[1][-1])
- diam_d_dep = coord_d_dep[2] - coord_d_dep[0]
-
- coord_d_arr = self.can.coords(self.tour_arr[1][-1])
- diam_d_arr = coord_d_arr[2] - coord_d_arr[0]
-
- if diam_d_arr < diam_d_dep:
-
- message = " Attention pas de disque sur un plus petit que lui ! \n"
- boite = boite_dialogTk(self,title="Violation de la règle 2",icone=self.icone,
- pos=self.pos[2],mess=message,buttons=['ok'],bg_f='orange',
- bg_l='pink',bg_b='black',fg_b='ivory')
-
- if(boite.go()):
- self.can.itemconfigure(self.tour_dep[0],fill='gold')
- self.can.itemconfigure(self.tour_dep[2],fill='gold')
- self.can.itemconfigure(self.tour_arr[0],fill='gold')
- self.can.itemconfigure(self.tour_arr[2],fill='gold')
- self.tour_dep = []
- self.tour_arr = []
- return
-
- self.vit = self.vitesse.get()
- self.deplacement()
-
- #--- Mouvement d'un disque d'une tour à une autre ---#
- def deplacement(self):
-
- # Si appui sur 'initialisation' ou désélection du bonton 'solution'
- if self.init == 1 or (self.etat_but_sol.get()==0 and self.sol ==1):
- self.sol_but.deselect()
- self.initialisation()
- return
- self.can.unbind('<Button-1>')
-
- # Présence ou non d'un disque sur la tour d'arrivée par defaut : non
- disc_present = 0
-
- # Coord du disque qui se deplace et de son milieu
- coord_d = self.can.coords(self.tour_dep[1][-1])
- coord_d_xmin = coord_d[0]
- coord_d_xmax = coord_d[2]
- coord_d_ymin = coord_d[1]
- coord_mil_d = coord_d_xmin+(coord_d_xmax-coord_d_xmin)/2
-
- # Coord du dernier disque de la tour d'arrivé si il existe
- if self.tour_arr[1] != []:
- disc_present = 1
- coord_d_arr = self.can.coords(self.tour_arr[1][-1])
- coord_d_arr_ymin = coord_d_arr[1]
-
- # Si le disque est en haut
- if coord_d_ymin == 60:
- self.dy=0
- # le signe de la différence des coord en xmin des tours donne le sens de déplacement
- # Déplacement sur la droite
- if self.tour_arr[3][0] - self.tour_dep[3][0] > 0:
- self.dx = self.vit
- # Ou déplacement sur la gauche
- else:
- self.dx = -self.vit
-
- # Si les milieux du disque et de la tour d'arrivée correspondent
- if coord_mil_d == self.tour_arr[3][4]:
- self.dx = 0
- self.dy = self.vit
- # Descente du disque
- # jusq'au dernier de la tour d'arrivée
- if disc_present == 0:
- if coord_d_ymin+20 == self.tour_arr[3][1]:
- self.disque_pose() # Que faire quand le disque est posé
- return
- # sinon jusqu'à la base
- elif disc_present == 1:
- if coord_d_ymin+20 == coord_d_arr_ymin:
- self.disque_pose() # Que faire quand le disque est posé
- return
-
- self.can.move(self.tour_dep[1][-1],self.dx,self.dy)
- if self.flag == 1:
- self.master.after(self.vit,self.deplacement)
-
- #--- Actions à réaliser quand un disque est posé ---#
- def disque_pose(self):
- self.dy=0
-
- self.can.itemconfigure(self.tour_dep[0],fill='gold')
- self.can.itemconfigure(self.tour_dep[2],fill='gold')
- self.can.itemconfigure(self.tour_arr[0],fill='gold')
- self.can.itemconfigure(self.tour_arr[2],fill='gold')
-
- # Le disque déplacé est ajouté au tableau des disques de la tour d'arrivée
- self.tour_arr[1].append(self.tour_dep[1][-1])
- # Et supprimé du tableau de la tour de départ
- del(self.tour_dep[1][-1])
-
- # Quelques réinitialisations
- self.tour_dep = []
- self.tour_arr = []
- self.dx,self.dy = 0,-self.vit
- self.can.bind('<Button-1>',self.selection_tour)
- self.vit = self.vitesse.get()
-
- # Mise à jour du label 'déplacements effectués'
- self.mouv +=1
- self.can.itemconfigure(self.coups,text="Nombre de déplacements effectués : " +str(self.mouv))
-
-
- # Si la tour doite est complète en mode 'souris'
- if len(self.tours[2][1]) == self.nb_disc and self.sol == 0:
- # si il y a trop de déplacements
- if self.mouv > self.mini_mouv:
- diff = self.mouv - self.mini_mouv
- message =" Bien mais il y a %s coups de trop \n\n\
- Voulez-vous rejouer ? \n"%(str(diff))
- # si le minimum de déplacement est respecté
- elif self.mouv == self.mini_mouv:
- message =" Gagné ! en %s coups \n\n Voulez-vous rejouer ? \n"%(str(self.mouv))
-
- boite1 = boite_dialogTk(self,title='Question ?',icone=self.icone,pos=self.pos[2],
- mess=message,buttons=['Oui','Non'],bg_f='orange',
- bg_l='gold',bg_b='maroon',fg_b='ivory')
- if(boite1.go()):
- self.initialisation()
- else:
- self.master.destroy()
- return
-
- # Si la tour doite est complète en mode 'solution'
- elif len(self.tours[2][1]) == self.nb_disc and self.sol==1:
-
- message = " Solution terminée\n %s coups minimum respectés \n\n\
- Voulez-vous rejouer ? \n"%(str(self.mouv))
- boite2 = boite_dialogTk(self,title='Question ?',icone=self.icone,pos=self.pos[2],
- mess=message,buttons=['Oui','Non'],bg_f='orange',
- bg_l='gold',bg_b='maroon',fg_b='ivory')
-
- if(boite2.go()):
- self.sol_but.deselect()
- self.initialisation()
- else:
- self.master.destroy()
- return
-
- return
-
- # Si la tour doite n'est pas complète en mode 'solution'
-
- if self.sol == 1:
- # Retour en phase 3
- self.deplace_auto()
-
- # Si la tour doite n'est pas complète en mode 'souris'
- # retour dans l'attente d'une sélection
- return
-
- #--- Arret de l'aplication ---#
- def quitter(self):
- self.master.destroy()
- return 0
-
- #--- Règles du jeux ---#
- def regle(self):
- message = "Règles du jeux :\n\nLe but est de déplacer tous les disques de la tour gauche vers la tour droite\n\
- et ce sans violer les règles suivantes :\n\n\
- 1°/ On ne peut déplacer qu'un disque à la fois\n\
- 2°/ Un disque ne peut se poser sur un disque plus petit que lui\n"
-
- self.flag = 0
- boite = boite_dialogTk(self,title='Tours de Hanoï',icone=self.icone,pos=self.pos[1],
- mess=message,buttons=['ok'],bg_f='orange',bg_l='gold',
- bg_b='maroon',fg_b='ivory',justify=LEFT)
- if(boite.go()):
- self.flag = 1
- if self.sol == 1:
- self.deplacement()
-
-
-
- if __name__=='__main__':
-
- app = Hanoi()
- if app.err_chargement == 1:
- app.master.destroy()
- elif app.err_chargement == 0:
- app.pack()
- app.mainloop()
-
-
-
-
from Tkinter import *
from boite_dialogTk import *
from PIL import Image,ImageTk
class Hanoi(Frame):
def __init__(self):
Frame.__init__(self,bg='ivory')
self.master.resizable(0, 0)
dim = '530x560+247+0'
self.master.wm_geometry(newGeometry=dim)
#différentes positions pour les boites de dialogue
self.pos = ['+280+180','+140+180','+270+180']
#--- Icône ---#
try:
f=open('icone_hanoi.ico','r')
f.close()
self.icone = 'icone_hanoi.ico'
except:
message = " L'icône de l'application est manquante !"
boite = boite_dialogTk(self,title=' Erreur de chargement !',pos=self.pos[0],
mess=message,buttons=['ok'],bg_f='orange',bg_l='grey',fg_l='red',
bg_b='black',fg_b='ivory')
boite.go()
self.icone = None
self.master.iconbitmap(self.icone)
self.master.title("Tours de Hanoï")
self.can = Canvas(self,width=520,height=400,bg='navy',relief="raised", borderwidth=3)
#--- Image de fond ---#
self.err_chargement = 0
try:
image = Image.open("fond.bmp")
self.fond = ImageTk.PhotoImage(image)
except:
message = " L'image de fond 'fond.bmp' n'est pas présente \n\
dans le répertoire courant de l'application ! \n\n\
'Ignorer' pour continuer sans l'image de fond \n\
'Quitter' pour quitter le programme\n"
boite = boite_dialogTk(self,title=' Erreur de chargement !',icone=self.icone,
pos=self.pos[0],mess=message,buttons=['Ignorer','Quitter'],
bg_f='orange',bg_l='grey',fg_l='red',bg_b='black',fg_b='ivory')
if(boite.go()):
self.err_chargement = 0
self.fond=""
else:
self.err_chargement = 1
return
##------------------------------ L'interface --------------------------------------##
#--- le cadre principal --- #
cadre_interface = Frame(self,relief="ridge", borderwidth=3,bg='light green')
#--- les sous-cadres ---#
cadre_nbDisc = Frame(cadre_interface,bg='light blue',relief="ridge", borderwidth=3)
cadre_vitesse = Frame(cadre_interface,bg='light green',relief="ridge", borderwidth=3)
cadre_bouttons = Frame(cadre_interface,bg='light green',relief="ridge", borderwidth=3)
#--- la règle nb disques ---#
Label(cadre_nbDisc,text="Nombre de disques :",bg='light blue',
font="Arial 11 bold").grid(row=1,column=1)
self.nb_disques = IntVar()
disques = Scale(cadre_nbDisc,from_=1,to=8,length=420,orient=HORIZONTAL,
tickinterval=1,variable=self.nb_disques,command=self.initialisation,
bg='light blue',
activebackground='blue',
troughcolor='ivory')
disques.grid(row=2,column=1)
#--- le choix de la vitesse ---#
Label(cadre_vitesse,text="Vitesse de déplacement :",
font="Arial 11 bold",bg='light green').grid(row=1,column=2)
self.deux_vitesses = [('Lente',5),('Rapide',10)]
self.vitesse = IntVar()
self.vitesse.set(5)
i=1
for texte,vit in self.deux_vitesses:
rad_bout = Radiobutton(cadre_vitesse,text=texte,variable=self.vitesse,
bg='dark green',fg='ivory',selectcolor='maroon',
activebackground='ivory',activeforeground='maroon',
value=vit,indicatoron=0,font="Arial 13 bold")
rad_bout.grid(row=2,column=i)
i += 2
#--- les 3 boutons de droite ---#
Button(cadre_bouttons,text="Initialiser",
command=self.initialisation,font="Arial 11 bold",bg='blue',fg='ivory',
activebackground='ivory',activeforeground='blue').grid(row=1,column=1,padx=2
,pady=2, sticky="nsew")
self.etat_but_sol = IntVar()
self.sol_but = Checkbutton(cadre_bouttons,text='Solution',bg='red',fg='ivory',
activebackground='ivory',activeforeground='red',
selectcolor='red',
variable=self.etat_but_sol,command=self.solution,
indicatoron=0,font='Arial 11 bold')
self.sol_but.grid(row=2,column=1,padx=2,pady=2,sticky="nsew")
Button(cadre_bouttons,text="Quitter",bg='black',fg='ivory',
activebackground='ivory',activeforeground='black',
command=self.quitter,font="Arial 11 bold").grid(row=3,column=1,padx=2,pady=2,
sticky="nsew")
Button(cadre_bouttons,text="Règles",bg='dark violet',fg='ivory',
activebackground='ivory',activeforeground='dark violet',
command=self.regle,font="Arial 11 bold").grid(row=4,column=1,padx=2,pady=2,
sticky="nsew")
cadre_nbDisc.grid(row=1,column=1)
cadre_vitesse.grid(row=2,column=1)
cadre_bouttons.grid(row=1,column=2,rowspan=2)
cadre_interface.pack()
self.can.pack()
#--- Création du canevas et des item 'texte'(coups mini, coups effectués)
self.can.create_image(264,204,image=self.fond)
mess = "Nombre minimum de mouvements : "
self.coups_mini = self.can.create_text(250,340,text=mess,font="Arial 13 bold ",
fill='light blue')
self.coups = self.can.create_text(250,360,text="Nombre de déplacements effectués : "
+str(0),font="Arial 13 bold ",fill='light green')
#--- Tableau des 3 tours ---#
self.tours = [[],[],[]] # chacune d'elle est un tab à 4 dim :
t1 = [[],[],[],[]] # 1ere -> l'id de la tour
t2 = [[],[],[],[]] # 2eme -> tab d'id des disques
t3 = [[],[],[],[]] # 3eme -> id de la tige
# 4eme -> tab des coord de la base
# tn = [id tn,[idD1,idD2,...],id tige,[coord_tn_xmin,coord_tn_ymin,coord_tn_xmax,...]]
self.tours[0] = t1
self.tours[1] = t2
self.tours[2] = t3
#--- Les bases ---#
long_base = 120
t1[0] = self.can.create_rectangle(40,300,40+long_base,310,fill='gold')
t2[0] = self.can.create_rectangle(200,300,200+long_base,310,fill='gold')
t3[0] = self.can.create_rectangle(360,300,360+long_base,310,fill='gold')
#--- Les tiges ---#
t1[2] = self.can.create_rectangle(95,300,105,100,fill='gold')
t2[2] = self.can.create_rectangle(255,300,265,100,fill='gold')
t3[2] = self.can.create_rectangle(415,300,425,100,fill='gold')
#--- Toutes les coordonnées relatives à chaque base ---#
self.coord_t1 = self.can.coords(self.tours[0][0])
self.coord_t1_xmin = self.coord_t1[0]
self.coord_t1_ymin = self.coord_t1[1]
self.coord_t1_xmax = self.coord_t1[2]
self.coord_t1_ymax = self.coord_t1[3]
self.coord_t1_mil = self.coord_t1_xmin+(self.coord_t1_xmax-self.coord_t1_xmin)/2
self.tours[0][3] = [self.coord_t1_xmin,self.coord_t1_ymin,
self.coord_t1_xmax,self.coord_t1_ymax,self.coord_t1_mil]
self.coord_t2 = self.can.coords(self.tours[1][0])
self.coord_t2_xmin = self.coord_t2[0]
self.coord_t2_ymin = self.coord_t2[1]
self.coord_t2_xmax = self.coord_t2[2]
self.coord_t2_ymax = self.coord_t2[3]
self.coord_t2_mil = self.coord_t2_xmin+(self.coord_t2_xmax-self.coord_t2_xmin)/2
self.tours[1][3] = [self.coord_t2_xmin,self.coord_t2_ymin,
self.coord_t2_xmax,self.coord_t2_ymax,self.coord_t2_mil]
self.coord_t3 = self.can.coords(self.tours[2][0])
self.coord_t3_xmin = self.coord_t3[0]
self.coord_t3_ymin = self.coord_t3[1]
self.coord_t3_xmax = self.coord_t3[2]
self.coord_t3_ymax = self.coord_t3[3]
self.coord_t3_mil = self.coord_t3_xmin+(self.coord_t3_xmax-self.coord_t3_xmin)/2
self.tours[2][3] = [self.coord_t3_xmin,self.coord_t3_ymin,
self.coord_t3_xmax,self.coord_t3_ymax,self.coord_t3_mil]
##-----------------------------------------------------------------------------------##
#--- Démarage ---#
self.initialisation()
##---------------------- Init de toutes les variables ---------------------------------------##
def initialisation(self,x=1):
self.init = 1 # var qui permet de sortir de la boucle de mouvement si =1
self.flag = 1 # var qui met l'animation en pause si le bouton règle est pressé
for t in self.tours:
for disque in t[1]:
self.can.delete(disque) # effacement des disques du canevas
t[1] = [] # et du tableau des tours
self.nb_disc = self.nb_disques.get() # récupération du nombre de disques
self.creer_disques(self.nb_disc) # création des disques
self.vit = self.vitesse.get() # récupération de la vitesse de déplacement(5 ou 10 pix)
self.dx,self.dy = 0,-self.vit # direction du déplacement
self.tour_dep=[] # tableau qui contiendra la tour de depart selectionnée
self.tour_arr=[] # pareil pour la tour d'arrivée
self.tab = [] # tableau qui contiendra les couples (tour dep,tour arr) pour la solution
self.index = 0 # var pour se deplacer dans le ce tableau après chaque mouvement
self.sol = 0 # var qui indique que l'on est(=1) ou pas(=0) en mode solution
self.mouv = 0 # var qui contient le nombre de mouvements effectués
self.can.bind('<Button-1>',self.selection_tour)
self.mini_mouv = (2**self.nb_disc)-1 # nombre de mouvements minimum
mess = "Nombre minimum de mouvements : %s"%(str(self.mini_mouv))
self.can.itemconfigure(self.coups_mini,text=mess) # modif du Label
self.can.itemconfigure(self.coups,text="Nombre de déplacements effectués : "+str(0))
self.can.itemconfigure(self.tours[0][0],fill='gold')
self.can.itemconfigure(self.tours[0][2],fill='gold')
self.can.itemconfigure(self.tours[1][0],fill='gold')
self.can.itemconfigure(self.tours[1][2],fill='gold')
self.can.itemconfigure(self.tours[2][0],fill='gold')
self.can.itemconfigure(self.tours[2][2],fill='gold')
def creer_disques(self,nb):
# Création de nb disques choisi par l'utilisateur
for i in range(nb): # i compris entre 0 et nb-1
coord_d_xmin = self.coord_t1_mil - (nb-i)*10 # i étant croissant les pos en xmin et
coord_d_xmax = self.coord_t1_mil + (nb-i)*10 # xmax diminuent et sont multiple de 10
coord_d_y = self.coord_t1_ymin - (i*20) # pos en y = i fois l'épaisseur d'un disque
self.tours[0][1].append(self.can.create_rectangle(coord_d_xmin,coord_d_y
,coord_d_xmax,coord_d_y-20
,fill='maroon'))
#---------- Départ de l'algorithme de solution ----------#
#--- Phase 1 : Initialisation ---#
def solution(self):
self.initialisation() # Appel à l'initialisation générale
self.init = 0
self.sol = 1 # mode solution : après chaque pose de disque on reviendra sur la phase 3
self.recursivite(self.nb_disc,0,1,2) # 0,1,2 -> les indexs des tours dans self.tours
self.deplace_auto()
#--- Phase 2 : Remplissage du tableau de couple (t_dep,t_arr) par récursivité ---#
def recursivite(self,n,td,tt,ta):
if n>0:
self.recursivite(n-1,td,ta,tt)
self.tab.append((td,ta))
self.recursivite(n-1,tt,td,ta)
#--- Phase 3 : Appel successif de la fonction mouvement avec chaque couple(t_dep,t_arr) ---#
def deplace_auto(self):
self.can.unbind('<Button-1>')
couple = self.tab[self.index]
self.tour_dep = self.tours[couple[0]]
self.tour_arr = self.tours[couple[1]]
self.vit = self.vitesse.get()
self.deplacement()
self.index += 1
#-----------------------------------------------------#
#--- Selection à la souris des tours de départ et d'arrivée ---#
def selection_tour(self,event):
x,y = event.x,event.y
tour_selec = []
if((x>self.coord_t1_xmin and x<self.coord_t1_xmax) and
(y>100 and y<self.coord_t1_ymax)):
tour_selec = self.tours[0]
elif((x>self.coord_t2_xmin and x<self.coord_t2_xmax) and
(y>100 and y<self.coord_t2_ymax)):
tour_selec = self.tours[1]
elif((x>self.coord_t3_xmin and x<self.coord_t3_xmax) and
(y>100 and y<self.coord_t3_ymax)):
tour_selec = self.tours[2]
else:
return
if self.tour_dep == []:
self.tour_dep = tour_selec
self.can.itemconfigure(self.tour_dep[0],fill='red')
self.can.itemconfigure(self.tour_dep[2],fill='red')
if self.tour_dep[1] == []: # tour de départ vide : violation d'une règle
message = " Cette tour de départ ne contient aucun disque ! \n"
boite = boite_dialogTk(self,title='Tours de Hanoï',icone=self.icone,
pos=self.pos[2],mess=message,buttons=['ok'],bg_f='orange',
bg_l='pink',bg_b='black',fg_b='ivory')
boite.go()
self.can.itemconfigure(self.tour_dep[0],fill='gold')
self.can.itemconfigure(self.tour_dep[2],fill='gold')
self.tour_dep = []
elif self.tour_dep != []:
self.tour_arr = tour_selec
self.can.itemconfigure(self.tour_arr[0],fill='green')
self.can.itemconfigure(self.tour_arr[2],fill='green')
if self.tour_arr == self.tour_dep:
self.can.itemconfigure(self.tour_dep[0],fill='gold')
self.can.itemconfigure(self.tour_dep[2],fill='gold')
self.tour_dep=[]
self.tour_arr = []
if self.tour_dep != [] and self.tour_arr != []:
self.verif()
#--- Vérification du respect de la règle pas de disque sur un plus petit ---#
def verif(self):
self.init = 0
if self.tour_arr[1] != []:
coord_d_dep = self.can.coords(self.tour_dep[1][-1])
diam_d_dep = coord_d_dep[2] - coord_d_dep[0]
coord_d_arr = self.can.coords(self.tour_arr[1][-1])
diam_d_arr = coord_d_arr[2] - coord_d_arr[0]
if diam_d_arr < diam_d_dep:
message = " Attention pas de disque sur un plus petit que lui ! \n"
boite = boite_dialogTk(self,title="Violation de la règle 2",icone=self.icone,
pos=self.pos[2],mess=message,buttons=['ok'],bg_f='orange',
bg_l='pink',bg_b='black',fg_b='ivory')
if(boite.go()):
self.can.itemconfigure(self.tour_dep[0],fill='gold')
self.can.itemconfigure(self.tour_dep[2],fill='gold')
self.can.itemconfigure(self.tour_arr[0],fill='gold')
self.can.itemconfigure(self.tour_arr[2],fill='gold')
self.tour_dep = []
self.tour_arr = []
return
self.vit = self.vitesse.get()
self.deplacement()
#--- Mouvement d'un disque d'une tour à une autre ---#
def deplacement(self):
# Si appui sur 'initialisation' ou désélection du bonton 'solution'
if self.init == 1 or (self.etat_but_sol.get()==0 and self.sol ==1):
self.sol_but.deselect()
self.initialisation()
return
self.can.unbind('<Button-1>')
# Présence ou non d'un disque sur la tour d'arrivée par defaut : non
disc_present = 0
# Coord du disque qui se deplace et de son milieu
coord_d = self.can.coords(self.tour_dep[1][-1])
coord_d_xmin = coord_d[0]
coord_d_xmax = coord_d[2]
coord_d_ymin = coord_d[1]
coord_mil_d = coord_d_xmin+(coord_d_xmax-coord_d_xmin)/2
# Coord du dernier disque de la tour d'arrivé si il existe
if self.tour_arr[1] != []:
disc_present = 1
coord_d_arr = self.can.coords(self.tour_arr[1][-1])
coord_d_arr_ymin = coord_d_arr[1]
# Si le disque est en haut
if coord_d_ymin == 60:
self.dy=0
# le signe de la différence des coord en xmin des tours donne le sens de déplacement
# Déplacement sur la droite
if self.tour_arr[3][0] - self.tour_dep[3][0] > 0:
self.dx = self.vit
# Ou déplacement sur la gauche
else:
self.dx = -self.vit
# Si les milieux du disque et de la tour d'arrivée correspondent
if coord_mil_d == self.tour_arr[3][4]:
self.dx = 0
self.dy = self.vit
# Descente du disque
# jusq'au dernier de la tour d'arrivée
if disc_present == 0:
if coord_d_ymin+20 == self.tour_arr[3][1]:
self.disque_pose() # Que faire quand le disque est posé
return
# sinon jusqu'à la base
elif disc_present == 1:
if coord_d_ymin+20 == coord_d_arr_ymin:
self.disque_pose() # Que faire quand le disque est posé
return
self.can.move(self.tour_dep[1][-1],self.dx,self.dy)
if self.flag == 1:
self.master.after(self.vit,self.deplacement)
#--- Actions à réaliser quand un disque est posé ---#
def disque_pose(self):
self.dy=0
self.can.itemconfigure(self.tour_dep[0],fill='gold')
self.can.itemconfigure(self.tour_dep[2],fill='gold')
self.can.itemconfigure(self.tour_arr[0],fill='gold')
self.can.itemconfigure(self.tour_arr[2],fill='gold')
# Le disque déplacé est ajouté au tableau des disques de la tour d'arrivée
self.tour_arr[1].append(self.tour_dep[1][-1])
# Et supprimé du tableau de la tour de départ
del(self.tour_dep[1][-1])
# Quelques réinitialisations
self.tour_dep = []
self.tour_arr = []
self.dx,self.dy = 0,-self.vit
self.can.bind('<Button-1>',self.selection_tour)
self.vit = self.vitesse.get()
# Mise à jour du label 'déplacements effectués'
self.mouv +=1
self.can.itemconfigure(self.coups,text="Nombre de déplacements effectués : " +str(self.mouv))
# Si la tour doite est complète en mode 'souris'
if len(self.tours[2][1]) == self.nb_disc and self.sol == 0:
# si il y a trop de déplacements
if self.mouv > self.mini_mouv:
diff = self.mouv - self.mini_mouv
message =" Bien mais il y a %s coups de trop \n\n\
Voulez-vous rejouer ? \n"%(str(diff))
# si le minimum de déplacement est respecté
elif self.mouv == self.mini_mouv:
message =" Gagné ! en %s coups \n\n Voulez-vous rejouer ? \n"%(str(self.mouv))
boite1 = boite_dialogTk(self,title='Question ?',icone=self.icone,pos=self.pos[2],
mess=message,buttons=['Oui','Non'],bg_f='orange',
bg_l='gold',bg_b='maroon',fg_b='ivory')
if(boite1.go()):
self.initialisation()
else:
self.master.destroy()
return
# Si la tour doite est complète en mode 'solution'
elif len(self.tours[2][1]) == self.nb_disc and self.sol==1:
message = " Solution terminée\n %s coups minimum respectés \n\n\
Voulez-vous rejouer ? \n"%(str(self.mouv))
boite2 = boite_dialogTk(self,title='Question ?',icone=self.icone,pos=self.pos[2],
mess=message,buttons=['Oui','Non'],bg_f='orange',
bg_l='gold',bg_b='maroon',fg_b='ivory')
if(boite2.go()):
self.sol_but.deselect()
self.initialisation()
else:
self.master.destroy()
return
return
# Si la tour doite n'est pas complète en mode 'solution'
if self.sol == 1:
# Retour en phase 3
self.deplace_auto()
# Si la tour doite n'est pas complète en mode 'souris'
# retour dans l'attente d'une sélection
return
#--- Arret de l'aplication ---#
def quitter(self):
self.master.destroy()
return 0
#--- Règles du jeux ---#
def regle(self):
message = "Règles du jeux :\n\nLe but est de déplacer tous les disques de la tour gauche vers la tour droite\n\
et ce sans violer les règles suivantes :\n\n\
1°/ On ne peut déplacer qu'un disque à la fois\n\
2°/ Un disque ne peut se poser sur un disque plus petit que lui\n"
self.flag = 0
boite = boite_dialogTk(self,title='Tours de Hanoï',icone=self.icone,pos=self.pos[1],
mess=message,buttons=['ok'],bg_f='orange',bg_l='gold',
bg_b='maroon',fg_b='ivory',justify=LEFT)
if(boite.go()):
self.flag = 1
if self.sol == 1:
self.deplacement()
if __name__=='__main__':
app = Hanoi()
if app.err_chargement == 1:
app.master.destroy()
elif app.err_chargement == 0:
app.pack()
app.mainloop()
Conclusion
Je tenais à remercier Aéra Group pour sa collaboration à faire évoluer cette application !
Fichier Zip
Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !
Télécharger le zip
Historique
- 15 mars 2007 20:43:45 :
- Rajout du fichier .zip contenant code+fond
- 15 mars 2007 20:45:20 :
- Rajout du fichier .zip contenant code+fond
- 17 mars 2007 22:57:25 :
- Rajout de titres de fenêtres et initialisation automatique avec la règle "nombre de disques"
- 23 mars 2007 19:08:27 :
- Addition d'un module de boites de dialogue personnalisables, rajout d'une îcone, et divers détails
- 23 mars 2007 19:13:41 :
- copier/coller du script pour les visiteurs (oublie oups !)
- 23 mars 2007 19:50:26 :
- Rectif dans le texte 'Règles'(rien d'important)
- 25 mars 2007 16:35:53 :
- Correction d'un bug lors de l'appui sur 'règles' en mode sélection.
- 26 avril 2007 12:03:10 :
- Modif de la classe boite_dialogTK pour éviter un bug lors de la fermeture par la 'croix'
- 28 avril 2007 18:41:21 :
- Encore une modif dans le script 'boite_dialogTk' qui rajoute le paramètre 'quit_opt' pour pouvoir choisir le comportement de la boite de dialogue lors de la fermeture par la 'croix'.
Sources de la même categorie
Sources en rapport avec celle ci
Commentaires et avis
|
Comparez les prix Nouvelle version
|