mirror of
https://github.com/kepler155c/opus
synced 2025-01-01 03:10:28 +00:00
restructure
This commit is contained in:
parent
ad447f36b5
commit
e5a5f76fb3
@ -58,6 +58,9 @@ local page = UI.Page {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
statusBar = UI.StatusBar { },
|
statusBar = UI.StatusBar { },
|
||||||
|
accelerators = {
|
||||||
|
[ 'control-q' ] = 'quit',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function page:loadPackages()
|
function page:loadPackages()
|
||||||
|
@ -47,8 +47,8 @@ local function runDir(directory)
|
|||||||
end
|
end
|
||||||
|
|
||||||
runDir('sys/autorun')
|
runDir('sys/autorun')
|
||||||
for name in pairs(Packages:installed()) do
|
for _, package in pairs(Packages:installedSorted()) do
|
||||||
local packageDir = 'packages/' .. name .. '/autorun'
|
local packageDir = 'packages/' .. package.name .. '/autorun'
|
||||||
runDir(packageDir)
|
runDir(packageDir)
|
||||||
end
|
end
|
||||||
runDir('usr/autorun')
|
runDir('usr/autorun')
|
||||||
|
@ -39,7 +39,7 @@ local function setModem(dev)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- create a psuedo-device named 'wireleess_modem'
|
-- create a psuedo-device named 'wireless_modem'
|
||||||
kernel.hook('device_attach', function(_, eventData)
|
kernel.hook('device_attach', function(_, eventData)
|
||||||
local dev = device[eventData[1]]
|
local dev = device[eventData[1]]
|
||||||
if dev and dev.type == 'modem' then
|
if dev and dev.type == 'modem' then
|
||||||
|
1273
sys/init/6.tl3.lua
1273
sys/init/6.tl3.lua
File diff suppressed because it is too large
Load Diff
247
sys/lzwfs.lua
Normal file
247
sys/lzwfs.lua
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
-- see: https://github.com/Rochet2/lualzw
|
||||||
|
-- MIT License - Copyright (c) 2016 Rochet2
|
||||||
|
|
||||||
|
-- Transparent file system compression for non-binary files using lzw
|
||||||
|
|
||||||
|
-- Files that are compressed will have the first bytes in file set to 'LZWC'.
|
||||||
|
-- If a file does not benefit from compression, the contents will not be altered.
|
||||||
|
|
||||||
|
-- Allow exclusions for files that shouldn't be compressed
|
||||||
|
-- Also allow for future types of exclusions using bit operations
|
||||||
|
-- 1 is reserved for compression exclusion
|
||||||
|
-- fs.addException('startup.lua', 1)
|
||||||
|
|
||||||
|
-- To renable compression for a file
|
||||||
|
-- fs.removeException('startup.lua', 1)
|
||||||
|
|
||||||
|
-- Restores file system
|
||||||
|
-- fs.restore()
|
||||||
|
|
||||||
|
local char = string.char
|
||||||
|
local type = type
|
||||||
|
local sub = string.sub
|
||||||
|
local tconcat = table.concat
|
||||||
|
local tinsert = table.insert
|
||||||
|
|
||||||
|
local SIGC = 'LZWC'
|
||||||
|
local IGNORE_COMPRESSION = 1 -- support other bits as well
|
||||||
|
|
||||||
|
local basedictcompress = {}
|
||||||
|
local basedictdecompress = {}
|
||||||
|
for i = 0, 255 do
|
||||||
|
local ic, iic = char(i), char(i, 0)
|
||||||
|
basedictcompress[ic] = iic
|
||||||
|
basedictdecompress[iic] = ic
|
||||||
|
end
|
||||||
|
|
||||||
|
local native = { open = fs.open }
|
||||||
|
fs.exceptions = fs.exceptions or { }
|
||||||
|
|
||||||
|
local function dictAddA(str, dict, a, b)
|
||||||
|
if a >= 256 then
|
||||||
|
a, b = 0, b+1
|
||||||
|
if b >= 256 then
|
||||||
|
dict = {}
|
||||||
|
b = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dict[str] = char(a,b)
|
||||||
|
a = a+1
|
||||||
|
return dict, a, b
|
||||||
|
end
|
||||||
|
|
||||||
|
local function compress(input)
|
||||||
|
if type(input) ~= "string" then
|
||||||
|
error ("string expected, got "..type(input))
|
||||||
|
end
|
||||||
|
local len = #input
|
||||||
|
if len <= 1 then
|
||||||
|
return input
|
||||||
|
end
|
||||||
|
|
||||||
|
local dict = {}
|
||||||
|
local a, b = 0, 1
|
||||||
|
|
||||||
|
local result = { SIGC }
|
||||||
|
local resultlen = 1
|
||||||
|
local n = 2
|
||||||
|
local word = ""
|
||||||
|
for i = 1, len do
|
||||||
|
local c = sub(input, i, i)
|
||||||
|
local wc = word..c
|
||||||
|
if not (basedictcompress[wc] or dict[wc]) then
|
||||||
|
local write = basedictcompress[word] or dict[word]
|
||||||
|
if not write then
|
||||||
|
error "algorithm error, could not fetch word"
|
||||||
|
end
|
||||||
|
result[n] = write
|
||||||
|
resultlen = resultlen + #write
|
||||||
|
n = n+1
|
||||||
|
if len <= resultlen then
|
||||||
|
return input
|
||||||
|
end
|
||||||
|
dict, a, b = dictAddA(wc, dict, a, b)
|
||||||
|
word = c
|
||||||
|
else
|
||||||
|
word = wc
|
||||||
|
end
|
||||||
|
end
|
||||||
|
result[n] = basedictcompress[word] or dict[word]
|
||||||
|
resultlen = resultlen+#result[n]
|
||||||
|
if len <= resultlen then
|
||||||
|
return input
|
||||||
|
end
|
||||||
|
return tconcat(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dictAddB(str, dict, a, b)
|
||||||
|
if a >= 256 then
|
||||||
|
a, b = 0, b+1
|
||||||
|
if b >= 256 then
|
||||||
|
dict = {}
|
||||||
|
b = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dict[char(a,b)] = str
|
||||||
|
a = a+1
|
||||||
|
return dict, a, b
|
||||||
|
end
|
||||||
|
|
||||||
|
local function decompress(input)
|
||||||
|
if type(input) ~= "string" then
|
||||||
|
error( "string expected, got "..type(input))
|
||||||
|
end
|
||||||
|
|
||||||
|
if #input <= 1 then
|
||||||
|
return input
|
||||||
|
end
|
||||||
|
|
||||||
|
local control = sub(input, 1, 4)
|
||||||
|
if control ~= SIGC then
|
||||||
|
return input
|
||||||
|
end
|
||||||
|
input = sub(input, 5)
|
||||||
|
local len = #input
|
||||||
|
|
||||||
|
if len < 2 then
|
||||||
|
error("invalid input - not a compressed string")
|
||||||
|
end
|
||||||
|
|
||||||
|
local dict = {}
|
||||||
|
local a, b = 0, 1
|
||||||
|
|
||||||
|
local result = {}
|
||||||
|
local n = 1
|
||||||
|
local last = sub(input, 1, 2)
|
||||||
|
result[n] = basedictdecompress[last] or dict[last]
|
||||||
|
n = n+1
|
||||||
|
for i = 3, len, 2 do
|
||||||
|
local code = sub(input, i, i+1)
|
||||||
|
local lastStr = basedictdecompress[last] or dict[last]
|
||||||
|
if not lastStr then
|
||||||
|
error( "could not find last from dict. Invalid input?")
|
||||||
|
end
|
||||||
|
local toAdd = basedictdecompress[code] or dict[code]
|
||||||
|
if toAdd then
|
||||||
|
result[n] = toAdd
|
||||||
|
n = n+1
|
||||||
|
dict, a, b = dictAddB(lastStr..sub(toAdd, 1, 1), dict, a, b)
|
||||||
|
else
|
||||||
|
local tmp = lastStr..sub(lastStr, 1, 1)
|
||||||
|
result[n] = tmp
|
||||||
|
n = n+1
|
||||||
|
dict, a, b = dictAddB(tmp, dict, a, b)
|
||||||
|
end
|
||||||
|
last = code
|
||||||
|
end
|
||||||
|
return tconcat(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function split(str, pattern)
|
||||||
|
pattern = pattern or "(.-)\n"
|
||||||
|
local t = {}
|
||||||
|
local function helper(line) tinsert(t, line) return "" end
|
||||||
|
helper((str:gsub(pattern, helper)))
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.open(fname, flags)
|
||||||
|
if flags == 'r' then
|
||||||
|
local f, err = native.open(fname, 'rb')
|
||||||
|
if not f then
|
||||||
|
return f, err
|
||||||
|
end
|
||||||
|
|
||||||
|
local ctr = 0
|
||||||
|
local lines
|
||||||
|
return {
|
||||||
|
readLine = function()
|
||||||
|
if not lines then
|
||||||
|
lines = split(decompress(f.readAll()))
|
||||||
|
end
|
||||||
|
ctr = ctr + 1
|
||||||
|
return lines[ctr]
|
||||||
|
end,
|
||||||
|
readAll = function()
|
||||||
|
return decompress(f.readAll())
|
||||||
|
end,
|
||||||
|
close = function()
|
||||||
|
f.close()
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
elseif flags == 'w' or flags == 'a' then
|
||||||
|
if bit.band(fs.exceptions[fs.combine(fname, '')] or 0, IGNORE_COMPRESSION) == IGNORE_COMPRESSION then
|
||||||
|
return native.open(fname, flags)
|
||||||
|
end
|
||||||
|
|
||||||
|
local c = { }
|
||||||
|
|
||||||
|
if flags == 'a' then
|
||||||
|
local f = fs.open(fname, 'r')
|
||||||
|
if f then
|
||||||
|
tinsert(c, f.readAll())
|
||||||
|
f.close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local f, err = native.open(fname, 'wb')
|
||||||
|
if not f then
|
||||||
|
return f, err
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
write = function(str)
|
||||||
|
tinsert(c, str)
|
||||||
|
end,
|
||||||
|
writeLine = function(str)
|
||||||
|
tinsert(c, str)
|
||||||
|
tinsert(c, '\n')
|
||||||
|
end,
|
||||||
|
flush = function()
|
||||||
|
-- this isn't gonna work...
|
||||||
|
// f.write(compress(tconcat(c)))
|
||||||
|
f.flush();
|
||||||
|
end,
|
||||||
|
close = function()
|
||||||
|
f.write(compress(tconcat(c)))
|
||||||
|
f.close()
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return native.open(fname, flags)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.addException(fname, mode)
|
||||||
|
fname = fs.combine(fname, '')
|
||||||
|
fs.exceptions[fname] = bit.bor(fs.exceptions[fname] or 0, mode)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.removeException(fname, mode)
|
||||||
|
fname = fs.combine(fname, '')
|
||||||
|
fs.exceptions[fname] = bit.bxor(fs.exceptions[fname] or 0, mode)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.restore()
|
||||||
|
fs.open = native.open
|
||||||
|
end
|
@ -1,175 +0,0 @@
|
|||||||
--- 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
|
|
@ -1,41 +0,0 @@
|
|||||||
--- 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
|
|
@ -1,67 +0,0 @@
|
|||||||
--- 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
|
|
@ -1,57 +0,0 @@
|
|||||||
-- 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
|
|
@ -1,101 +0,0 @@
|
|||||||
--- The Grid class.
|
|
||||||
-- Implementation of the `grid` class.
|
|
||||||
-- The `grid` is a implicit graph which represents the 2D
|
|
||||||
-- world map layout on which the `pathfinder` object will run.
|
|
||||||
-- During a search, the `pathfinder` object needs to save some critical values.
|
|
||||||
-- These values are cached within each `node`
|
|
||||||
-- object, and the whole set of nodes are tight inside the `grid` object itself.
|
|
||||||
|
|
||||||
if (...) then
|
|
||||||
|
|
||||||
-- Dependencies
|
|
||||||
local _PATH = (...):gsub('%.grid$','')
|
|
||||||
|
|
||||||
-- Local references
|
|
||||||
local Utils = require (_PATH .. '.core.utils')
|
|
||||||
local Node = require (_PATH .. '.core.node')
|
|
||||||
|
|
||||||
-- Local references
|
|
||||||
local setmetatable = setmetatable
|
|
||||||
|
|
||||||
-- Offsets for straights moves
|
|
||||||
local straightOffsets = {
|
|
||||||
{x = 1, y = 0, z = 0} --[[W]], {x = -1, y = 0, z = 0}, --[[E]]
|
|
||||||
{x = 0, y = 1, z = 0} --[[S]], {x = 0, y = -1, z = 0}, --[[N]]
|
|
||||||
{x = 0, y = 0, z = 1} --[[U]], {x = 0, y = -0, z = -1}, --[[D]]
|
|
||||||
}
|
|
||||||
|
|
||||||
local Grid = {}
|
|
||||||
Grid.__index = Grid
|
|
||||||
|
|
||||||
function Grid:new(dim)
|
|
||||||
local newGrid = { }
|
|
||||||
newGrid._min_x, newGrid._max_x = dim.x, dim.ex
|
|
||||||
newGrid._min_y, newGrid._max_y = dim.y, dim.ey
|
|
||||||
newGrid._min_z, newGrid._max_z = dim.z, dim.ez
|
|
||||||
newGrid._nodes = { }
|
|
||||||
newGrid._width = (newGrid._max_x-newGrid._min_x)+1
|
|
||||||
newGrid._height = (newGrid._max_y-newGrid._min_y)+1
|
|
||||||
newGrid._length = (newGrid._max_z-newGrid._min_z)+1
|
|
||||||
return setmetatable(newGrid,Grid)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Grid:isWalkableAt(x, y, z)
|
|
||||||
local node = self:getNodeAt(x,y,z)
|
|
||||||
return node and node.walkable ~= 1
|
|
||||||
end
|
|
||||||
|
|
||||||
function Grid:getWidth()
|
|
||||||
return self._width
|
|
||||||
end
|
|
||||||
|
|
||||||
function Grid:getHeight()
|
|
||||||
return self._height
|
|
||||||
end
|
|
||||||
|
|
||||||
function Grid:getNodes()
|
|
||||||
return self._nodes
|
|
||||||
end
|
|
||||||
|
|
||||||
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`.
|
|
||||||
-- @treturn {node,...} an array of nodes neighbouring a given node
|
|
||||||
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
|
|
||||||
)
|
|
||||||
if n and self:isWalkableAt(n.x, n.y, n.z) then
|
|
||||||
neighbours[#neighbours+1] = n
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return neighbours
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
||||||
if Utils.outOfRange(z,self._min_z,self._max_z) then return end
|
|
||||||
|
|
||||||
-- inefficient
|
|
||||||
if not self._nodes[y] then self._nodes[y] = {} end
|
|
||||||
if not self._nodes[y][x] then self._nodes[y][x] = {} end
|
|
||||||
if not self._nodes[y][x][z] then self._nodes[y][x][z] = Node:new(x,y,z) end
|
|
||||||
return self._nodes[y][x][z]
|
|
||||||
end
|
|
||||||
|
|
||||||
return setmetatable(Grid,{
|
|
||||||
__call = function(self,...)
|
|
||||||
return self:new(...)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
end
|
|
@ -1,104 +0,0 @@
|
|||||||
--[[
|
|
||||||
The following License applies to all files within the jumper directory.
|
|
||||||
|
|
||||||
Note that this is only a partial copy of the full jumper code base. Also,
|
|
||||||
the code was modified to support 3D maps.
|
|
||||||
--]]
|
|
||||||
|
|
||||||
--[[
|
|
||||||
This work is under MIT-LICENSE
|
|
||||||
Copyright (c) 2012-2013 Roland Yonaba.
|
|
||||||
|
|
||||||
-- https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
--]]
|
|
||||||
|
|
||||||
local _VERSION = ""
|
|
||||||
local _RELEASEDATE = ""
|
|
||||||
|
|
||||||
if (...) then
|
|
||||||
|
|
||||||
-- Dependencies
|
|
||||||
local _PATH = (...):gsub('%.pathfinder$','')
|
|
||||||
local Utils = require (_PATH .. '.core.utils')
|
|
||||||
|
|
||||||
-- Internalization
|
|
||||||
local pairs = pairs
|
|
||||||
local assert = assert
|
|
||||||
local setmetatable = setmetatable
|
|
||||||
|
|
||||||
--- Finders (search algorithms implemented). Refers to the search algorithms actually implemented in Jumper.
|
|
||||||
-- <li>[A*](http://en.wikipedia.org/wiki/A*_search_algorithm)</li>
|
|
||||||
local Finders = {
|
|
||||||
['ASTAR'] = require (_PATH .. '.search.astar'),
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Will keep track of all nodes expanded during the search
|
|
||||||
-- to easily reset their properties for the next pathfinding call
|
|
||||||
local toClear = {}
|
|
||||||
|
|
||||||
-- Performs a traceback from the goal node to the start node
|
|
||||||
-- Only happens when the path was found
|
|
||||||
|
|
||||||
local Pathfinder = {}
|
|
||||||
Pathfinder.__index = Pathfinder
|
|
||||||
|
|
||||||
function Pathfinder:new(heuristic)
|
|
||||||
local newPathfinder = {}
|
|
||||||
setmetatable(newPathfinder, Pathfinder)
|
|
||||||
self._finder = Finders.ASTAR
|
|
||||||
self._heuristic = heuristic
|
|
||||||
return newPathfinder
|
|
||||||
end
|
|
||||||
|
|
||||||
function Pathfinder:setGrid(grid)
|
|
||||||
self._grid = grid
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- 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.
|
|
||||||
-- @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)
|
|
||||||
self:reset()
|
|
||||||
local startNode = self._grid:getNodeAt(startX, startY, startZ)
|
|
||||||
local endNode = self._grid:getNodeAt(endX, endY, endZ)
|
|
||||||
if not startNode or not endNode then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
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),
|
|
||||||
('Invalid or unreachable location [%d, %d, %d]'):format(endX, endY, endZ))
|
|
||||||
local _endNode = self._finder(self, startNode, endNode, toClear)
|
|
||||||
if _endNode then
|
|
||||||
return Utils.traceBackPath(self, _endNode, startNode)
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Resets the `pathfinder`. This function is called internally between
|
|
||||||
-- successive pathfinding calls, so you should not
|
|
||||||
-- use it explicitely, unless under specific circumstances.
|
|
||||||
-- @class function
|
|
||||||
-- @treturn pathfinder self (the calling `pathfinder` itself, can be chained)
|
|
||||||
-- @usage local path, len = myFinder:getPath(1,1,5,5)
|
|
||||||
function Pathfinder:reset()
|
|
||||||
for node in pairs(toClear) do node:reset() end
|
|
||||||
toClear = {}
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Returns Pathfinder class
|
|
||||||
Pathfinder._VERSION = _VERSION
|
|
||||||
Pathfinder._RELEASEDATE = _RELEASEDATE
|
|
||||||
return setmetatable(Pathfinder,{
|
|
||||||
__call = function(self,...)
|
|
||||||
return self:new(...)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
@ -1,77 +0,0 @@
|
|||||||
-- Astar algorithm
|
|
||||||
-- This actual implementation of A-star is based on
|
|
||||||
-- [Nash A. & al. pseudocode](http://aigamedev.com/open/tutorials/theta-star-any-angle-paths/)
|
|
||||||
|
|
||||||
if (...) then
|
|
||||||
|
|
||||||
-- Internalization
|
|
||||||
local huge = math.huge
|
|
||||||
|
|
||||||
-- Dependancies
|
|
||||||
local _PATH = (...):match('(.+)%.search.astar$')
|
|
||||||
local Heap = require (_PATH.. '.core.bheap')
|
|
||||||
|
|
||||||
-- Updates G-cost
|
|
||||||
local function computeCost(node, neighbour, heuristic)
|
|
||||||
local mCost, heading = heuristic(neighbour, node) -- Heuristics.EUCLIDIAN(neighbour, node)
|
|
||||||
|
|
||||||
if node._g + mCost < neighbour._g then
|
|
||||||
neighbour._parent = node
|
|
||||||
neighbour._g = node._g + mCost
|
|
||||||
neighbour.heading = heading
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Updates vertex node-neighbour
|
|
||||||
local function updateVertex(openList, node, neighbour, endNode, heuristic)
|
|
||||||
local oldG = neighbour._g
|
|
||||||
computeCost(node, neighbour, heuristic)
|
|
||||||
if neighbour._g < oldG then
|
|
||||||
if neighbour._opened then neighbour._opened = false end
|
|
||||||
neighbour._h = heuristic(endNode, neighbour)
|
|
||||||
neighbour._f = neighbour._g + neighbour._h
|
|
||||||
openList:push(neighbour)
|
|
||||||
neighbour._opened = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Calculates a path.
|
|
||||||
-- Returns the path from location `<startX, startY>` to location `<endX, endY>`.
|
|
||||||
return function (finder, startNode, endNode, toClear)
|
|
||||||
local openList = Heap()
|
|
||||||
startNode._g = 0
|
|
||||||
startNode._h = finder._heuristic(endNode, startNode)
|
|
||||||
startNode._f = startNode._g + startNode._h
|
|
||||||
openList:push(startNode)
|
|
||||||
toClear[startNode] = true
|
|
||||||
startNode._opened = true
|
|
||||||
|
|
||||||
while not openList:empty() do
|
|
||||||
local node = openList:pop()
|
|
||||||
node._closed = true
|
|
||||||
if node == endNode then return node end
|
|
||||||
local neighbours = finder._grid:getNeighbours(node)
|
|
||||||
for i = 1,#neighbours do
|
|
||||||
local neighbour = neighbours[i]
|
|
||||||
if not neighbour._closed then
|
|
||||||
toClear[neighbour] = true
|
|
||||||
if not neighbour._opened then
|
|
||||||
neighbour._g = huge
|
|
||||||
neighbour._parent = nil
|
|
||||||
end
|
|
||||||
updateVertex(openList, node, neighbour, endNode, finder._heuristic)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
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)
|
|
||||||
end
|
|
||||||
--]]
|
|
||||||
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end
|
|
@ -20,6 +20,29 @@ function Packages:installed()
|
|||||||
return list
|
return list
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Packages:installedSorted()
|
||||||
|
local list = { }
|
||||||
|
|
||||||
|
for k, v in pairs(self.installed()) do
|
||||||
|
v.name = k
|
||||||
|
v.deps = { }
|
||||||
|
table.insert(list, v)
|
||||||
|
for _, v2 in pairs(v.required or { }) do
|
||||||
|
v.deps[v2] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(list, function(a, b)
|
||||||
|
return not not (b.deps and b.deps[a.name])
|
||||||
|
end)
|
||||||
|
|
||||||
|
table.sort(list, function(a, b)
|
||||||
|
return not (a.deps and a.deps[b.name])
|
||||||
|
end)
|
||||||
|
|
||||||
|
return list
|
||||||
|
end
|
||||||
|
|
||||||
function Packages:list()
|
function Packages:list()
|
||||||
if not fs.exists('usr/config/packages') then
|
if not fs.exists('usr/config/packages') then
|
||||||
self:downloadList()
|
self:downloadList()
|
||||||
|
@ -1,256 +0,0 @@
|
|||||||
local Grid = require('opus.jumper.grid')
|
|
||||||
local Pathfinder = require('opus.jumper.pathfinder')
|
|
||||||
local Point = require('opus.point')
|
|
||||||
local Util = require('opus.util')
|
|
||||||
|
|
||||||
local turtle = _G.turtle
|
|
||||||
|
|
||||||
local function addBlock(grid, b, dim)
|
|
||||||
if Point.inBox(b, dim) then
|
|
||||||
local node = grid:getNodeAt(b.x, b.y, b.z)
|
|
||||||
if node then
|
|
||||||
node.walkable = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- map shrinks/grows depending upon blocks encountered
|
|
||||||
-- the map will encompass any blocks encountered, the turtle position, and the destination
|
|
||||||
local function mapDimensions(dest, blocks, boundingBox, dests)
|
|
||||||
local box = Point.makeBox(turtle.point, turtle.point)
|
|
||||||
|
|
||||||
Point.expandBox(box, dest)
|
|
||||||
|
|
||||||
for _,d in pairs(dests) do
|
|
||||||
Point.expandBox(box, d)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _,b in pairs(blocks) do
|
|
||||||
Point.expandBox(box, b)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- expand one block out in all directions
|
|
||||||
if boundingBox then
|
|
||||||
box.x = math.max(box.x - 1, boundingBox.x)
|
|
||||||
box.z = math.max(box.z - 1, boundingBox.z)
|
|
||||||
box.y = math.max(box.y - 1, boundingBox.y)
|
|
||||||
box.ex = math.min(box.ex + 1, boundingBox.ex)
|
|
||||||
box.ez = math.min(box.ez + 1, boundingBox.ez)
|
|
||||||
box.ey = math.min(box.ey + 1, boundingBox.ey)
|
|
||||||
else
|
|
||||||
box.x = box.x - 1
|
|
||||||
box.z = box.z - 1
|
|
||||||
box.y = box.y - 1
|
|
||||||
box.ex = box.ex + 1
|
|
||||||
box.ez = box.ez + 1
|
|
||||||
box.ey = box.ey + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
return box
|
|
||||||
end
|
|
||||||
|
|
||||||
local function nodeToPoint(node)
|
|
||||||
return { x = node.x, y = node.y, z = node.z, heading = node.heading }
|
|
||||||
end
|
|
||||||
|
|
||||||
local function heuristic(n, node)
|
|
||||||
return Point.calculateMoves(node, n)
|
|
||||||
-- { x = node.x, y = node.y, z = node.z, heading = node.heading },
|
|
||||||
-- { x = n.x, y = n.y, z = n.z, heading = n.heading })
|
|
||||||
end
|
|
||||||
|
|
||||||
local function dimsAreEqual(d1, d2)
|
|
||||||
return d1.ex == d2.ex and
|
|
||||||
d1.ey == d2.ey and
|
|
||||||
d1.ez == d2.ez and
|
|
||||||
d1.x == d2.x and
|
|
||||||
d1.y == d2.y and
|
|
||||||
d1.z == d2.z
|
|
||||||
end
|
|
||||||
|
|
||||||
-- turtle sensor returns blocks in relation to the world - not turtle orientation
|
|
||||||
-- so cannot figure out block location unless we know our orientation in the world
|
|
||||||
-- really kinda dumb since it returns the coordinates as offsets of our location
|
|
||||||
-- instead of true coordinates
|
|
||||||
local function addSensorBlocks(blocks, sblocks)
|
|
||||||
for _,b in pairs(sblocks) do
|
|
||||||
if b.type ~= 'AIR' then
|
|
||||||
local pt = { x = turtle.point.x, y = turtle.point.y + b.y, z = turtle.point.z }
|
|
||||||
pt.x = pt.x - b.x
|
|
||||||
pt.z = pt.z - b.z -- this will only work if we were originally facing west
|
|
||||||
local found = false
|
|
||||||
for _,ob in pairs(blocks) do
|
|
||||||
if pt.x == ob.x and pt.y == ob.y and pt.z == ob.z then
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not found then
|
|
||||||
table.insert(blocks, pt)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function selectDestination(pts, box, grid)
|
|
||||||
while #pts > 0 do
|
|
||||||
local pt = Point.closest(turtle.point, pts)
|
|
||||||
if box and not Point.inBox(pt, box) then
|
|
||||||
Util.removeByValue(pts, pt)
|
|
||||||
else
|
|
||||||
if grid:isWalkableAt(pt.x, pt.y, pt.z) then
|
|
||||||
return pt
|
|
||||||
end
|
|
||||||
Util.removeByValue(pts, pt)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function updateCanvas(path)
|
|
||||||
local t = { }
|
|
||||||
for node in path:nodes() do
|
|
||||||
table.insert(t, { x = node.x, y = node.y, z = node.z })
|
|
||||||
end
|
|
||||||
os.queueEvent('canvas', {
|
|
||||||
type = 'canvas_path',
|
|
||||||
data = t,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function pathTo(dest, options)
|
|
||||||
local blocks = options.blocks or turtle.getState().blocks or { }
|
|
||||||
local dests = options.dest or { dest } -- support alternative destinations
|
|
||||||
local box = options.box or turtle.getState().box
|
|
||||||
local lastDim
|
|
||||||
local grid
|
|
||||||
|
|
||||||
if box then
|
|
||||||
box = Point.normalizeBox(box)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Creates a pathfinder object
|
|
||||||
local finder = Pathfinder(heuristic)
|
|
||||||
|
|
||||||
while turtle.point.x ~= dest.x or turtle.point.z ~= dest.z or turtle.point.y ~= dest.y do
|
|
||||||
|
|
||||||
-- map expands as we encounter obstacles
|
|
||||||
local dim = mapDimensions(dest, blocks, box, dests)
|
|
||||||
|
|
||||||
-- reuse map if possible
|
|
||||||
if not lastDim or not dimsAreEqual(dim, lastDim) then
|
|
||||||
-- Creates a grid object
|
|
||||||
grid = Grid(dim)
|
|
||||||
finder:setGrid(grid)
|
|
||||||
|
|
||||||
lastDim = dim
|
|
||||||
end
|
|
||||||
for _,b in pairs(blocks) do
|
|
||||||
addBlock(grid, b, dim)
|
|
||||||
end
|
|
||||||
|
|
||||||
dest = selectDestination(dests, box, grid)
|
|
||||||
if not dest then
|
|
||||||
return false, 'failed to reach destination'
|
|
||||||
end
|
|
||||||
if turtle.point.x == dest.x and turtle.point.z == dest.z and turtle.point.y == dest.y then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Define start and goal locations coordinates
|
|
||||||
local startPt = turtle.point
|
|
||||||
|
|
||||||
-- Calculates the path, and its length
|
|
||||||
local path = finder:getPath(
|
|
||||||
startPt.x, startPt.y, startPt.z, turtle.point.heading,
|
|
||||||
dest.x, dest.y, dest.z, dest.heading)
|
|
||||||
|
|
||||||
if not path then
|
|
||||||
Util.removeByValue(dests, dest)
|
|
||||||
else
|
|
||||||
updateCanvas(path)
|
|
||||||
|
|
||||||
path:filter()
|
|
||||||
|
|
||||||
for node in path:nodes() do
|
|
||||||
local pt = nodeToPoint(node)
|
|
||||||
|
|
||||||
if turtle.isAborted() then
|
|
||||||
return false, 'aborted'
|
|
||||||
end
|
|
||||||
|
|
||||||
--if this is the next to last node
|
|
||||||
--and we are traveling up or down, then the
|
|
||||||
--heading for this node should be the heading of the last node
|
|
||||||
--or, maybe..
|
|
||||||
--if last node is up or down (or either?)
|
|
||||||
|
|
||||||
-- use single turn method so the turtle doesn't turn around
|
|
||||||
-- when encountering obstacles
|
|
||||||
--if not turtle.gotoSingleTurn(pt.x, pt.y, pt.z, pt.heading) then
|
|
||||||
pt.heading = nil
|
|
||||||
if not turtle.go(pt) then
|
|
||||||
local bpt = Point.nearestTo(turtle.point, pt)
|
|
||||||
|
|
||||||
if turtle.getFuelLevel() == 0 then
|
|
||||||
return false, 'Out of fuel'
|
|
||||||
end
|
|
||||||
table.insert(blocks, bpt)
|
|
||||||
os.queueEvent('canvas', {
|
|
||||||
type = 'canvas_barrier',
|
|
||||||
data = { bpt },
|
|
||||||
})
|
|
||||||
-- really need to check if the block we ran into was a turtle.
|
|
||||||
-- if so, this block should be temporary (1-2 secs)
|
|
||||||
|
|
||||||
--local side = turtle.getSide(turtle.point, pt)
|
|
||||||
--if turtle.isTurtleAtSide(side) then
|
|
||||||
-- pt.timestamp = os.clock() + ?
|
|
||||||
--end
|
|
||||||
-- if dim has not changed, then need to update grid with
|
|
||||||
-- walkable = nil (after time has elapsed)
|
|
||||||
|
|
||||||
--if device.turtlesensorenvironment then
|
|
||||||
-- addSensorBlocks(blocks, device.turtlesensorenvironment.sonicScan())
|
|
||||||
--end
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if dest.heading then
|
|
||||||
turtle.setHeading(dest.heading)
|
|
||||||
end
|
|
||||||
return dest
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
pathfind = function(dest, options)
|
|
||||||
options = options or { }
|
|
||||||
--if not options.blocks and turtle.gotoPoint(dest) then
|
|
||||||
-- return dest
|
|
||||||
--end
|
|
||||||
return pathTo(dest, options)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- set a global bounding box
|
|
||||||
-- box can be overridden by passing box in pathfind options
|
|
||||||
setBox = function(box)
|
|
||||||
turtle.getState().box = box
|
|
||||||
end,
|
|
||||||
|
|
||||||
setBlocks = function(blocks)
|
|
||||||
turtle.getState().blocks = blocks
|
|
||||||
end,
|
|
||||||
|
|
||||||
addBlock = function(block)
|
|
||||||
if turtle.getState().blocks then
|
|
||||||
table.insert(turtle.getState().blocks, block)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
reset = function()
|
|
||||||
turtle.getState().box = nil
|
|
||||||
turtle.getState().blocks = nil
|
|
||||||
end,
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user