本文采用A*算法的思想,借助pycharm下的python环境,求解迷宫的最优路径问题。
①根据用户界面的提示,可以自定义生成一个迷宫;
②本迷宫所采用的主要算法为启发式A*算法,通过其寻找到一条起点和终点间的最短距离,输出走迷宫的最优路径并显示。
(1)A*算法介绍
A算法是一种启发式搜索算法。在A*算法中,从起点开始后,通过启发函数来检查相邻方格,找到代价最小的“节点”作为下一个“父节点”,不断地搜索直到找到最优解。
(2)open表和close表
A*算法的两个重要数据列表:
①open表:用来存储可能用来寻找最短路径的可到达或通过的方格
②close表:用来存储不再需要检查或无法通过的方格
(3)启发函数
启发函数是A*算法的核心,在选择路径的过程中发挥重要作用。其表达式如下:
f(n)=g(n)+h(n)
g(n)表示的是从起点到当前位置所产生的实际移动代价;
h(n)表示的是从当前位置到终点的预估移动代价。
h(n)的选取保证了找到最短路径这一条件,计算h(n)的方法有许多种,本文主要采用的是曼哈顿方法,即计算从当前格到目的格之间水平和垂直的方格的数量总和。
此外,A算法的启发函数还要满足的条件是:h(n)≤h*(n) (h*(n)为实际距离的代价值)。
(4)算法流程
① 首先初始化open表和close表,将开始节点放入open表中,此时开始节点的g值和h值都为0。
② 将所有可到达的方格作为相邻节点加入open表中,通过启发函数计算这些节点的f值,找到值最小的节点。
③ 将open表中的父节点删除,放入close表中。
④ 将找到的f值最小的节点作为现在的父节点,不断地重复循环遍历,找到下一个当前节点和其相邻结点f值最小的节点作为父节点。
若相邻节点不可通过或者已经在close中,则不执行任何操作;
若相邻节点不在open表中,则将其加入open表中,当前节点为其父节点,计算出它们之间的f值;
若相邻节点已经在open表中,则判断此时的当前节点和该相邻节点间的f值是否更小,如果是,则更新此时的f值。
⑤ 当最终的目的节点被加入到open列表时,表明此时已经找到了最短路径;否则,直到open列表空时也没有找到目的节点,则表明找不到路径,结束遍历。
重要代码:
(1)判断终点是否在open表中
def endInOpenList(self):
for i in self.openList:
if self.endPoint[0] == i.x and self.endPoint[1] == i.y:
return True
return False
(2)将节点加进open表前需检查其是否已经在open表中
def inOpenList(self, p1):
for i in range(0, len(self.openList)):
if p1.isEq(self.openList[i]):
return i
return -1
(3)A star算法寻路
def AstarMap(self):
self.openList.append(self.startPoint) # 将起点放到open表中
while (not self.isOK): # 先检查终点是否在open表中 ,有则结束
if self.inOpenList(self.endPoint) != -1:
self.isOK = True
self.end = self.openList[self.inOpenList(self.endPoint)]
self.route.append(self.end)
self.te = self.end
while (self.te.parentPoint != 0):
self.te = self.te.parentPoint
self.route.append(self.te)
else:
self.sortOpenList() # 将估值最小的节点放在index = 0
Minnode = self.openList[0] # Minnode为估值最小节点
self.openList.pop(0)
self.closeList.append(Minnode)
(1)设计窗体布局,以及实现迷宫的画布
self.root = Tk()
self.root.title('A*算法实现迷宫')
self.root.geometry("1050x600+200+50") #设置窗口大小
self.canvas = Canvas(self.root, width=500, height=500, bg="white")
self.canvas.pack(side='left')
for i in range(1, 10):
self.canvas.create_line(50, 50 * i, 450, 50 * i) # 横线
self.canvas.create_line(50 * i, 50, 50 * i, 450) # 竖线
self.canvas.bind("" , self.drawMapBlock)# 将画布绑定到迷宫区域
self.root.mainloop()
(2)设计障碍物、起点、终点、所寻路径的颜色
self.blockcolor = ['black', 'green', 'red', 'purple'] # 设置障碍为黑色,起点为绿色 终点为红色,路径为紫色
self.mapStatus = np.ones((8, 8), dtype=int) # 地图状态数组(全0数组) 1无障碍 0障碍
#颜色设置
def selectobstacle(self):
self.blockcolorIndex = 0 #黑色
def selectstart(self):
if not self.selectedStart:
self.blockcolorIndex = 1 # 绿色
else:
self.blockcolorIndex = 0 # 黑色
def selectend(self):
if not self.selectedEnd:
self.blockcolorIndex = 2 # 红色
else:
self.blockcolorIndex = 0 # 黑色
def selectaction(self):
self.blockcolorIndex = 0 # 黑色
self.AstarMap()
self.route.pop(-1)
self.route.pop(0)
for i in self.route:
self.canvas.create_rectangle((i.x + 1) * 50, (i.y + 1) * 50, (i.x + 2) * 50, (i.y + 2) * 50, fill='purple')
(3)设计五个功能,分别为选择障碍,选择起点,选择重点,开始寻路和重新开始。
self.btn_obstacle = Button(self.root, text="选择障碍", command=self.selectobstacle)
self.btn_obstacle.pack(side='left',anchor='n')
self.btn_start = Button(self.root, text="选择起点", command=self.selectstart)
self.btn_start.pack(side='left',anchor='n')
self.btn_end = Button(self.root, text="选择终点", command=self.selectend)
self.btn_end.pack(side='left',anchor='n')
self.btn_pathfinding = Button(self.root, text="开始寻路", command=self.selectaction)
self.btn_pathfinding.pack(side='left',anchor='n')
self.btn_restart = Button(self.root, text="重新游戏", command=self.selectrestart)
self.btn_restart.pack(anchor='w')