From fee0eaf1be3dd3e0e370866f01a6a9d391f8b9cb Mon Sep 17 00:00:00 2001 From: Tom Wang Date: Mon, 4 May 2020 01:26:51 -0700 Subject: [PATCH] Update hanoi_class.py - v1.0 release --- hanoi_class.py | 339 +++++++++++++++++++------------------------------ 1 file changed, 129 insertions(+), 210 deletions(-) diff --git a/hanoi_class.py b/hanoi_class.py index bfe4ec9..93fd79c 100644 --- a/hanoi_class.py +++ b/hanoi_class.py @@ -14,12 +14,17 @@ class Hanoi: self.num_disks = num_disks self.canvas_w = canvas_w self.canvas_h = canvas_h + self.peg_w = 10 + self.peg_h = canvas_h // 2 + self.peg_dist = canvas_w // 3 + self.disk_h = self.peg_h // 16 + self.disk_maxw = self.peg_dist * 2 // 3 + self.disk_minw = 2 * self.peg_w self._gap = 2 self._speed = 0.001 # in seconds self._step = 2 - self._pegs = [] - self._pegstate = [[], [], []] + self._ready = False # not ready to run self._tkroot = tkroot self._frame = tk.Frame(self._tkroot) @@ -35,6 +40,7 @@ class Hanoi: *{'slow', 'normal', 'fastest'}) self._canvas = tk.Canvas(self._tkroot, width=canvas_w, height=canvas_h) self._init_tkwindow() + self._setup_pegs() def _init_tkwindow(self): self._tkroot.title('Hanoi Tower') @@ -56,244 +62,157 @@ class Hanoi: self._tkroot.lift() self._tkroot.focus_force() + def _setup_pegs(self): + + self._pegs = [] + self._disks = [] + self._pegstate = [[], [], []] + + self._canvas.delete('all') + # draw the three pegs a, b, c + + x1, y1 = (self.peg_dist - self.peg_w) // 2, self.canvas_h // 3 + x2, y2 = x1 + self.peg_w, y1 + self.peg_h + + for i in range(3): + self._pegs.append(self._canvas.create_rectangle(x1, y1, x2, y2, fill = 'black')) + x1 += self.peg_dist + x2 += self.peg_dist + + # create disks on the first peg + x1, y1 = (self.peg_dist - self.disk_maxw) // 2, y2 - self.disk_h - self._gap + x2, y2 = x1 + self.disk_maxw, y1 + self.disk_h + dx = (self.disk_maxw - self.disk_minw) // (2 * max(1, self.num_disks-1)) + dx = min(dx, 2 * self.peg_w) + + for i in range(self.num_disks): + self._disks.insert(0, self._canvas.create_rectangle(x1, y1, x2, y2, fill = 'red')) + self._pegstate[0].append(self.num_disks - i - 1) + x1 += dx + x2 -= dx + y1 -= self.disk_h + self._gap + y2 -= self.disk_h + self._gap + + self._tkroot.update_idletasks() + self._tkroot.update() + self._ready = True + 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') + focus_widget = self._tkroot.focus_get() + if focus_widget != self._ent_disks: + self._btn_reset.focus_set() + self._btn_start.config(state='disabled') + self._run_hanoi() + else: + self._btn_start.focus_set() def _cmd_reset(self): self._btn_start.focus_set() self._btn_start.config(state='normal') + self._setup_pegs() def _run_hanoi(self): - pass + if self._ready: + self._ready = False + try: + self.hanoi(self.num_disks - 1, 0, 1, 2) + except Exception as err: + print(f'forced reset: {repr(err)}') - def _ent_return(self): - pass + def _ent_return(self, event): + self._ent_disks.tk_focusNext().focus() - def _ent_focus(self): - pass + def _ent_focus(self, event): + try: + new_num = int(self._ent_disks.get()) + if new_num < 1: new_num = 1 + if new_num > 12: new_num =12 + self.num_disks = new_num + self._cmd_reset() + except Exception as err: + print(repr(err)) + finally: + self._ent_disks.delete(0, tk.END) + self._ent_disks.insert(0, str(self.num_disks)) - def _reset_hanoi(self): - pass + def _move_disk(self, i, a, b): # move disk i from peg a to peg b + disk_num = self._pegstate[a].pop() + if disk_num != i: # should not happen + raise RuntimeError(f'Trying to move disk piece #{i} from peg {a}\ + to peg {b} that does not exist.') + disk = self._disks[i] + # Lift the disk above peg a + px1, py1, px2, py2 = self._canvas.bbox(self._pegs[a]) + while True: + x1, y1, x2, y2 = self._canvas.bbox(disk) + if y2 < py1: + break + self._canvas.move(disk, 0, -self._step) + self._tkroot.update_idletasks() + self._tkroot.update() + time.sleep(self._speed) -# 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 + # Move to peg b + px1, py1, px2, py2 = self._canvas.bbox(self._pegs[b]) + new_center = (px1 + px2) // 2 + x1, y1, x2, y2 = self._canvas.bbox(disk) + center = (x1 + x2) // 2 + while center < new_center: + self._canvas.move(disk, self._step, 0) + self._tkroot.update_idletasks() + self._tkroot.update() + time.sleep(0.7*self._speed) # faster + x1, y1, x2, y2 = self._canvas.bbox(disk) + center = (x1 + x2) // 2 -# def setup_gui(): -# global gbl_canvas + while center > new_center: + self._canvas.move(disk, -self._step, 0) + self._tkroot.update_idletasks() + self._tkroot.update() + time.sleep(0.7*self._speed) # faster + x1, y1, x2, y2 = self._canvas.bbox(disk) + center = (x1 + x2) // 2 -# def ent_return(event): -# event.widget.tk_focusNext().focus() + # Drop down + disk_h = self.disk_h + new_bottom = py2 - disk_h * len(self._pegstate[b]) - self._gap + while True: + x1, y1, x2, y2 = self._canvas.bbox(disk) + if y2 >= new_bottom: + break + self._canvas.move(disk, 0, self._step) + self._tkroot.update_idletasks() + self._tkroot.update() + time.sleep(self._speed) -# def get_ent_focus(event): -# global gbl_num_disks + self._pegstate[b].append(i) -# 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 hanoi(self, 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 + self.hanoi(n-1, a, c, b) + self._move_disk(n, a, c) # move nth piece from peg a to peg c + self.hanoi(n-1, b, a, c) def main(): tkroot = tk.Tk() - hanoi = Hanoi(tkroot) + Hanoi(tkroot) tkroot.mainloop()