マイクロマウス:tkinterアニメーションで見せる,バックトラック
Python3(3.10)で動くソースコード(.pyファイル .ipynbファイル)あります
「anaconda3」on .py「PyCharm」.ipynb「Jupyter Notebook」
(2023-11-14)Python3.10で動作確認済み
◆◆バックトラックに使われる再帰呼び出しとは◆◆
1.イニシャライズする 2.CALL SUB(1番目のステップ) 3.END SUB(N番目のステップ) 1.WHILE 方法がまだある A.次の方法を探す B.IF その方法が可能なら THEN 1.その方法を実行する 2.IF ゴールに達したなら THEN A.解を出力する 3.ELSE A.CALL SUB 4.その方法を取り消す 2.RETURN
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
Sub マイクロマウス イニシャライズ Gosub Mouse_Run End Sub Mouse_Run: W=1 LoopStart: If W=<3 Then 試行の計算 If Wが成功するなら Then 仮にマウスを進める If マウス座標がゴール Then 解を出力 Else Stackに保存する Stack Pointer1増 Gosub Mouse_Run 次のステップがもうなく戻ってきた 次のWの準備 Goto LoopStart Else 入ってきた方角に戻る Stackがまだ残っていれば Stack Pointer1減 Stackから戻す Return
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 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
# -*- coding: utf-8 -*- """ Micro Mouse """ import micromousesetting as St import micromouseBackTrack as MM import tkinter as Tk No_grid = 16 THKwall = 4 Timer = 250 # ミリ秒;アニメーションのコマ送り間隔(1以上を設定) class Gboard(Tk.Canvas): """Tk.Canvas for Mouse Grid """ grid_size = 25 def __init__(self, master): """コンストラクタ """ self.master = master """set initial value""" endline = self.grid_size * (No_grid + 1) z = self.grid_size * 0.36 """read solution""" path, visited, mouse_sol = MM.main_dungeon() self.mouse_sol = mouse_sol self.len_sol = len(mouse_sol) print(self.mouse_sol) # debug print(self.len_sol) # debug """親コンストラクタ""" Tk.Canvas.__init__(self, master, relief=Tk.RAISED, bd=4, bg="#DDFFFF", width=(No_grid+2)*self.grid_size, height=(No_grid+2)*self.grid_size, highlightthickness=0) """set initial value""" self.No_step = 0 # マウスの歩行数 self.dir_next = 0 # 出る方向 """drawing lines and coordinate""" for i in range(No_grid+1): x= (i + 1) * self.grid_size """グリッド細線を描く""" """drawing X axes""" self.create_line(self.grid_size, x, endline, x, width = 1) """drawing y axes""" self.create_line(x, self.grid_size, x, endline, width = 1) """drawing x coordinate""" self.create_text(x, self.grid_size * (No_grid + 1) + z , text=str(i), justify=Tk.CENTER, fill="#002010", font = ("Helvetica","6","normal")) """drawing y coordinate""" self.create_text(self.grid_size - z, x, text=str(No_grid - i), justify=Tk.RIGHT, fill="#002010", font = ("Helvetica","6","normal")) """drawing x axes wall""" for yg in range(No_grid): ww = St.wall_x(yg) y = (No_grid - yg + 1) * self.grid_size for xg in range(No_grid): if ww[xg] == 1: self.create_line(self.grid_size * (xg + 1), y, self.grid_size * (xg + 2), y, width = THKwall) self.create_line(self.grid_size, self.grid_size, endline, self.grid_size, width = THKwall) """drawing y axes wall""" for xg in range(No_grid): ww = St.wall_y(xg) x = (xg + 1) * self.grid_size for yg in range(No_grid): if ww[yg] == 1: self.create_line(x, self.grid_size * (No_grid - yg), x, self.grid_size * (No_grid - yg + 1), width = THKwall) self.create_line((No_grid+1) * self.grid_size, self.grid_size, (No_grid+1) * self.grid_size, endline, width = THKwall) """コンストラクタ終""" def put_a_mouse(self, xg, yg, dir): """draw a mouse """ xx = self.grid_size * (xg + 1.5) yy = self.grid_size * (No_grid - yg + 0.5) if dir == 1: chr = '∨' if dir == 2: chr = '<' if dir == 4: chr = '∧' if dir == 8: chr = '>' if dir == 0: chr = '○' self.delete(hex(xg) + hex(yg)) return self.create_text( xx, yy, text = chr, justify=Tk.CENTER, fill="#000000", font = ("MSゴシック","12","bold"), tag = hex(xg) + hex(yg)) def dir_steer(self, No_step): """出る方向を決定する """ """一番最初は上方向""" if No_step == 0: return 4 """今の座標と次の座標を比較して方向を選択""" before = self.mouse_sol[No_step] after = self.mouse_sol[No_step + 1] if after[0] == before[0]: if after[1] < before[1]: return 1 else: return 4 if after[1] == before[1]: if after[0] < before[0]: return 2 else: return 8 return 0 def refresh(self, No_step): """マウスを指示どおりに動かす 経路探索アルゴリズムは1行目のdir_steer()関数に組み込むことができる ここではマウスの軌跡を管理しているだけ """ if No_step == self.len_sol - 1: return True """出る方向の指示を確認する""" self.dir_next = self.dir_steer(No_step) #print self.dir_next, """出るグリッドを描画""" before = self.mouse_sol[No_step] self.put_a_mouse(before[0], before[1], self.dir_next) """入るグリッドを描画""" after = self.mouse_sol[No_step + 1] self.put_a_mouse(after[0], after[1], self.dir_next) return False class MouseGrid(Tk.Frame): """ Tk.Frame class of Mouse Grid """ def __init__(self, master=None): """コンストラクタ creating mousegrid canvas """ """親コンストラクタ""" Tk.Frame.__init__(self, master, relief=Tk.FLAT, bd=2) self.master.title("Micro Mouse") """title""" l_title = Tk.Label(self, text='Micro Mouse', font=('Times', '24', ('italic', 'bold')), fg='#191970', bg='#E8B77D', width=12) l_title.pack(padx=10, pady=10) self.mousegrid = Gboard(self) self.mousegrid.pack(padx=10,pady=10) 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) """set initial value""" self.No_step = 0 def show_next(self): """アニメーションのコマ送り """ sol = self.mousegrid.refresh(self.No_step) if sol: """解発見""" return self.No_step += 1 self.after(Timer, self.show_next) if __name__ == '__main__': app = MouseGrid() 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 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 131 132 133 134
# -*- coding: utf-8 -*- """ micro mouse by backtrack """ import micromousesetting as St dungeon = St.wall_setting() init = [0, 0] goal = [7, 7] path = [tuple(init)] pathtotal = [tuple(init)] mouse_at = [[0 for i in range(16)] for j in range(16)] mouse_at[0][0] = 1 way_to_dir = [[0 for i in range(16)] for j in range(16)] way_to_dir[0][0] = 4 stackway = [0 for i in range(250)] stackpointer = 1 def through_maze(): """メインなサブルーチン """ global path, pathtotal, stackpointer, sol_pathtotal, sol_path way = 1 while way < 4: # while文中で今のマスから隣接マスに進める # ここの座標 pos = list(path[-1]) # 3つの方法(方角)を試みる,4つめは戻りの方角 dir = get_dir(pos, way) if mouse_can_go(pos, dir): # 進むことができるなら仮に試みる pos = mouse_has_gone(pos, dir) path = path + [tuple(pos)] pathtotal = pathtotal + [tuple(pos)] if mouse_at[pos[0]][pos[1]] == 0: # 未訪問,訪問済みにする mouse_at[pos[0]][pos[1]] = 1 way_to_dir[pos[0]][pos[1]] = dir if pos == goal: # ゴールを発見 sol_pathtotal = pathtotal sol_path = path else: # 次のステップに進める recursiveなcall stackway[stackpointer] = way stackpointer += 1 through_maze() # 同じposで次のwayを試みる準備 way += 1 # while文の終了 else: # while文の終了後に実行 # recursiveなreturn if stackpointer > 1: stackpointer -= 1 way = stackway[stackpointer] pathtotal.append(path.pop()) #return None def get_dir(pos, ww): """最初(Way=1)に選ぶのは同じ方角,戻る方角は最後(Way=4)に選ぶ """ if way_to_dir[pos[0]][pos[1]] == 1: if ww == 1: return 1 if ww == 2: return 2 if ww == 3: return 8 if ww == 4: return 4 if way_to_dir[pos[0]][pos[1]] == 2: if ww == 1: return 2 if ww == 2: return 4 if ww == 3: return 1 if ww == 4: return 8 if way_to_dir[pos[0]][pos[1]] == 4: if ww == 1: return 4 if ww == 2: return 8 if ww == 3: return 2 if ww == 4: return 1 if way_to_dir[pos[0]][pos[1]] == 8: if ww == 1: return 8 if ww == 2: return 1 if ww == 3: return 4 if ww == 4: return 2 def mouse_can_go(pos, dir): ww = dungeon[pos[0]][pos[1]] if dir == 1 and ww & 1 != 1 and mouse_at[pos[0]][pos[1] - 1] == 0: return True if dir == 2 and ww & 2 != 2 and mouse_at[pos[0] - 1][pos[1]] == 0: return True if dir == 4 and ww & 4 != 4 and mouse_at[pos[0]][pos[1] + 1] == 0: return True if dir == 8 and ww & 8 != 8 and mouse_at[pos[0] + 1][pos[1]] == 0: return True return False def mouse_has_gone(pos, dir): if dir == 1: pos[1] = pos[1] - 1 if dir == 2: pos[0] = pos[0] - 1 if dir == 4: pos[1] = pos[1] + 1 if dir == 8: pos[0] = pos[0] + 1 return pos def main_dungeon(): """main program """ through_maze() # 文字壁の探索プログラムのreturn文に合わせる dummy =[] return sol_path, dummy, sol_pathtotal if __name__ == "__main__": sol_path, dummy, sol_pathtotal = main_dungeon() print(sol_path) print(sol_pathtotal)
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
# -*- coding: utf-8 -*- """ micro mouse setting """ def wall_setting(): """wall_setting """ return [ [11,2,6,7,7,7,3,10,10,10,6,3,10,2,10,6], [3,12,1,0,0,0,4,11,2,10,12,5,3,8,6,5], [9,6,5,13,13,13,9,6,9,2,10,12,5,3,12,5], [3,12,9,10,10,10,6,9,6,13,3,6,5,9,6,5], [9,6,3,10,10,10,8,14,9,10,12,9,12,3,12,5], [3,8,4,11,2,10,10,10,10,10,10,10,10,8,6,5], [5,7,1,6,5,3,2,2,14,11,2,10,2,14,5,5], [5,1,12,5,5,5,5,1,6,3,12,3,8,6,5,5], [5,5,3,12,5,5,5,9,12,9,6,9,2,12,5,5], [5,5,9,6,5,5,5,11,2,6,5,3,8,6,5,5], [5,5,3,8,4,5,1,6,5,1,12,9,2,12,5,5], [1,0,8,2,12,1,12,9,4,5,3,6,9,6,5,5], [5,5,3,12,11,8,10,10,8,4,5,9,6,9,4,5], [9,8,12,3,6,3,10,10,10,12,9,6,9,6,5,5], [3,10,10,12,9,8,10,10,10,10,10,8,14,9,4,5], [9,10,10,10,10,10,10,10,10,10,10,10,10,10,8,12]] def wall_code(x, y): """wall position of grid """ w = wall_setting() return w[x][y] def wall_x(y): wall = [0 for i in range(16)] w = wall_setting() for i in range(16): wall[i]= w[i][y] % 2 return wall def wall_y(x): wall = [0 for i in range(16)] w = wall_setting() for i in range(16): wall[i]= int(bin(w[x][i]>>1),2) % 2 return wall if __name__=='__main__': print(wall_code(0,0), wall_code(15,15)) print(wall_x(0)) print(wall_x(1)) print(wall_x(2)) print(wall_x(3)) print(wall_y(0)) print(wall_y(1)) print(wall_y(2)) print(wall_y(3))