小时候我们都玩过推箱子,把箱子从一个点推到指定地点,碰到墙的时候就推不动了,这个游戏大家小时候肯定玩的不亦乐乎,废了不少功夫一关一关的过,其实这个游戏用代码实现还是很简单的,今天就给大家讲讲A星寻路算法,A星寻路算法是一种用于寻找有效路径的算法。
A星寻路算法需要两个列表和一个公式:
open_list——可到达的格子
close_list——已走过的格子
公式:
F = G + H
每一个格子都具有F,G,H这3个属性,如图
G: 从起点走到当前格子一共走了几步
H: 在不考虑障碍的情况下,从当前格子走到目标格子需要的步数
F:综合权重,G和H的相加的值,F = G+H
假设我们有一个7*5大小的迷宫,从绿色点走到红色点,黑色为墙壁,每一步只能上,下,左,右移动一格,如图:
怎么才能从绿点走到黄点呢,其实并不难:(grid:格子)
1.把起点放入open_list列表中,open_list:grid(1,2) ,close_list:
2.起点直接放在open_list中,这时open_list只有这一个点,然后把起点移除open_list,放入close_list中,代表这个格子检查过了。
3.找出当前方格,上,下,左,右所有可到达的格子,看他们是否在open_list和close_list当中,如果不在,则将他们加入open_list中,计算相应的G,H,F值,并把所在的格子作为父节点。(父节点用来记录上一个格子的位置,在输出最终路线会用到)
open_list:grid(1,1),grid(0,2),grid(2,2),grid(1,3)
close_list:grid(1,2)
5.找出open_list中F值最小的方格,即方格grid(2,2),F值为4,将它作为当前方格,并把当前方格移除open_list,放入close_list,代表这个格子检查过了。剩下的就是以前面的方式继续迭代,直到open_list中出现终点为止,寻路算法就结束了,我们只要顺着终点方格找到它的父亲,在找到父亲的父亲,如此回溯,就找到一条最佳路径。
主要思路:
1.初始化迷宫地图
2.设置其实点和终点
3.path列表记录走过路径的点
4.使用查找终点方法找到终点,然后回溯路径,用path添加当前路径
5.一个写了5个方法和一个节点类实现:查找终点方法,找F值最小格子的方法,找领近格子方法,验证是否越界墙壁方法,验证走过格子方法
代码如下:
def a_star_search(start, end):
"""
寻路方法,找到终点返回终点grid,否则返回None
:param start:
:param end:
:return:
"""
#待访问的格子
open_list = []
#已访问的格子
close_list = []
#把起点加入open_list中
open_list.append(start)
#主循环,每一轮检查一个当前方格节点
while len(open_list) > 0:
#在open_list中查找F值最小的节点作为当前方格节点
current_grid = find_min_gird(open_list)
#将F值最小的节点从open_list中删除
open_list.remove(current_grid)
##将F值最小的节点加入到close_list中
close_list.append(current_grid)
#找到当前节点的所有领近节点
neighbors = find_neighbors(current_grid, open_list, close_list)
for grid in neighbors:
if grid not in open_list:
#如果当前节点不在open_list中,标记为父节点,并放入open_list中
grid.init_grid(current_grid, end)
open_list.append(grid)
#如果终点在open_list中,直接返回终点格子
for grid in open_list:
if (grid.x == end.x) and (grid.y == end.y):
return grid
#遍历完open_list,仍然找不到终点,说明还没到终点,返回空
return None
def find_min_gird(open_list=[]):
"""
寻找F值最小的格子的方法
:param open_list:
:return:
"""
temp_grid = open_list[0]
for grid in open_list:
if grid.f < temp_grid.f:
temp_grid = grid
return temp_grid
def find_neighbors(grid,open_list=[],close_list=[]):
"""
寻找当前格子领近格子的方法
:param grid:
:param open_list:
:param close_list:
:return:
"""
grid_list = []
if is_valid_grid(grid.x,grid.y-1,open_list,close_list):
grid_list.append(Grid(grid.x,grid.y-1))
if is_valid_grid(grid.x,grid.y+1,open_list,close_list):
grid_list.append(Grid(grid.x,grid.y+1))
if is_valid_grid(grid.x-1,grid.y,open_list,close_list):
grid_list.append(Grid(grid.x-1,grid.y))
if is_valid_grid(grid.x+1,grid.y,open_list,close_list):
grid_list.append(Grid(grid.x+1,grid.y))
return grid_list
def is_valid_grid(x,y,open_list=[],close_list=[]):
"""
判断是否越界和障碍物方法
:param x:
:param y:
:param open_list:
:param close_list:
:return:
"""
#判断是否越界
if x < 0 or x >=len(MAZE) or y < 0 or y >= len(MAZE[0]):
return False
#判断是否有障碍物
if MAZE[x][y] == 1:
return False
#是否已经在open_list中
if contain_grid(open_list,x,y):
return False
# 是否已经在close_list中
if contain_grid(close_list,x,y):
return False
return True
def contain_grid(grids, x, y):
for grid in grids:
if (grid.x == x) and (grid.y == y):
return True
return False
class Grid:
def __init__(self, x, y):
self.x = x
self.y = y
self.f = 0
self.g = 0
self.h = 0
self.parent = None
def init_grid(self, parent, end):
self.parent = parent
if parent is not None:
self.g = parent.g + 1
else:
self.g = 1
self.h = abs(self.x - end.x) + abs(self.y - end.y)
self.f = self.g + self.h
#迷宫地图
MAZE = [
[0,0,0,0,0,0,0],
[0,0,0,1,0,0,0],
[0,0,0,1,0,0,0],
[0,0,0,1,0,0,0],
[0,0,0,0,0,0,0],
]
#设置起始点和终点
start_grid = Grid(2, 1)
end_grid = Grid(2, 5)
#搜索迷宫终点
result_grid = a_star_search(start_grid, end_grid)
#回溯迷宫路径
path = []
while result_grid is not None:
path.append(Grid(result_grid.x, result_grid.y))
result_grid = result_grid.parent
#输出迷宫和路径,路径用*号表示
for i in range(0,len(MAZE)):
for j in range(0,len(MAZE[0])):
if contain_grid(path, i, j):
print("*, ", end='')
else:
print(str(MAZE[i][j]) + ", ", end='')
print()
结果图: