マイクロマウス:tkinterアニメーションで見せる
探索回数が少ない最短経路探索A*(A-Star,エースター)アルゴリズム
Python3(3.10)で動くソースコード(.pyファイル .ipynbファイル)あります
「anaconda3」on .py「PyCharm」.ipynb「Jupyter Notebook」
(2023-11-14)Python3.10で動作確認済み
◆◆最短経路探索 A*(エースター)アルゴリズム◆◆
修正前 def heuristic(pos): return ((pos[0] - goal[0]) ** 2 + (pos[1] - goal[1]) ** 2) ** 0.5 修正後 def heuristic(pos): return (abs(pos[0] - goal[0]) + abs(pos[1] - goal[1]))
修正前 def distance(path): return len(path) 修正後 def distance(path): dist = 0 for n in range(len(path)-2): p0 = path[n] p1 = path[n+1] p2 = path[n+2] x = p0[0] - 2 * p1[0] + p2[0] y = p0[1] - 2 * p1[1] + p2[1] if x != 0 or y != 0: dist += 1 return len(path) + dist
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 micromouseAstar 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
# -*- coding: utf-8 -*- """ path search by A* algorithim """ from heapq import heappush, heappop import micromousesetting as St dungeon = St.wall_setting() init = (0, 0) goal = (7, 7) def astar(init, goal): """A* algorithim """ queue = [] visited = [init] # debug # 評価関数 初期値 init_score = distance([init]) + heuristic(init) # close-listの代わりに訪問済みリストを用意 checked = {init: init_score} # open-listのstart-node heappush(queue, (init_score, [init])) No = 0 while len(queue) > 0: No += 1 # open-listからコスト最小の探索済み経路を取り出す score, path = heappop(queue) # コスト最小経路の最後の座標 last = path[-1] # 訪問済みとして登録,debug用でありアルゴリズムに関係ない visited.append(last) # debug # print(len(path), len(visited), last) # debug if last == goal: # ゴールを発見 print(No) return path, visited # コスト最小経路の最後の座標の四方を探索 for pos in nexts(last): newpath = path + [pos] # 評価関数でコスト予測値 pos_score = distance(newpath) + heuristic(pos) # コスト最小経路の最後の座標が, # 訪問済みで今回のコストのほうが大きいならば次の四方の座標の探索へ if pos in checked and checked[pos] <= pos_score: continue # 訪問済みで今回のコストのほうが小さいならばor訪問済みでなくても # 訪問済みリストに格納 checked[pos] = pos_score # open-listに今回のコストと経路を追加 heappush(queue, (pos_score, newpath)) # pass # pass # while文を抜けたら探索失敗 return None def nexts(pos): """マウスが四方に行くことが可能か判定 """ # north if (dungeon[pos[0]][pos[1]]) & 4 != 4: yield (pos[0], pos[1] + 1) # south if (dungeon[pos[0]][pos[1]]) & 1 != 1: yield (pos[0], pos[1] - 1) # west if (dungeon[pos[0]][pos[1]]) & 2 != 2: yield (pos[0] - 1, pos[1]) # east if (dungeon[pos[0]][pos[1]]) & 8 != 8: yield (pos[0] + 1, pos[1]) # pass def heuristic(pos): """直線距離;ユークリッド距離""" # return ((pos[0] - goal[0]) ** 2 + (pos[1] - goal[1]) ** 2) ** 0.5 """マンハッタン距離""" return (abs(pos[0] - goal[0]) + abs(pos[1] - goal[1])) def distance(path): return len(path) dist = 0 for n in range(len(path)-2): p0 = path[n] p1 = path[n+1] p2 = path[n+2] x = p0[0] - 2 * p1[0] + p2[0] y = p0[1] - 2 * p1[1] + p2[1] if x != 0 or y != 0: dist += 1 return len(path) + dist def main_dungeon(): """main program """ mdpath, mdvisited = astar(init, goal) # 文字壁の探索プログラムのreturn文に合わせる return mdpath, mdvisited, mdpath if __name__ == "__main__": mpath, mvisited, mpath = main_dungeon() print(mpath) ''' print(nexts([0,0])) print(nexts([15,15])) for i in range(16): print(i&8,) '''
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))