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
TECHDAYS PARIS 2010 : LA BI DANS SHAREPOINT 2010TECHDAYS PARIS 2010 : LA BI DANS SHAREPOINT 2010 par ROMELARD Fabrice
Animé par: Vincent Bellet et Baptiste Giraudier La BI dans SharePoint 2010, Les nouveaux services d'application dans SP2010 et SQL Server Reporting services 2008 R2. La BI dans SharePoint est généralisée pour tous afin de permettre à tous les coll...
Cliquez pour lire la suite de l'article par ROMELARD Fabrice TECHDAYS PARIS 2010 : PLAN DE MIGRATION VERS SHAREPOINT 2010TECHDAYS PARIS 2010 : PLAN DE MIGRATION VERS SHAREPOINT 2010 par ROMELARD Fabrice
Animé par: Arnault Nouvel et Antoine Dongois Le processus à prendre : Apprendre (découvrir la plateforme) Préparer (documenter l'historique et choisir la méthode de MAJ) Test (Test de MAJ) Implémenter (Effectuer la MAJ) Valid...
Cliquez pour lire la suite de l'article par ROMELARD Fabrice TECHDAYS PARIS 2010 : LA PLEINIèRE DU SECOND JOURTECHDAYS PARIS 2010 : LA PLEINIèRE DU SECOND JOUR par ROMELARD Fabrice
Après un retour sur l'histoire des TechDays de Paris et le fait que ce soit le plus gros event MS au monde (du fait de sa gratuité), le président de MS France (Eric Boustoullier) a fait une présentation de la vision Microsoft pour les années à venir...
Cliquez pour lire la suite de l'article par ROMELARD Fabrice
Logiciels
DB-MAIN (9.1.0)DB-MAIN (9.1.0)DB-MAIN is a data-modeling and data-architecture tool. It is designed to help developers and anal... Cliquez pour télécharger DB-MAIN Xilisoft DPG Convertisseur (5.1.37.0120)XILISOFT DPG CONVERTISSEUR (5.1.37.0120)Xilisoft DPG Convertisseur offre aux fans de Nintendo DS une bonne solution leur permettant de dé... Cliquez pour télécharger Xilisoft DPG Convertisseur GraphicsGale (2.01.01)GRAPHICSGALE (2.01.01)GraphicsGale est un logiciel de PixelArt avec de nombreuse fonctionnalités permettant de réalisé ... Cliquez pour télécharger GraphicsGale Architecte 3D (Platinum 2010)ARCHITECTE 3D (PLATINUM 2010)Architecte 3D Platinium vous permet de concevoir facilement les plans votre future maison, de l'é... Cliquez pour télécharger Architecte 3D TeamViewer 5 (TeamViewer 5)TEAMVIEWER 5 (TEAMVIEWER 5)Dépanner un ami,expliquer une manipulation devient un jeu d'enfant.
Prise en main d'un autre ord... Cliquez pour télécharger TeamViewer 5
|