原創文章,轉載請聯繫做者node
時光只解催人老,不信多情,長恨離亭,淚滴春衫酒易醒。git
最近接觸了一個挺有意思的小課題,跟你們分享一下。就是利用A*
算法,來計算迷宮可行路徑。有關這個算法的知識,你們能夠看看A*算法維基百科以及A星算法詳解來稍做了解。
代碼地址在此Maze,喜歡Python
的小可愛們能夠拿去練練手。github
本題中的迷宮,是以宮格類型呈現的,在代碼中的呈現爲二維數組
。其次在迷宮中的移動,也只有上、下、左、右四個動做可選。以下所示:算法
其中1表明入口,2表明障礙物不可通行,3表明出口數組
[[3, 2, 2, 2, 2, 2, 2, 2, 1],
[0, 0, 2, 2, 2, 2, 2, 0, 0],
[2, 0, 0, 2, 2, 2, 0, 0, 2],
[2, 2, 0, 0, 2, 0, 0, 2, 2],
[2, 2, 2, 0, 0, 0, 2, 2, 2]]
複製代碼
其實在A*算法
中,對單位搜索區域的描述爲--節點nodes
。在本題中,咱們能夠把搜索區域視爲正方形,會更簡單一點。bash
A*算法
的邏輯其實並非很難,簡化起來就是兩個詞:評估、循環。
從起點開始行動,首先找到起點周圍能夠行走的節點
,而後在這個節點中,評估
出距離終點最優(最短)的節點
。那麼這個最優
節點,將做爲下一步行動的點,以此類推,直至找到終點。
能夠看到,在這個邏輯中,其實最重要的就是評估
這一步了。A*算法
的評估函數爲:f(n) = g(n) + h(n)
app
g(n)
--表明移動到這個點的代價,在本題中均爲1.由於只能夠水平或者數值運動。要是斜角能夠移動的話,那麼這個值就爲√2
h(n)
--從這個點移動到終點的代價,這是一個猜想值。本題中,將迷宮視做座標系的話,那麼h(n)
就是取和終點x、y各自差值的最小者。譬如點[4,2]和終點[1,1]的h(n)
取值爲:1函數
代碼中對點的描述,均爲實際值,並不是以0爲開始值計算。ui
env_data
表明迷宮數組:# 上下左右四個移動命令,只具有四個移動命令
orders = ['u', 'd', 'l', 'r']
# 定位起點和終點
start_loc = []
des_loc = []
for index, value in enumerate(env_data, 1):
if len(start_loc) == 0 or len(des_loc) != 0:
if 1 in value:
start_loc = (index, value.index(1) + 1)
if 3 in value:
des_loc = (index, value.index(3) + 1)
else:
break
複製代碼
def valid_actions(loc):
""" :param loc: :return: 當前位置全部可用的命令 """
loc_actions = []
for order in orders:
if is_move_valid(loc, order):
loc_actions.append(order)
return loc_actions
def is_move_valid(loc, act):
""" 判斷當前點,是否可以使用此移動命令 """
x = loc[0] - 1
y = loc[1] - 1
if act not in orders:
return false
else:
if act == orders[0]:
return x != 0 and env_data[x - 1][y] != 2
elif act == orders[1]:
return x != len(env_data) - 1 and env_data[x + 1][y] != 2
elif act == orders[2]:
return y != 0 and env_data[x][y - 1] != 2
else:
return y != len(env_data[0]) - 1 and env_data[x][y + 1] != 2
複製代碼
1
的全部可到達點,不包括此節點:def get_all_valid_loc(loc):
""" 計算當前點,附近全部可用的點 :param loc: :return: """
all_valid_data = []
cur_acts = valid_actions(loc)
for act in cur_acts:
all_valid_data.append(move_robot(loc, act))
if loc in all_valid_data:
all_valid_data.remove(loc)
return all_valid_data
def move_robot(loc, act):
""" 移動機器人,返回新位置 :param loc: :param act: :return: """
if is_move_valid(loc, act):
if act == orders[0]:
return loc[0] - 1, loc[1]
elif act == orders[1]:
return loc[0] + 1, loc[1]
elif act == orders[2]:
return loc[0], loc[1] - 1
else:
return loc[0], loc[1] + 1
else:
return loc
複製代碼
h(n)
函數體現:def compute_cost(loc):
""" 計算loc到終點消耗的代價 :param loc: :return: """
return min(abs(loc[0] - des_loc[0]), abs(loc[1] - des_loc[1]))
複製代碼
使用road_list
來保存走過的路徑,同時用另外一個集合保存失敗的節點——即此節點附近無可用節點,死衚衕。
spa
# 已經走過的路徑list,走過的路
road_list = [start_loc]
# 證明是失敗的路徑
failed_list = []
# 沒有到達終點就一直循環
while road_list[len(road_list) - 1] != des_loc:
if len(road_list) == 0:
print("迷宮無解")
break
# 當前點
cur_loc = road_list[len(road_list) - 1]
# 當前點四周全部可用點
valid_loc_data = get_all_valid_loc(cur_loc)
# 若是可用點裏包括已經走過的節點,則移除
for cl in road_list:
if cl in valid_loc_data:
valid_loc_data.remove(cl)
# 若是可用點集合包括失敗的節點,則移除
for fl in failed_list:
if fl in valid_loc_data:
valid_loc_data.remove(fl)
# 沒有可用點,視做失敗,放棄該節點。從走過的路集合中移除掉
if len(valid_loc_data) == 0:
failed_list.append(road_list.pop())
continue
# 用評估函數對可用點集合排序,取末端的值,加入走過的路集合中
valid_loc_data.sort(key=compute_cost, reverse=True)
road_list.append(valid_loc_data.pop())
複製代碼
人生苦短,我用Python
。代碼地址在此Maze,喜歡Python
的小可愛們能夠拿去練練手。
在研究迷宮的過程當中,發現生成迷宮的算法也是頗有意思的,等忙完這段時間再去研究研究。嘻~~~~~
以上