寫了個A*算法的lua版本,也參考了不少網上資料還有開源的代碼,關於寫這個的原因,只是在做一個rts的尋路,寫了個lua版本也是圖省事,想盡快看效果。出於程序員的好奇和執着,修改了好幾個版本,優化算法,提高了一些效率,當然這裏還有優化的餘地,就不做進一步深入了,畢竟時間有限,優化的結果也是很明顯的。
關於A*算法的基本原理,可以參考一個很好教程:http://www.policyalmanac.org/games/aStarTutorial.htm
優化
關於A*算法的優化,無非以下二點:
- openlist、closelist的查找
- openlist中獲得最優節點
這裏優化的方向有針對性,不同需要有不同的優化策略。這裏我們研究的是針對2維地圖的優化方案。
解決第一點,是要有好的查找效率,簡單的可以用hash來加速查找。當然lua裏直接map就可以提升不少速度:
local idx = self:getIndex(row, col)
local curNode = self:getNode(node, row, col, (row ~= node.row and col ~= node.col))
local openNode = self.open_list[idx]
local closeNode = self.close_list[idx]
if not openNode and not closeNode then
-- 不在OPEN表和CLOSE表中
self.open_list[idx] = curNode
elseif openNode then
-- 在OPEN表
if openNode.f > curNode.f then
-- 更新OPEN表中的節點
self.open_list[idx] = curNode
end
else
-- 在CLOSE表中
if closeNode.f > curNode.f then
self.open_list[idx] = curNode
self.close_list[idx] = nil
end
end
解決第二點,是要讓openlist更快的查找最小f值節點,這裏使用更快的heap,代碼參考https://github.com/Yonaba/Binary-Heaps:
-- start node
local idx = self:getIndex(self.startPoint.row, self.startPoint.col)
self.open_list:push(self.nodes[idx])
-- walkable
local check = function(row, col)
...
end
local dir = self.four_dir and four_dir or eight_dir
while not self.open_list:empty() do
-- check
local node = self.open_list:pop()
node.state = 2
if node.row == self.endPoint.row and node.col == self.endPoint.col then
-- found
end
for i = 1, #dir do
local row = node.row + dir[i][1]
local col = node.col + dir[i][2]
local idx = self:getIndex(row, col)
if check(row, col) then
local g, h, f = self:getCost(node, row, col, ...)
local newNode = self.nodes[idx]
if newNode.state == 0 then
-- add new node
newNode.father = node
newNode.g = g
newNode.h = h
newNode.f = f
newNode.state = 1
self.open_list:push(newNode)
elseif newNode.state == 1 then
-- alreay in openlist
if newNode.f > f then
-- a better way then update
newNode.father = node
newNode.g = g
newNode.h = h
newNode.f = f
end
else
-- in closelist
end
end
end
end
測試結果
測試環境使用cocos2dx2.2.6-lua,地圖tilemap,大小50*50,地圖信息如下:
PC - Experiment1 實驗結果:
Experiment1([1, 1] -> [1, 50]) | time (s) |
---|---|
A* - array | 0.0960~0.1030 |
A* - map | 0.0290~0.0300 |
A* - map & heap sort | 0.0020~0.0030 |
PC - Experiment2 實驗結果:
Experiment2([1, 1] -> [6, 5]) | time (s) |
---|---|
A* - array | 0.1360~0.1450 |
A* - map | 0.0420~0.0460 |
A* - map & heap sort | 0.0020~0.0030 |
Mobile - Experiment1 實驗結果:
Experiment1([1, 1] -> [1, 50]) | time (s) |
---|---|
A* - array | 1.050~1.110 |
A* - map | 0.500~0.600 |
A* - map & heap sort | 0.040~0.050 |
Mobile - Experiment2 實驗結果:
Experiment2([1, 1] -> [6, 5]) | time (s) |
---|---|
A* - array | 1.260~1.300 |
A* - map | 0.640~0.700 |
A* - map & heap sort | 0.024~0.027 |
Repository
github:adamwu
往後可能寫個c++版本的,畢竟lua效率要差些。