A*算法lua实现

转自:http://www.cocoachina.com/bbs/read.php?tid-279162.html
加了点注释方便理解

--[[
    A*寻路算法,估价值代表估计代价值
--]]

-- 行走的4个方向
local four_dir = {
    {1, 0},
    {0, 1},
    {0, -1},
    {-1, 0},
}

-- 行走的8个方向
local eight_dir = {
    {1, 1},
    {1, 0},
    {1, -1},
    {0, 1},
    {0, -1},
    {-1, 1},
    {-1, 0},
    {-1, -1}
}

local AStar = {}

-- 地图、起始点、终点
-- map 是一个二维数组,对应值为0表示可以通行,1表示障碍物
AStar.init = function(self, map, startPoint, endPoint, four_dir)
    self.startPoint = startPoint
    self.endPoint   = endPoint
    self.map        = map
    self.cost       = 10  -- 单位花费
    self.diag       = 1.4 -- 对角线长, 根号2 一位小数
    self.open_list  = {}  -- open_list表保存了所有已生成而未考察的节点 
    self.close_list = {}  -- close_list表中记录已访问过的节点
    self.mapRows    = #map
    self.mapCols    = #map[1]
    self.four_dir   = four_dir -- 使用4方向的寻路还是八方向的寻路
end

-- 搜索路径,核心代码
AStar.searchPath = function(self)
    -- 验证终点是否为阻挡,如果为阻挡,则直接返回空路径
    if self.map[self.endPoint.row][self.endPoint.col] ~= 0 then
        logf("(", self.endPoint.row, ",", self.endPoint.col, ") 是阻挡!!!无法寻路")
        return nil
    end

    -- 把出发节点加入open_list中
    local startNode = {}  
    startNode.row = self.startPoint.row
    startNode.col = self.startPoint.col
    startNode.g = 0
    startNode.h = 0
    startNode.f = 0
    table.insert(self.open_list, startNode)

    -- 检查边界、障碍点 
    local check = function(row, col)
        if 1 <= row and row <= self.mapRows and 1 <= col and col <= self.mapCols then
            if self.map[row][col] == 0 or (row == self.endPoint.row and col == self.endPoint.col) then
                return true
            end
        end

        return false
    end

    local dir = self.four_dir and four_dir or eight_dir
    while #self.open_list > 0 do
        local node = self:getMinNode()
        if node.row == self.endPoint.row and node.col == self.endPoint.col then
            -- 找到路径
            return self:buildPath(node)
        end

        for i = 1, #dir do
            local row = node.row + dir<i>[1]
            local col = node.col + dir<i>[2]
            if check(row, col) then
                local curNode = self:getNodeWithFGH(node, row, col, (row ~= node.row and col ~= node.col))
                local openNode, openIndex = self:findNodeInOpenList(row, col)
                local closeNode, closeIndex = self:findNodeInCloseList(row, col)

                if not openNode and not closeNode then
                    -- 不在OPEN表和CLOSE表中
                    -- 求出估价值并把节点加到 open list
                    table.insert(self.open_list, curNode)
                --根据估价函数重排OPEN表
                --这样循环中的每一步只考虑OPEN表中状态最好的节点。
                elseif openNode then
                    -- 已经在OPEN表中,且估价值小于OPEN表中的估价值
                    -- 更新OPEN表中的估价值
                    if openNode.f > curNode.f then
                        self.open_list[openIndex] = curNode
                    end
                else
                    -- 在CLOSE表中,且估价值小于CLOSE表中的估价值
                    -- 从CLOSE表中移除节点,并且加入OPEN表中
                    if closeNode.f > curNode.f then
                        table.insert(self.open_list, curNode)
                        table.remove(self.close_list, closeIndex)
                    end
                end
            end
        end

        -- 节点放入到 close list 里面
        table.insert(self.close_list, node)
    end

    -- 不存在路径
    return nil
end

-- 获取 f ,g ,h, 最后一个参数代表是否允许沿着对角线走
AStar.getNodeWithFGH = function(self, father, row, col, isdiag)
    local node = {}
    local cost = self.cost
    if isdiag then
        cost = cost * self.diag
    end

    node.father = father
    node.row = row
    node.col = col
    node.g = father.g + cost
    -- 估价值h
    if self.four_dir then
        node.h = self:manhattan(row, col)
    else
        node.h = self:diagonal(row, col)
    end
    node.f = node.g + node.h  -- f = g + h 
    return node
end

-- 判断节点是否已经存在 open list 里面
AStar.findNodeInOpenList = function(self, row, col)
    for i = 1, #self.open_list do
        local node = self.open_list<i>
        if node.row == row and node.col == col then
            return node, i   -- 返回节点和下标
        end
    end

    return nil
end

-- 判断节点是否已经存在 close list 里面
AStar.findNodeInCloseList = function(self, row, col)
    for i = 1, #self.close_list do
        local node = self.close_list<i>
        if node.row == row and node.col == col then
            return node, i
        end
    end

    return nil
end

-- 在open_list中找到最佳点,并删除
AStar.getMinNode = function(self)
    if #self.open_list < 1 then
        return nil
    end

    local min_node = self.open_list[1]
    local min_i = 1
    for i,v in ipairs(self.open_list) do
        if min_node.f > v.f then
            min_node = v
            min_i = i
        end
    end

    table.remove(self.open_list, min_i)
    return min_node
end

-- 计算路径
AStar.buildPath = function(self, node)
    local path = {}
    local sumCost = node.f -- 路径的总花费
    while node do
        path[#path + 1] = {row = node.row, col = node.col}
        node = node.father
    end

    return path, sumCost
end

-- 估价h函数
-- 曼哈顿估价法(用于不能对角行走)
AStar.manhattan = function(self, row, col)  
    local h = math.abs(row - self.endPoint.row) + math.abs(col - self.endPoint.col)
    return h * self.cost
end

-- 对角线估价法,先按对角线走,一直走到与终点水平或垂直平行后,再笔直的走
AStar.diagonal = function(self, row, col)
    local dx = math.abs(row - self.endPoint.row)
    local dy = math.abs(col - self.endPoint.col)
    local minD = math.min(dx, dy)
    local h = minD * self.diag + dx + dy - 2 * minD
    return h * self.cost
end

return AStar
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值