Accueil > > > TOUR DE HANOI AVEC TKINTER
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 !
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
Commentaires et avis
|
Derniers Blogs
[WP7] AJOUTER DES IMAGES DANS LA MEDIA LIBRARY D'UN WINDOWS PHONE 7[WP7] AJOUTER DES IMAGES DANS LA MEDIA LIBRARY D'UN WINDOWS PHONE 7 par Audrey
L'émulateur Windows Phone 7, fourni avec la version Beta des outils développeurs n'inclut aucune image dans sa bibliothèque. Pas très pratique de tester son application lorsque l'on souhaite que l'utilisateur puisse choisir une image présente dans le télé...
Cliquez pour lire la suite de l'article par Audrey VIVE LES MOCKS ET LES POCOSVIVE LES MOCKS ET LES POCOS par vLabz
J'observe régulièrement autour de moi de la confusion à propos de ces deux termes et j'aimerais juste rappeler ce qu'ils signifient. Je ne suis bien sûr pas le mieux placé pour faire une leçon mais je vais faire de mon mieux pour mettre en valeur ce q...
Cliquez pour lire la suite de l'article par vLabz [WF4] WORKFLOW AND CUSTOM ACTIVITIES - BEST PRACTICES (4/5)[WF4] WORKFLOW AND CUSTOM ACTIVITIES - BEST PRACTICES (4/5) par JeremyJeanson
Vendredi dernier Microsoft a publié le quatrième épisode des bonnes pratiques pour coder ses activités custom dans WF4 : endpoint.tv - Workflow and Custom Activities - Best Practices (Part 4) . Tout comme pour les précédents épisodes, j'ai pris le temps d...
Cliquez pour lire la suite de l'article par JeremyJeanson DéVELOPPEMENT MOBILE : .NET COMPACT FRAMEWORK & LIMITATIONSDéVELOPPEMENT MOBILE : .NET COMPACT FRAMEWORK & LIMITATIONS par Pi-R
Introduction :
Le développement d'applications mobiles est quelque peu différent du développement d'applications sous Windows. En effet, le développement d'applications mobiles se base sur le .NET Compact Fra...
Cliquez pour lire la suite de l'article par Pi-R IPHONE VERSUS WP7 CODINGIPHONE VERSUS WP7 CODING par Nicolas
Je relais une présentation sur slideshare.net, qui compare le développement sur Iphone et Windows Phone 7, qui ma fait sourire. I phone versus windows phone 7 coding View more presentations from www.donburnett.com. J'aurais bien aimé une comparai...
Cliquez pour lire la suite de l'article par Nicolas
Logiciels
Xilisoft HD Vidéo Convertisseur 6 (6.0.3.0421)XILISOFT HD VIDéO CONVERTISSEUR 6 (6.0.3.0421)Xilisoft HD Vidéo Convertisseur est un outil professionnel de conversion HDTV, conçu pour transfo... Cliquez pour télécharger Xilisoft HD Vidéo Convertisseur 6 Xilisoft MP4 Convertisseur 6 (6.0.2.0415)XILISOFT MP4 CONVERTISSEUR 6 (6.0.2.0415)Xilisoft MP4 Convertisseur est un outil puissant pour la conversion de vidéo MP4, qui peut conver... Cliquez pour télécharger Xilisoft MP4 Convertisseur 6 Vade Retro Desktop (3.03)VADE RETRO DESKTOP (3.03)Le logiciel antispam Vade Retro pour Microsoft Outlook®, Outlook Express® et Windows Mail®(Vista)... Cliquez pour télécharger Vade Retro Desktop Malwarebytes Anti Malwares (1.46)MALWAREBYTES ANTI MALWARES (1.46)Malwarebytes' Anti-Malware est un anti-malware qui peut éliminer même les plus avancés des logic... Cliquez pour télécharger Malwarebytes Anti Malwares
|