move apis into rom/modules/main for shell compatibility
This commit is contained in:
175
sys/modules/opus/jumper/core/bheap.lua
Normal file
175
sys/modules/opus/jumper/core/bheap.lua
Normal file
@@ -0,0 +1,175 @@
|
||||
--- 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).
|
||||
--
|
||||
-- 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
|
||||
-- up quickly.
|
||||
--
|
||||
-- This module is internally used by the library on purpose.
|
||||
-- It should normally not be used explicitely, yet it remains fully accessible.
|
||||
--
|
||||
|
||||
--[[
|
||||
Notes:
|
||||
This lighter implementation of binary heaps, based on :
|
||||
https://github.com/Yonaba/Binary-Heaps
|
||||
--]]
|
||||
|
||||
if (...) then
|
||||
|
||||
-- Dependency
|
||||
local Utils = require((...):gsub('%.bheap$','.utils'))
|
||||
|
||||
-- Local reference
|
||||
local floor = math.floor
|
||||
|
||||
-- Default comparison function
|
||||
local function f_min(a,b) return a < b end
|
||||
|
||||
-- Percolates up
|
||||
local function percolate_up(heap, index)
|
||||
if index == 1 then return end
|
||||
local pIndex
|
||||
if index <= 1 then return end
|
||||
if index%2 == 0 then
|
||||
pIndex = index/2
|
||||
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[index], heap._heap[pIndex]
|
||||
percolate_up(heap, pIndex)
|
||||
end
|
||||
end
|
||||
|
||||
-- Percolates down
|
||||
local function percolate_down(heap,index)
|
||||
local lfIndex,rtIndex,minIndex
|
||||
lfIndex = 2*index
|
||||
rtIndex = lfIndex + 1
|
||||
if rtIndex > heap._size then
|
||||
if lfIndex > heap._size then return
|
||||
else minIndex = lfIndex end
|
||||
else
|
||||
if heap._sort(heap._heap[lfIndex],heap._heap[rtIndex]) then
|
||||
minIndex = lfIndex
|
||||
else
|
||||
minIndex = rtIndex
|
||||
end
|
||||
end
|
||||
if not heap._sort(heap._heap[index],heap._heap[minIndex]) then
|
||||
heap._heap[index],heap._heap[minIndex] = heap._heap[minIndex],heap._heap[index]
|
||||
percolate_down(heap,minIndex)
|
||||
end
|
||||
end
|
||||
|
||||
-- Produces a new heap
|
||||
local function newHeap(template,comp)
|
||||
return setmetatable({_heap = {},
|
||||
_sort = comp or f_min, _size = 0},
|
||||
template)
|
||||
end
|
||||
|
||||
|
||||
--- The `heap` class.<br/>
|
||||
-- This class is callable.
|
||||
-- _Therefore,_ <code>heap(...)</code> _is used to instantiate new heaps_.
|
||||
-- @type heap
|
||||
local heap = setmetatable({},
|
||||
{__call = function(self,...)
|
||||
return newHeap(self,...)
|
||||
end})
|
||||
heap.__index = heap
|
||||
|
||||
--- Checks if a `heap` is empty
|
||||
-- @class function
|
||||
-- @treturn bool __true__ of no item is queued in the heap, __false__ otherwise
|
||||
-- @usage
|
||||
-- if myHeap:empty() then
|
||||
-- print('Heap is empty!')
|
||||
-- end
|
||||
function heap:empty()
|
||||
return (self._size==0)
|
||||
end
|
||||
|
||||
--- Clears the `heap` (removes all items queued in the heap)
|
||||
-- @class function
|
||||
-- @treturn heap self (the calling `heap` itself, can be chained)
|
||||
-- @usage myHeap:clear()
|
||||
function heap:clear()
|
||||
self._heap = {}
|
||||
self._size = 0
|
||||
self._sort = self._sort or f_min
|
||||
return self
|
||||
end
|
||||
|
||||
--- Adds a new item in the `heap`
|
||||
-- @class function
|
||||
-- @tparam value item a new value to be queued in the heap
|
||||
-- @treturn heap self (the calling `heap` itself, can be chained)
|
||||
-- @usage
|
||||
-- myHeap:push(1)
|
||||
-- -- or, with chaining
|
||||
-- myHeap:push(1):push(2):push(4)
|
||||
function heap:push(item)
|
||||
if item then
|
||||
self._size = self._size + 1
|
||||
self._heap[self._size] = item
|
||||
percolate_up(self, self._size)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Pops from the `heap`.
|
||||
-- Removes and returns the lowest cost item (with respect to the comparison function being used) from the `heap`.
|
||||
-- @class function
|
||||
-- @treturn value a value previously pushed into the heap
|
||||
-- @usage
|
||||
-- while not myHeap:empty() do
|
||||
-- local lowestValue = myHeap:pop()
|
||||
-- ...
|
||||
-- end
|
||||
function heap:pop()
|
||||
local root
|
||||
if self._size > 0 then
|
||||
root = self._heap[1]
|
||||
self._heap[1] = self._heap[self._size]
|
||||
self._heap[self._size] = nil
|
||||
self._size = self._size-1
|
||||
if self._size>1 then
|
||||
percolate_down(self, 1)
|
||||
end
|
||||
end
|
||||
return root
|
||||
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.
|
||||
-- @class function
|
||||
-- @tparam[opt] value item the modified value
|
||||
-- @treturn heap self (the calling `heap` itself, can be chained)
|
||||
-- @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
|
||||
percolate_down(self, i)
|
||||
percolate_up(self, i)
|
||||
end
|
||||
return
|
||||
end
|
||||
for i = floor(self._size/2),1,-1 do
|
||||
percolate_down(self,i)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
return heap
|
||||
end
|
||||
41
sys/modules/opus/jumper/core/node.lua
Normal file
41
sys/modules/opus/jumper/core/node.lua
Normal file
@@ -0,0 +1,41 @@
|
||||
--- The Node class.
|
||||
-- The `node` represents a cell (or a tile) on a collision map. Basically, for each single cell (tile)
|
||||
-- in the collision map passed-in upon initialization, a `node` object will be generated
|
||||
-- and then cached within the `grid`.
|
||||
--
|
||||
-- In the following implementation, nodes can be compared using the `<` operator. The comparison is
|
||||
-- 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
|
||||
|
||||
local Node = {}
|
||||
Node.__index = Node
|
||||
|
||||
function Node:new(x,y,z)
|
||||
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
|
||||
|
||||
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.
|
||||
function Node:reset()
|
||||
self._g, self._h, self._f = nil, nil, nil
|
||||
self._opened, self._closed, self._parent = nil, nil, nil
|
||||
return self
|
||||
end
|
||||
|
||||
return setmetatable(Node,
|
||||
{__call = function(_,...)
|
||||
return Node:new(...)
|
||||
end}
|
||||
)
|
||||
end
|
||||
67
sys/modules/opus/jumper/core/path.lua
Normal file
67
sys/modules/opus/jumper/core/path.lua
Normal file
@@ -0,0 +1,67 @@
|
||||
--- The Path class.
|
||||
-- The `path` class is a structure which represents a path (ordered set of nodes) from a start location to a goal.
|
||||
-- An instance from this class would be a result of a request addressed to `Pathfinder:getPath`.
|
||||
--
|
||||
-- This module is internally used by the library on purpose.
|
||||
-- It should normally not be used explicitely, yet it remains fully accessible.
|
||||
--
|
||||
|
||||
if (...) then
|
||||
|
||||
local t_remove = table.remove
|
||||
|
||||
local Path = {}
|
||||
Path.__index = 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}
|
||||
-- @usage
|
||||
-- for node, count in p:iter() do
|
||||
-- ...
|
||||
-- end
|
||||
function Path:nodes()
|
||||
local i = 1
|
||||
return function()
|
||||
if self._nodes[i] then
|
||||
i = i+1
|
||||
return self._nodes[i-1],i-1
|
||||
end
|
||||
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(...)
|
||||
end
|
||||
})
|
||||
end
|
||||
57
sys/modules/opus/jumper/core/utils.lua
Normal file
57
sys/modules/opus/jumper/core/utils.lua
Normal file
@@ -0,0 +1,57 @@
|
||||
-- Various utilities for Jumper top-level modules
|
||||
|
||||
if (...) then
|
||||
|
||||
-- Dependencies
|
||||
local _PATH = (...):gsub('%.utils$','')
|
||||
local Path = require (_PATH .. '.path')
|
||||
|
||||
-- Local references
|
||||
local pairs = pairs
|
||||
local t_insert = table.insert
|
||||
|
||||
-- Raw array items count
|
||||
local function arraySize(t)
|
||||
local count = 0
|
||||
for _ in pairs(t) do
|
||||
count = count+1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
-- Extract a path from a given start/end position
|
||||
local function traceBackPath(finder, node, startNode)
|
||||
local path = Path:new()
|
||||
path._grid = finder._grid
|
||||
while true do
|
||||
if node._parent then
|
||||
t_insert(path._nodes,1,node)
|
||||
node = node._parent
|
||||
else
|
||||
t_insert(path._nodes,1,startNode)
|
||||
return path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Lookup for value in a table
|
||||
local indexOf = function(t,v)
|
||||
for i = 1,#t do
|
||||
if t[i] == v then return i end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Is i out of range
|
||||
local function outOfRange(i,low,up)
|
||||
return (i< low or i > up)
|
||||
end
|
||||
|
||||
return {
|
||||
arraySize = arraySize,
|
||||
indexOf = indexOf,
|
||||
outOfRange = outOfRange,
|
||||
traceBackPath = traceBackPath
|
||||
}
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user