首先本文並不打算詳細的介紹A*算法,要想大致的了解A*算法可參看下面兩篇文章:
http://wenku.baidu.com/view/d39faba1284ac850ad02425d.html
http://wenku.baidu.com/view/eaa14f11f18583d049645992.html
其次,不用太糾結算法的效率,例如remove_min_pnode函數使用線性探索尋找最小值,實際上可以使用
二叉堆或別的方法提高執行效率.
本文的目的是提供一個較通用的A*框架,用於解決游戲中的尋路問題.
首先看下結構的定義:
//一個地圖塊節點 struct map_node{}; //路徑節點 struct path_node { struct list_node l_node; struct double_link_node _open_list_node; struct double_link_node _close_list_node; struct path_node *parent; struct map_node *_map_node; double G;//從初始點到當前點的開銷 double H;//從當前點到目標點的估計開銷 double F; };
map_node用於表示地圖上一塊區域節點,用戶可繼承自map_node並實現自己的地圖表示,path_node表示一個路徑搜索節點,通過parent連接在
一起,當路徑被搜索到之后,可通過parent從目標節點遍歷到起始節點。
將地圖表示與路徑表示分離的最主要目的是,使應用可以在同一個地圖表示上高效的開啟多線程執行路徑搜索任務.
下面再看下A*搜索過程:
//由使用者提供的3個函數 //get_neighbors約定:如果一個map_node*是阻擋點,將不會被返回 typedef struct map_node** (*get_neighbors)(struct map_node*); typedef double (*cost_2_neighbor)(struct path_node*,struct path_node*); typedef double (*cost_2_goal)(struct path_node*,struct path_node*); //一次路徑搜索的過程對象 struct A_star_procedure { get_neighbors _get_neighbors; cost_2_neighbor _cost_2_neighbor;//用於計算兩個路徑點G值的函數指針 cost_2_goal _cost_2_goal;//用於計算兩個路徑點H值的函數指針 struct double_link open_list; struct double_link close_list; hash_map_t mnode_2_pnode;//map_node到path_node的映射 struct link_list *pnodes;//所有臨時path_node列表 }; struct A_star_procedure *create_astar(get_neighbors,cost_2_neighbor,cost_2_goal); //尋找從from到to的路徑,找到返回路徑點,否則返回NULL struct path_node* find_path(struct A_star_procedure *astar,struct map_node *from,struct map_node *to); void destroy_Astar(struct A_star_procedure**);
A_star_procedure代表了一個A*搜索過程,創建這個過程的時候,用戶需要提供三個回調函數:
get_neighbors:用於獲得一個節點的臨接節點,這里約定,如果一個節點是阻擋點,那么不被認為是臨接節點.
例如:對於一個用格子表示的地圖,其臨接節點可以是當前格子周圍的8個格子,如果節點是一個3角形,
則其臨接節點是於其相接的其它三角形.
cost_2_neighbor:用於計算從當前節點到其臨接節點的代價.
cost_2_goal:用於估算從當前節點到達目標節點的代價.
用戶定義好這三個函數之后,調用create_astar創建一個搜索過程對象,然后通過find_path搜索從開始點到目標點的路徑.
下面提供了兩個測試用例,在一個用格子表示的迷宮中搜索路徑和解決8碼問題:
https://github.com/sniperHW/kendylib/blob/master/Astar/testmaze.c
https://github.com/sniperHW/kendylib/blob/master/Astar/8puzzle.c
對於8碼問題,預先判斷兩個狀態是否可達的可參看: