From 08ccb4c68ae7ff59eb6bdaf3023023a13a8a4810 Mon Sep 17 00:00:00 2001 From: cpan Date: Mon, 27 Apr 2020 00:27:20 +0000 Subject: [PATCH] Initial Commit When using tkinter in python, there seems to be some sutble conflict when using tk.update() and tk.mainloop() at the same time. --- hanoi.py | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 hanoi.py diff --git a/hanoi.py b/hanoi.py new file mode 100644 index 0000000..772e8a3 --- /dev/null +++ b/hanoi.py @@ -0,0 +1,215 @@ +# -*- coding: utf-8 -*- +""" +Created on Sat Apr 25 21:18:50 2020 + +@author: cpan +""" + +import tkinter as tk +import time + +gbl_num_disks = 3 +GAP = 2 +CANVAS_W = 500 +CANVAS_H = 400 +gbl_speed = 0.001 # in seconds +gbl_pegs = [] +gbl_pegstate = [[], [], []] +gbl_disks = [] +gbl_tkroot = tk.Tk() +gbl_canvas = None +gbl_ready = True + + +def setup_gui(): + global gbl_canvas + + def get_ent(event): + global gbl_num_disks + + try: + new_num = int(ent_disks.get()) + gbl_num_disks = new_num + event.widget.tk_focusNext().focus() + 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('', get_ent) + ent_disks.bind('', get_ent) + ent_disks.pack(side=tk.LEFT, padx=5) + btn_start = tk.Button(tk_row, text="Start", width=6) + btn_start.bind('', run_hanoi) + btn_start.pack(side=tk.LEFT, padx=5) + btn_reset = tk.Button(tk_row, text="Reset", width=6) + 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) + 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() + + def change_speed(*args): + global gbl_speed + speed_opt = {'slow': 0.002, 'normal': 0.001, 'fastest': 0} + gbl_speed = speed_opt.get(tk_var.get()) + # print(gbl_speed) + + tk_var.trace('w', change_speed) + + +def run_hanoi(event): + global gbl_ready + + 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 + + +def reset_hanoi(event): + 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, -1) + 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 + while True: + x1, y1, x2, y2 = gbl_canvas.bbox(disk) + center = (x1 + x2) // 2 + if center == new_center: + break + if center > new_center: + gbl_canvas.move(disk, -1, 0) + else: + gbl_canvas.move(disk, 1, 0) + gbl_tkroot.update_idletasks() + gbl_tkroot.update() + time.sleep(0.7*gbl_speed) # faster + + # 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, 1) + gbl_tkroot.update_idletasks() + gbl_tkroot.update() + time.sleep(gbl_speed) + + gbl_pegstate[b].append(i) + + +def hanoi(n, a, b, c): + ''' + 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(): + + setup_gui() + draw_init_setting() + + # input("Press Enter to continue") + + # try: + # hanoi(gbl_num_disks-1, 0, 1, 2) + # except Exception as err: + # print("aborted: " + repr(err)) + + gbl_tkroot.mainloop() + + +if __name__ == "__main__": + main() \ No newline at end of file