# -*- coding: utf-8 -*- """ Created on Sat Apr 25 21:18:50 2020 @author: cpan """ import tkinter as tk import time class Hanoi: def __init__(self, tkroot, num_disks=3, canvas_w=500, canvas_h=400): self.num_disks = num_disks self.canvas_w = canvas_w self.canvas_h = canvas_h self._gap = 2 self._speed = 0.001 # in seconds self._step = 2 self._pegs = [] self._pegstate = [[], [], []] self._tkroot = tkroot self._frame = tk.Frame(self._tkroot) self._lbl_disks = tk.Label(self._frame, text="No. of Disks: ") self._ent_disks = tk.Entry(self._frame, width=5) self._btn_start = tk.Button(self._frame, text="Start", width=6, command=self._cmd_start) self._btn_reset = tk.Button(self._frame, text="Reset", width=6, command=self._cmd_reset) self._lbl_speed = tk.Label(self._frame, text="Speed: ") self._var_speed = tk.StringVar(self._tkroot) self._opt_speed = tk.OptionMenu(self._frame, self._var_speed, *{'slow', 'normal', 'fastest'}) self._canvas = tk.Canvas(self._tkroot, width=canvas_w, height=canvas_h) self._init_tkwindow() def _init_tkwindow(self): self._tkroot.title('Hanoi Tower') self._lbl_disks.pack(side=tk.LEFT) self._ent_disks.insert(0, str(self.num_disks)) self._ent_disks.bind('', self._ent_return) self._ent_disks.bind('', self._ent_focus) self._ent_disks.pack(side=tk.LEFT, padx=5) self._btn_start.pack(side=tk.LEFT, padx=5) self._btn_reset.pack(side=tk.LEFT, padx=5) self._lbl_speed.pack(side=tk.LEFT) self._var_speed.set('normal') self._opt_speed.config(width=6, takefocus=True) self._opt_speed.pack(side=tk.LEFT) self._frame.pack(side=tk.TOP, pady=5) self._canvas.pack() self._btn_start.focus_set() self._var_speed.trace('w', self._var_speed_changed) self._tkroot.lift() self._tkroot.focus_force() def _var_speed_changed(self, *args): self._opt_speed.focus_set() speed_opt = {'slow': 0.001, 'normal': 0.001, 'fastest': 0} step_opt = {'slow': 1, 'normal': 2, 'fastest': 1} self._speed = speed_opt.get(self._var_speed.get()) self._step = step_opt.get(self._var_speed.get()) print(self._speed) def _cmd_start(self): self._btn_reset.focus_set() self._btn_start.config(state='disabled') def _cmd_reset(self): self._btn_start.focus_set() self._btn_start.config(state='normal') def _run_hanoi(self): pass def _ent_return(self): pass def _ent_focus(self): pass def _reset_hanoi(self): pass # gbl_num_disks = 3 # GAP = 2 # CANVAS_W = 500 # CANVAS_H = 400 # gbl_speed = 0.001 # in seconds # gbl_step = 2 # gbl_pegs = [] # gbl_pegstate = [[], [], []] # gbl_disks = [] # gbl_tkroot = tk.Tk() # gbl_tkroot.title('Hanoi Tower') # gbl_canvas = None # gbl_ready = True # def setup_gui(): # global gbl_canvas # def ent_return(event): # event.widget.tk_focusNext().focus() # def get_ent_focus(event): # global gbl_num_disks # try: # new_num = int(ent_disks.get()) # gbl_num_disks = new_num # draw_init_setting() # except Exception as err: # ent_disks.delete(0, tk.END) # ent_disks.insert(0, str(gbl_num_disks)) # print(repr(err)) # # print(gbl_num_disks) # # options row # tk_row = tk.Frame(gbl_tkroot) # tk.Label(tk_row, text="No. of Disks: ").pack(side=tk.LEFT) # ent_disks = tk.Entry(tk_row, width=5) # ent_disks.insert(0, str(gbl_num_disks)) # ent_disks.bind('', ent_return) # ent_disks.bind('', get_ent_focus) # ent_disks.pack(side=tk.LEFT, padx=5) # btn_start = tk.Button(tk_row, text="Start", width=6, command=run_hanoi) # # btn_start.bind('', run_hanoi) # btn_start.pack(side=tk.LEFT, padx=5) # btn_reset = tk.Button(tk_row, text="Reset", width=6, command=reset_hanoi) # # btn_reset.bind('', reset_hanoi) # btn_reset.pack(side=tk.LEFT, padx=5) # tk.Label(tk_row, text="Speed: ").pack(side=tk.LEFT) # tk_var = tk.StringVar(gbl_tkroot) # tk_var.set('normal') # tk_opt_menu = tk.OptionMenu(tk_row, tk_var, # *{'slow', 'normal', 'fastest'}) # tk_opt_menu.config(width=6, takefocus=True) # tk_opt_menu.pack(side=tk.LEFT) # tk_row.pack(side=tk.TOP, pady=5) # # animation canvas # gbl_canvas = tk.Canvas(gbl_tkroot, width=CANVAS_W, height=CANVAS_H) # gbl_canvas.pack() # btn_start.focus_set() # def change_speed(*args): # global gbl_speed, gbl_step # speed_opt = {'slow': 0.001, 'normal': 0.001, 'fastest': 0} # step_opt = {'slow': 1, 'normal': 2, 'fastest': 1} # gbl_speed = speed_opt.get(tk_var.get()) # gbl_step = step_opt.get(tk_var.get()) # # print(gbl_speed) # tk_var.trace('w', change_speed) # def run_hanoi(): # global gbl_ready # # t0 = time.time() # if gbl_ready: # gbl_ready = False # try: # hanoi(gbl_num_disks-1, 0, 1, 2) # except Exception as err: # print(f'forced reset: {repr(err)}') # else: # pass # # t1 = time.time() # # print(t1-t0) # def reset_hanoi(): # draw_init_setting() # def draw_init_setting(): # global gbl_pegs, gbl_disks, gbl_pegstate, gbl_ready # gbl_pegs = [] # gbl_pegstate = [[], [], []] # gbl_disks = [] # gbl_canvas.delete('all') # # draw the three pegs a, b, c # peg_w = 10 # peg_h = CANVAS_H // 2 # peg_dist = CANVAS_W // 3 # x1, y1 = (peg_dist - peg_w) // 2, CANVAS_H // 3 # x2, y2 = x1 + peg_w, y1 + peg_h # for i in range(3): # gbl_pegs.append(gbl_canvas.create_rectangle(x1, y1, x2, y2, fill = 'black')) # x1 += peg_dist # x2 += peg_dist # gbl_tkroot.update_idletasks() # gbl_tkroot.update() # # create disks on the first peg # disk_h = peg_h // 16 # disk_maxw = peg_dist * 2 // 3 # disk_minw = 2 * peg_w # x1, y1 = (peg_dist - disk_maxw) // 2, y2 - disk_h - GAP # x2, y2 = x1 + disk_maxw, y1 + disk_h # dx = (disk_maxw - disk_minw) // (2 * max(1, gbl_num_disks-1)) # dx = min(dx, 2 * peg_w) # for i in range(gbl_num_disks): # gbl_disks.insert(0, gbl_canvas.create_rectangle(x1, y1, x2, y2, fill = 'red')) # gbl_pegstate[0].append(gbl_num_disks - i - 1) # x1 += dx # x2 -= dx # y1 -= disk_h + GAP # y2 -= disk_h + GAP # gbl_tkroot.update_idletasks() # gbl_tkroot.update() # gbl_ready = True def move_disk(i, a, b): global gbl_pegstate, gbl_canvas disk_num = gbl_pegstate[a].pop() if disk_num != i: raise RuntimeError(f'Trying to move disk piece #{i} from peg {a}\ to peg {b} that does not exist.') # disk = gbl_disks[i] # # Lift the disk above peg a # px1, py1, px2, py2 = gbl_canvas.bbox(gbl_pegs[a]) # while True: # x1, y1, x2, y2 = gbl_canvas.bbox(disk) # if y2 < py1: # break # gbl_canvas.move(disk, 0, -gbl_step) # gbl_tkroot.update_idletasks() # gbl_tkroot.update() # time.sleep(gbl_speed) # # Move to peg b # px1, py1, px2, py2 = gbl_canvas.bbox(gbl_pegs[b]) # new_center = (px1 + px2) // 2 # x1, y1, x2, y2 = gbl_canvas.bbox(disk) # center = (x1 + x2) // 2 # while center < new_center: # gbl_canvas.move(disk, gbl_step, 0) # gbl_tkroot.update_idletasks() # gbl_tkroot.update() # time.sleep(0.7*gbl_speed) # faster # x1, y1, x2, y2 = gbl_canvas.bbox(disk) # center = (x1 + x2) // 2 # while center > new_center: # gbl_canvas.move(disk, -gbl_step, 0) # gbl_tkroot.update_idletasks() # gbl_tkroot.update() # time.sleep(0.7*gbl_speed) # faster # x1, y1, x2, y2 = gbl_canvas.bbox(disk) # center = (x1 + x2) // 2 # # Drop down # disk_h = y2 - y1 # new_bottom = py2 - disk_h * len(gbl_pegstate[b]) - GAP # while True: # x1, y1, x2, y2 = gbl_canvas.bbox(disk) # if y2 >= new_bottom: # break # gbl_canvas.move(disk, 0, gbl_step) # gbl_tkroot.update_idletasks() # gbl_tkroot.update() # time.sleep(gbl_speed) # gbl_pegstate[b].append(i) def hanoi(n :int, a :int, b :int, c :int): ''' Hanoi algorithm: move n pieces from a to c, using b as buffer. For each move, call animate() to show the move ''' if n < 0: return hanoi(n-1, a, c, b) move_disk(n, a, c) # move nth piece from peg a to peg c hanoi(n-1, b, a, c) def main(): tkroot = tk.Tk() hanoi = Hanoi(tkroot) tkroot.mainloop() if __name__ == "__main__": main()