Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- if not pQueue then
- if not os.loadAPI("pQueue") then
- error("could not load pQueue API")
- end
- end
- -- a very basic map API used to store node information
- local mapMethods = {
- get = function(self, tVector)
- if self.map[tVector.x] and self.map[tVector.x][tVector.y] then
- return self.map[tVector.x][tVector.y][tVector.z]
- end
- end,
- set = function(self, tVector, value)
- if not self.map[tVector.x] then
- self.map[tVector.x] = {}
- end
- if not self.map[tVector.x][tVector.y] then
- self.map[tVector.x][tVector.y] = {}
- end
- self.map[tVector.x][tVector.y][tVector.z] = value
- return self.map[tVector.x][tVector.y][tVector.z]
- end,
- getOrSet = function(self, tVector, value)
- if self.map[tVector.x] and self.map[tVector.x][tVector.y] and self.map[tVector.x][tVector.y][tVector.z] ~= nil then
- return self.map[tVector.x][tVector.y][tVector.z], false
- else
- return self:set(tVector, value), true
- end
- end,
- }
- local mapMetatable = {__index = mapMethods}
- function newMap()
- return setmetatable({map = {}}, mapMetatable)
- end
- local function makePath(nodes, start, startEnd, goalStart, goal)
- local current, path = startEnd, {}
- while not vectorEquals(current, start) do
- table.insert(path, current)
- current = nodes:get(current)[1]
- end
- current = goalStart
- while not vectorEquals(current, goal) do
- table.insert(path, 1, current)
- current = nodes:get(current)[1]
- end
- table.insert(path, 1, goal)
- return path
- end
- function vectorEquals(a, b) -- the comparison function used in pQueue
- return a.x == b.x and a.y == b.y and a.z == b.z
- end
- local posZ = vector.new(0, 0, 1)
- local negX = vector.new(-1, 0, 0)
- local negZ = vector.new(0, 0, -1)
- local posX = vector.new(1, 0, 0)
- local posY = vector.new(0, 1, 0)
- local negY = vector.new(0, -1, 0)
- function adjacent(u)
- return {
- u + posZ,
- u + negX,
- u + negZ,
- u + posX,
- u + posY,
- u + negY,
- }
- end
- function distance(a, b) -- 1-norm/manhattan metric
- return math.abs(a.x - b.x) + math.abs(a.y - b.y) + math.abs(a.z - b.z)
- end
- function compute(distanceFunction, start, goal)
- if type(distanceFunction) ~= "function" then
- error("aStar new: distanceFunction must be of type function", 2)
- end
- local distanceFunc = distanceFunction -- is this necessary?
- -- node data structure is {parent node, true cost from startNode/goalNode, whether in closed list, search direction this node was found in, whether in open list}
- local nodes = newMap()
- nodes:set(start, {start + vector.new(0, 0, -1), 0, false, true, true})
- nodes:set(goal, {goal + vector.new(0, 0, -1), 0, false, false, true})
- local openStartSet = pQueue.new()
- openStartSet:insert(start, distance(start, goal))
- local openGoalSet = pQueue.new()
- openGoalSet:insert(goal, distance(start, goal))
- local yieldCount = 0
- local activeOpenSet, pendingOpenSet = openStartSet, openGoalSet
- local forwardSearch, lastNode, switch = true, false, false
- local current, currNode, parent
- local baseCost
- local newCost
- local nbrNode, newNode
- local preHeuristic
- while not openStartSet:isEmpty() and not openGoalSet:isEmpty() do
- --yield every so often to avoid getting timed out
- yieldCount = yieldCount + 1
- if yieldCount > 200 then
- os.pullEvent(os.queueEvent("yield"))
- yieldCount = 0
- end
- if switch then --switch the search direction
- activeOpenSet, pendingOpenSet = pendingOpenSet, activeOpenSet
- forwardSearch = not forwardSearch
- lastNode = false
- end
- current = activeOpenSet:pop()
- currNode = nodes:get(current)
- parent = current - currNode[1]
- currNode[3], currNode[5], switch = true, false, true
- for _, neighbour in ipairs(adjacent(current)) do
- baseCost = distanceFunc(current, neighbour)
- if baseCost < math.huge then -- if not graph:get(neighbour) then
- newCost = currNode[2] + baseCost
- nbrNode, newNode = nodes:getOrSet(neighbour, {current, newCost, false, forwardSearch, false})
- if switch and ((not lastNode) or vectorEquals(lastNode, neighbour)) then
- switch = false
- end
- if not newNode then
- if forwardSearch ~= nbrNode[4] then -- nbrNode has been discovered in the opposite search direction
- if nbrNode[3] then -- and is in the closed list so has been expanded already
- return makePath(nodes, start, (forwardSearch and current) or neighbour, (forwardSearch and neighbour) or current, goal)
- end
- elseif newCost < nbrNode[2] then
- if nbrNode[5] then
- activeOpenSet:remove(neighbour, vectorEquals)
- nbrNode[5] = false
- end
- nbrNode[3] = false
- end
- end
- if (newNode or (forwardSearch ~= nbrNode[4] and not nbrNode[5] and not nbrNode[3])) and newCost < math.huge then
- nbrNode[1] = current
- nbrNode[2] = newCost
- nbrNode[4] = currNode[4]
- nbrNode[5] = true
- preHeuristic = distance(neighbour, (forwardSearch and goal) or start)
- activeOpenSet:insert(neighbour, newCost + preHeuristic + 0.0001*(preHeuristic + parent.length(parent:cross(neighbour - current))))
- end
- end
- end
- lastNode = current
- end
- return false
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement