refactor + cleanup
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
--- A light implementation of Binary heaps data structure.
|
||||
-- While running a search, some search algorithms (Astar, Dijkstra, Jump Point Search) have to maintains
|
||||
-- a list of nodes called __open list__. Retrieve from this list the lowest cost node can be quite slow,
|
||||
-- as it normally requires to skim through the full set of nodes stored in this list. This becomes a real
|
||||
-- problem especially when dozens of nodes are being processed (on large maps).
|
||||
-- a list of nodes called __open list__. Retrieve from this list the lowest cost node can be quite slow,
|
||||
-- as it normally requires to skim through the full set of nodes stored in this list. This becomes a real
|
||||
-- problem especially when dozens of nodes are being processed (on large maps).
|
||||
--
|
||||
-- The current module implements a <a href="http://www.policyalmanac.org/games/binaryHeaps.htm">binary heap</a>
|
||||
-- data structure, from which the search algorithm will instantiate an open list, and cache the nodes being
|
||||
-- examined during a search. As such, retrieving the lower-cost node is faster and globally makes the search end
|
||||
-- data structure, from which the search algorithm will instantiate an open list, and cache the nodes being
|
||||
-- examined during a search. As such, retrieving the lower-cost node is faster and globally makes the search end
|
||||
-- up quickly.
|
||||
--
|
||||
--
|
||||
-- This module is internally used by the library on purpose.
|
||||
-- It should normally not be used explicitely, yet it remains fully accessible.
|
||||
--
|
||||
@@ -23,7 +23,7 @@ if (...) then
|
||||
|
||||
-- Dependency
|
||||
local Utils = require((...):gsub('%.bheap$','.utils'))
|
||||
|
||||
|
||||
-- Local reference
|
||||
local floor = math.floor
|
||||
|
||||
@@ -40,7 +40,7 @@ if (...) then
|
||||
else pIndex = (index-1)/2
|
||||
end
|
||||
if not heap._sort(heap._heap[pIndex], heap._heap[index]) then
|
||||
heap._heap[pIndex], heap._heap[index] =
|
||||
heap._heap[pIndex], heap._heap[index] =
|
||||
heap._heap[index], heap._heap[pIndex]
|
||||
percolate_up(heap, pIndex)
|
||||
end
|
||||
@@ -89,7 +89,7 @@ if (...) then
|
||||
-- @class function
|
||||
-- @treturn bool __true__ of no item is queued in the heap, __false__ otherwise
|
||||
-- @usage
|
||||
-- if myHeap:empty() then
|
||||
-- if myHeap:empty() then
|
||||
-- print('Heap is empty!')
|
||||
-- end
|
||||
function heap:empty()
|
||||
@@ -129,7 +129,7 @@ if (...) then
|
||||
-- @class function
|
||||
-- @treturn value a value previously pushed into the heap
|
||||
-- @usage
|
||||
-- while not myHeap:empty() do
|
||||
-- while not myHeap:empty() do
|
||||
-- local lowestValue = myHeap:pop()
|
||||
-- ...
|
||||
-- end
|
||||
@@ -148,18 +148,18 @@ if (...) then
|
||||
end
|
||||
|
||||
--- Restores the `heap` property.
|
||||
-- Reorders the `heap` with respect to the comparison function being used.
|
||||
-- When given argument __item__ (a value existing in the `heap`), will sort from that very item in the `heap`.
|
||||
-- Otherwise, the whole `heap` will be cheacked.
|
||||
-- Reorders the `heap` with respect to the comparison function being used.
|
||||
-- When given argument __item__ (a value existing in the `heap`), will sort from that very item in the `heap`.
|
||||
-- Otherwise, the whole `heap` will be cheacked.
|
||||
-- @class function
|
||||
-- @tparam[opt] value item the modified value
|
||||
-- @treturn heap self (the calling `heap` itself, can be chained)
|
||||
-- @usage myHeap:heapify()
|
||||
-- @usage myHeap:heapify()
|
||||
function heap:heapify(item)
|
||||
if self._size == 0 then return end
|
||||
if item then
|
||||
local i = Utils.indexOf(self._heap,item)
|
||||
if i then
|
||||
if i then
|
||||
percolate_down(self, i)
|
||||
percolate_up(self, i)
|
||||
end
|
||||
|
||||
@@ -7,59 +7,26 @@
|
||||
-- made with regards of their `f` cost. From a given node being examined, the `pathfinder` will expand the search
|
||||
-- to the next neighbouring node having the lowest `f` cost. See `core.bheap` for more details.
|
||||
--
|
||||
|
||||
if (...) then
|
||||
|
||||
--- The `Node` class.<br/>
|
||||
-- This class is callable.
|
||||
-- Therefore,_ <code>Node(...)</code> _acts as a shortcut to_ <code>Node:new(...)</code>.
|
||||
-- @type Node
|
||||
local Node = {}
|
||||
Node.__index = Node
|
||||
|
||||
--- Inits a new `node`
|
||||
-- @class function
|
||||
-- @tparam int x the x-coordinate of the node on the collision map
|
||||
-- @tparam int y the y-coordinate of the node on the collision map
|
||||
-- @treturn node a new `node`
|
||||
-- @usage local node = Node(3,4)
|
||||
function Node:new(x,y,z)
|
||||
return setmetatable({_x = x, _y = y, _z = z }, Node)
|
||||
return setmetatable({x = x, y = y, z = z }, Node)
|
||||
end
|
||||
|
||||
-- Enables the use of operator '<' to compare nodes.
|
||||
-- Will be used to sort a collection of nodes in a binary heap on the basis of their F-cost
|
||||
function Node.__lt(A,B) return (A._f < B._f) end
|
||||
|
||||
--- Returns x-coordinate of a `node`
|
||||
-- @class function
|
||||
-- @treturn number the x-coordinate of the `node`
|
||||
-- @usage local x = node:getX()
|
||||
function Node:getX() return self._x end
|
||||
|
||||
--- Returns y-coordinate of a `node`
|
||||
-- @class function
|
||||
-- @treturn number the y-coordinate of the `node`
|
||||
-- @usage local y = node:getY()
|
||||
function Node:getY() return self._y end
|
||||
|
||||
function Node:getZ() return self._z end
|
||||
|
||||
--- Returns x and y coordinates of a `node`
|
||||
-- @class function
|
||||
-- @treturn number the x-coordinate of the `node`
|
||||
-- @treturn number the y-coordinate of the `node`
|
||||
-- @usage local x, y = node:getPos()
|
||||
function Node:getPos() return self._x, self._y, self._z end
|
||||
function Node:getX() return self.x end
|
||||
function Node:getY() return self.y end
|
||||
function Node:getZ() return self.z end
|
||||
|
||||
--- Clears temporary cached attributes of a `node`.
|
||||
-- Deletes the attributes cached within a given node after a pathfinding call.
|
||||
-- This function is internally used by the search algorithms, so you should not use it explicitely.
|
||||
-- @class function
|
||||
-- @treturn node self (the calling `node` itself, can be chained)
|
||||
-- @usage
|
||||
-- local thisNode = Node(1,2)
|
||||
-- thisNode:reset()
|
||||
function Node:reset()
|
||||
self._g, self._h, self._f = nil, nil, nil
|
||||
self._opened, self._closed, self._parent = nil, nil, nil
|
||||
|
||||
@@ -7,27 +7,18 @@
|
||||
--
|
||||
|
||||
if (...) then
|
||||
--- The `Path` class.<br/>
|
||||
-- This class is callable.
|
||||
-- Therefore, <em><code>Path(...)</code></em> acts as a shortcut to <em><code>Path:new(...)</code></em>.
|
||||
-- @type Path
|
||||
|
||||
local t_remove = table.remove
|
||||
|
||||
local Path = {}
|
||||
Path.__index = Path
|
||||
|
||||
--- Inits a new `path`.
|
||||
-- @class function
|
||||
-- @treturn path a `path`
|
||||
-- @usage local p = Path()
|
||||
function Path:new()
|
||||
return setmetatable({_nodes = {}}, Path)
|
||||
end
|
||||
|
||||
--- Iterates on each single `node` along a `path`. At each step of iteration,
|
||||
-- returns the `node` plus a count value. Aliased as @{Path:nodes}
|
||||
-- @class function
|
||||
-- @treturn node a `node`
|
||||
-- @treturn int the count for the number of nodes
|
||||
-- @see Path:nodes
|
||||
-- @usage
|
||||
-- for node, count in p:iter() do
|
||||
-- ...
|
||||
@@ -42,6 +33,32 @@ if (...) then
|
||||
end
|
||||
end
|
||||
|
||||
--- `Path` compression modifier. Given a `path`, eliminates useless nodes to return a lighter `path`
|
||||
-- consisting of straight moves. Does the opposite of @{Path:fill}
|
||||
-- @class function
|
||||
-- @treturn path self (the calling `path` itself, can be chained)
|
||||
-- @see Path:fill
|
||||
-- @usage p:filter()
|
||||
function Path:filter()
|
||||
local i = 2
|
||||
local xi,yi,zi,dx,dy,dz, olddx, olddy, olddz
|
||||
xi,yi,zi = self._nodes[i].x, self._nodes[i].y, self._nodes[i].z
|
||||
dx, dy,dz = xi - self._nodes[i-1].x, yi-self._nodes[i-1].y, zi-self._nodes[i-1].z
|
||||
while true do
|
||||
olddx, olddy, olddz = dx, dy, dz
|
||||
if self._nodes[i+1] then
|
||||
i = i+1
|
||||
xi, yi, zi = self._nodes[i].x, self._nodes[i].y, self._nodes[i].z
|
||||
dx, dy, dz = xi - self._nodes[i-1].x, yi - self._nodes[i-1].y, zi - self._nodes[i-1].z
|
||||
if olddx == dx and olddy == dy and olddz == dz then
|
||||
t_remove(self._nodes, i-1)
|
||||
i = i - 1
|
||||
end
|
||||
else break end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
return setmetatable(Path,
|
||||
{__call = function(_,...)
|
||||
return Path:new(...)
|
||||
|
||||
@@ -25,18 +25,9 @@ if (...) then
|
||||
{x = 0, y = 0, z = 1} --[[U]], {x = 0, y = -0, z = -1}, --[[D]]
|
||||
}
|
||||
|
||||
--- The `Grid` class.<br/>
|
||||
-- This class is callable.
|
||||
-- Therefore,_ <code>Grid(...)</code> _acts as a shortcut to_ <code>Grid:new(...)</code>.
|
||||
-- @type Grid
|
||||
local Grid = {}
|
||||
Grid.__index = Grid
|
||||
|
||||
--- Inits a new `grid`
|
||||
-- @class function
|
||||
-- @tparam table Map dimensions
|
||||
-- or a `string` with line-break chars (<code>\n</code> or <code>\r</code>) as row delimiters.
|
||||
-- @treturn grid a new `grid` instance
|
||||
function Grid:new(dim)
|
||||
local newGrid = { }
|
||||
newGrid._min_x, newGrid._max_x = dim.x, dim.ex
|
||||
@@ -49,73 +40,38 @@ if (...) then
|
||||
return setmetatable(newGrid,Grid)
|
||||
end
|
||||
|
||||
--- Checks if `node` at [x,y] is __walkable__.
|
||||
-- Will check if `node` at location [x,y] both *exists* on the collision map and *is walkable*
|
||||
-- @class function
|
||||
-- @tparam int x the x-location of the node
|
||||
-- @tparam int y the y-location of the node
|
||||
-- @tparam int z the z-location of the node
|
||||
--
|
||||
function Grid:isWalkableAt(x, y, z)
|
||||
local node = self:getNodeAt(x,y,z)
|
||||
return node and node.walkable ~= 1
|
||||
end
|
||||
|
||||
--- Returns the `grid` width.
|
||||
-- @class function
|
||||
-- @treturn int the `grid` width
|
||||
-- @usage print(myGrid:getWidth())
|
||||
function Grid:getWidth()
|
||||
return self._width
|
||||
end
|
||||
|
||||
--- Returns the `grid` height.
|
||||
-- @class function
|
||||
-- @treturn int the `grid` height
|
||||
-- @usage print(myGrid:getHeight())
|
||||
function Grid:getHeight()
|
||||
return self._height
|
||||
end
|
||||
|
||||
--- Returns the set of nodes.
|
||||
-- @class function
|
||||
-- @treturn {{node,...},...} an array of nodes
|
||||
-- @usage local nodes = myGrid:getNodes()
|
||||
function Grid:getNodes()
|
||||
return self._nodes
|
||||
end
|
||||
|
||||
--- Returns the `grid` bounds. Returned values corresponds to the upper-left
|
||||
-- and lower-right coordinates (in tile units) of the actual `grid` instance.
|
||||
-- @class function
|
||||
-- @treturn int the upper-left corner x-coordinate
|
||||
-- @treturn int the upper-left corner y-coordinate
|
||||
-- @treturn int the lower-right corner x-coordinate
|
||||
-- @treturn int the lower-right corner y-coordinate
|
||||
-- @usage local left_x, left_y, right_x, right_y = myGrid:getBounds()
|
||||
function Grid:getBounds()
|
||||
return self._min_x, self._min_y, self._min_z, self._max_x, self._max_y, self._max_z
|
||||
end
|
||||
|
||||
--- Returns neighbours. The returned value is an array of __walkable__ nodes neighbouring a given `node`.
|
||||
-- @class function
|
||||
-- @tparam node node a given `node`
|
||||
-- @tparam[opt] string|int|func walkable the value for walkable locations
|
||||
-- in the collision map array (see @{Grid:new}).
|
||||
-- Defaults to __false__ when omitted.
|
||||
-- @treturn {node,...} an array of nodes neighbouring a given node
|
||||
-- @usage
|
||||
-- local aNode = myGrid:getNodeAt(5,6)
|
||||
-- local neighbours = myGrid:getNeighbours(aNode, 0, true)
|
||||
function Grid:getNeighbours(node)
|
||||
local neighbours = {}
|
||||
for i = 1,#straightOffsets do
|
||||
local n = self:getNodeAt(
|
||||
node._x + straightOffsets[i].x,
|
||||
node._y + straightOffsets[i].y,
|
||||
node._z + straightOffsets[i].z
|
||||
node.x + straightOffsets[i].x,
|
||||
node.y + straightOffsets[i].y,
|
||||
node.z + straightOffsets[i].z
|
||||
)
|
||||
if n and self:isWalkableAt(n._x, n._y, n._z) then
|
||||
if n and self:isWalkableAt(n.x, n.y, n.z) then
|
||||
neighbours[#neighbours+1] = n
|
||||
end
|
||||
end
|
||||
@@ -123,17 +79,7 @@ if (...) then
|
||||
return neighbours
|
||||
end
|
||||
|
||||
--- Returns the `node` at location [x,y,z].
|
||||
-- @class function
|
||||
-- @name Grid:getNodeAt
|
||||
-- @tparam int x the x-coordinate coordinate
|
||||
-- @tparam int y the y-coordinate coordinate
|
||||
-- @tparam int z the z-coordinate coordinate
|
||||
-- @treturn node a `node`
|
||||
-- @usage local aNode = myGrid:getNodeAt(2,2)
|
||||
|
||||
-- Gets the node at location <x,y> on a preprocessed grid
|
||||
function Grid:getNodeAt(x,y,z)
|
||||
function Grid:getNodeAt(x,y,z)
|
||||
if not x or not y or not z then return end
|
||||
if Utils.outOfRange(x,self._min_x,self._max_x) then return end
|
||||
if Utils.outOfRange(y,self._min_y,self._max_y) then return end
|
||||
|
||||
@@ -28,11 +28,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
--]]
|
||||
|
||||
--- The Pathfinder class
|
||||
|
||||
--
|
||||
-- Implementation of the `pathfinder` class.
|
||||
|
||||
local _VERSION = ""
|
||||
local _RELEASEDATE = ""
|
||||
|
||||
@@ -49,8 +44,6 @@ if (...) then
|
||||
|
||||
--- Finders (search algorithms implemented). Refers to the search algorithms actually implemented in Jumper.
|
||||
-- <li>[A*](http://en.wikipedia.org/wiki/A*_search_algorithm)</li>
|
||||
-- @finder Finders
|
||||
-- @see Pathfinder:getFinders
|
||||
local Finders = {
|
||||
['ASTAR'] = require (_PATH .. '.search.astar'),
|
||||
}
|
||||
@@ -62,21 +55,9 @@ if (...) then
|
||||
-- Performs a traceback from the goal node to the start node
|
||||
-- Only happens when the path was found
|
||||
|
||||
--- The `Pathfinder` class.<br/>
|
||||
-- This class is callable.
|
||||
-- Therefore,_ <code>Pathfinder(...)</code> _acts as a shortcut to_ <code>Pathfinder:new(...)</code>.
|
||||
-- @type Pathfinder
|
||||
local Pathfinder = {}
|
||||
Pathfinder.__index = Pathfinder
|
||||
|
||||
--- Inits a new `pathfinder`
|
||||
-- @class function
|
||||
-- @tparam grid grid a `grid`
|
||||
-- @tparam[opt] string finderName the name of the `Finder` (search algorithm) to be used for search.
|
||||
-- Defaults to `ASTAR` when not given (see @{Pathfinder:getFinders}).
|
||||
-- @treturn pathfinder a new `pathfinder` instance
|
||||
-- @usage
|
||||
-- local finder = Pathfinder:new(myGrid, 'ASTAR')
|
||||
function Pathfinder:new(heuristic)
|
||||
local newPathfinder = {}
|
||||
setmetatable(newPathfinder, Pathfinder)
|
||||
@@ -85,23 +66,13 @@ if (...) then
|
||||
return newPathfinder
|
||||
end
|
||||
|
||||
--- Sets the `grid`. Defines the given `grid` as the one on which the `pathfinder` will perform the search.
|
||||
-- @class function
|
||||
-- @tparam grid grid a `grid`
|
||||
-- @treturn pathfinder self (the calling `pathfinder` itself, can be chained)
|
||||
-- @usage myFinder:setGrid(myGrid)
|
||||
function Pathfinder:setGrid(grid)
|
||||
self._grid = grid
|
||||
return self
|
||||
end
|
||||
|
||||
--- Calculates a `path`. Returns the `path` from location __[startX, startY]__ to location __[endX, endY]__.
|
||||
--- Calculates a `path`. Returns the `path` from start to end location
|
||||
-- Both locations must exist on the collision map. The starting location can be unwalkable.
|
||||
-- @class function
|
||||
-- @tparam int startX the x-coordinate for the starting location
|
||||
-- @tparam int startY the y-coordinate for the starting location
|
||||
-- @tparam int endX the x-coordinate for the goal location
|
||||
-- @tparam int endY the y-coordinate for the goal location
|
||||
-- @treturn path a path (array of nodes) when found, otherwise nil
|
||||
-- @usage local path = myFinder:getPath(1,1,5,5)
|
||||
function Pathfinder:getPath(startX, startY, startZ, ih, endX, endY, endZ, oh)
|
||||
@@ -112,8 +83,8 @@ if (...) then
|
||||
return nil
|
||||
end
|
||||
|
||||
startNode._heading = ih
|
||||
endNode._heading = oh
|
||||
startNode.heading = ih
|
||||
endNode.heading = oh
|
||||
|
||||
assert(startNode, ('Invalid location [%d, %d, %d]'):format(startX, startY, startZ))
|
||||
assert(endNode and self._grid:isWalkableAt(endX, endY, endZ),
|
||||
|
||||
@@ -18,7 +18,7 @@ if (...) then
|
||||
if node._g + mCost < neighbour._g then
|
||||
neighbour._parent = node
|
||||
neighbour._g = node._g + mCost
|
||||
neighbour._heading = heading
|
||||
neighbour.heading = heading
|
||||
end
|
||||
end
|
||||
|
||||
@@ -64,10 +64,10 @@ if (...) then
|
||||
end
|
||||
|
||||
--[[
|
||||
printf('x:%d y:%d z:%d g:%d', node._x, node._y, node._z, node._g)
|
||||
printf('x:%d y:%d z:%d g:%d', node.x, node.y, node.z, node._g)
|
||||
for i = 1,#neighbours do
|
||||
local n = neighbours[i]
|
||||
printf('x:%d y:%d z:%d f:%f g:%f h:%d', n._x, n._y, n._z, n._f, n._g, n._heading or -1)
|
||||
printf('x:%d y:%d z:%d f:%f g:%f h:%d', n.x, n.y, n.z, n._f, n._g, n.heading or -1)
|
||||
end
|
||||
--]]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user