写了个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效率要差些。