Update hanoi_class.py - v1.0 release

This commit is contained in:
2020-05-04 01:26:51 -07:00
parent e5fe256547
commit fee0eaf1be

View File

@@ -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('<Return>', ent_return)
# ent_disks.bind('<FocusOut>', 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('<ButtonRelease-1>', 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('<ButtonRelease-1>', 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()