ハノイの塔:tkinterアニメーションで人間の試行のように見せる
再起呼び出しだけどバックトラックではないアルゴリズムで解く
Python3(3.10)で動くソースコード(.pyファイル .ipynbファイル)あります
「anaconda3」on .py「PyCharm」.ipynb「Jupyter Notebook」
(2023-11-14)Python3.10で動作確認済み
◆◆ソースファイル◆◆
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
# -*- coding: utf-8 -*- """ Tower of Hanoi """ import tkinter as Tk import Hanoi_sol as Ha Thk_disk = 20 Rad_disk = 20 X00 = 150 Y00 = Thk_disk * 7 Span = 220 disk_color = '#999999' Timer = 1000 # ミリ秒;アニメーションのコマ送り間隔(1以上を設定):推奨は1500 class Tower(Tk.Canvas): """Tk.Canvas for Tower """ cell_size = 46 margin = 5 q_images = [] disk_fig = [] def __init__(self, master): """コンストラクタ """ cwidth = 3.35 * Span cheight = Y00 + Thk_disk * 2 """親コンストラクタ""" Tk.Canvas.__init__(self, master, relief=Tk.RAISED, bd=4, bg='white', width=cwidth, height=cheight) """解をいっぺんに読み込む""" self.sol = Ha.solution() # print self.sol """set initial value""" self.No_disk = [5, 0, 0] # 各塔にある円盤の数 """地面の描画""" self.create_rectangle( \ 20, Y00, cwidth-20, Y00+20, fill='#666666', width=0, \ tags=(hex(0)+hex(0)) ) """5枚の円盤の描画(ディフォルトは左の塔)""" for i in range(5): x0, y0, x1, y1 = self.coord_disk(1,i+1,5-i) self.disk_fig.append( \ self.create_rectangle ( \ x0, y0, x1, y1, fill=disk_color, \ width=0, tags=(hex(5-i)+hex(5-i)) \ ) ) """コンストラクタ終""" def put_a_disk(self, xg, yg, id): """draw a disk """ self.delete(hex(id)+hex(id)) x0, y0, x1, y1 = self.coord_disk(xg, yg, id) self.create_rectangle( \ x0, y0, x1, y1, fill=disk_color, \ width=0, tags=(hex(id)+hex(id)) \ ) def coord_disk(self, xg, yg, id): """xg番の塔の下から位置yg番目のid番の円盤 """ x0 = X00 - Rad_disk * id + Span * (xg - 1) x1 = X00 + Rad_disk * id + Span * (xg - 1) y0 = Y00 - Thk_disk * (yg - 1) y1 = Y00 - Thk_disk * yg return x0, y0, x1, y1 def refresh(self, No_step): """円盤を移す """ if self.sol[No_step+1][0] ==0: # ゴールの判定 return True xb = self.sol[No_step+1][1] xa = self.sol[No_step+1][2] id = self.sol[No_step+1][0] self.No_disk[xb - 1] -= 1 self.No_disk[xa - 1] += 1 self.put_a_disk(xa, self.No_disk[xa - 1], id) return False class Hanoi(Tk.Frame): """ Tk.Frame class of Hanoi """ def __init__(self, master=None): """コンストラクタ creating Hanoi canvas """ """親コンストラクタ""" Tk.Frame.__init__(self, master) self.master.title("Tower of Hanoi") # title l_title = Tk.Label( \ self, text='Tower of Hanoi', \ font=('Times', '24', ('italic', 'bold')), fg='#191970', bg='#EEE8AA', width=12 ) l_title.pack(padx=10, pady=10) # canvas self.TowerCanvas = Tower(self) self.TowerCanvas.pack(padx=10, pady=10) # buttons and a counter self.No_step = 0 self.f_footer = Tk.Frame(self) self.f_footer.pack() self.a_button = Tk.Button( \ self.f_footer, text="start", font=("Times", 14), \ command = self.show_next \ ) self.a_button.pack(side=Tk.LEFT, padx=5,pady=5) """コンストラクタ終""" def show_next(self): """アニメーションのコマ送り """ sol = self.TowerCanvas.refresh(self.No_step) if sol: """解発見""" return self.No_step += 1 self.after(Timer, self.show_next) if __name__ == "__main__": app = Hanoi() app.pack() app.mainloop()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
# -*- coding: utf-8 -*- """ Solution of Tower of Hanoi """ """ sol[] = [A, B, C] の意味 A 番の円盤をB 番の塔からC 番の塔へ移す。 円盤は最小が1番,最大が5番,塔は左が1番,右が3番とする。 hanoi(X, Y, Z)関数の引数の意味 X 枚の円盤が最初 Y+1 番目の塔にありそれを全部 Z+1 番目の塔に移す。 描画プログラムはそれを読み取らないのでバグります。 このプログラム単体で確認してください。 """ def solution(): No_tower = 3 # 塔の数 sol = [[]] # 円盤を移す手順 def hanoi(n, before, after): if n > 0: hanoi(n - 1, before, No_tower - (before + after)) sol.append([n, before+1, after+1]) hanoi(n - 1, No_tower - (before + after), after) hanoi(5,0,2) sol.append([0,0,0]) return sol if __name__ == '__main__': print(solution())