restructure
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
required = {
|
required = {
|
||||||
'core',
|
'core',
|
||||||
|
'turtle',
|
||||||
},
|
},
|
||||||
title = 'Schematic Builder',
|
title = 'Schematic Builder',
|
||||||
repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/builder',
|
repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/builder',
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
required = {
|
||||||
|
'turtle',
|
||||||
|
},
|
||||||
title = 'GPS',
|
title = 'GPS',
|
||||||
repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/gps',
|
repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/gps',
|
||||||
description = [[A single turtle GPS server
|
description = [[A single turtle GPS server
|
||||||
|
|||||||
@@ -180,21 +180,17 @@ Event.on('turtle_inventory', function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local cycleHandle
|
Event.onInterval(5, function()
|
||||||
cycleHandle = Event.onInterval(5, function()
|
|
||||||
Event.trigger('milo_cycle')
|
Event.trigger('milo_cycle')
|
||||||
if context.taskCounter > 0 then
|
|
||||||
--local average = context.taskTimer / context.taskCounter
|
|
||||||
--_syslog('Interval: ' .. math.max(5, 2 + average * 3))
|
|
||||||
--cycleHandle.updateInterval(math.max(5, 2 + average * 3))
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Event.on({ 'storage_offline', 'storage_online' }, function()
|
Event.on({ 'storage_offline', 'storage_online' }, function()
|
||||||
if context.storage:isOnline() then
|
if context.storage:isOnline() then
|
||||||
Milo:resumeCrafting({ key = 'storageOnline' })
|
Milo:resumeCrafting({ key = 'storageOnline' })
|
||||||
|
turtle.setStatus('Milo: online')
|
||||||
else
|
else
|
||||||
Milo:pauseCrafting({ key = 'storageOnline', msg = 'Storage offline' })
|
Milo:pauseCrafting({ key = 'storageOnline', msg = 'Storage offline' })
|
||||||
|
turtle.setStatus('Milo: offline')
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|||||||
@@ -211,6 +211,16 @@ function Milo:makeRequest(item, count, callback)
|
|||||||
return request
|
return request
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Milo:emptyInventory()
|
||||||
|
for i = 1, 16 do
|
||||||
|
if turtle.getItemCount(i) > 0 then
|
||||||
|
turtle.select(i)
|
||||||
|
turtle.drop()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
turtle.select(1)
|
||||||
|
end
|
||||||
|
|
||||||
function Milo:eject(item, count)
|
function Milo:eject(item, count)
|
||||||
local total = 0
|
local total = 0
|
||||||
while count > 0 do
|
while count > 0 do
|
||||||
@@ -224,7 +234,7 @@ function Milo:eject(item, count)
|
|||||||
|
|
||||||
--Sound.play('ui.button.click')
|
--Sound.play('ui.button.click')
|
||||||
Sound.play('entity.illusion_illager.death', .3)
|
Sound.play('entity.illusion_illager.death', .3)
|
||||||
turtle.emptyInventory()
|
self:emptyInventory()
|
||||||
end
|
end
|
||||||
return total
|
return total
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ end
|
|||||||
|
|
||||||
function learnPage:eventHandler(event)
|
function learnPage:eventHandler(event)
|
||||||
if event.type == 'cancel' then
|
if event.type == 'cancel' then
|
||||||
turtle.emptyInventory()
|
Milo:emptyInventory()
|
||||||
UI:setPreviousPage()
|
UI:setPreviousPage()
|
||||||
|
|
||||||
elseif event.type == 'form_invalid' or event.type == 'general_error' then
|
elseif event.type == 'form_invalid' or event.type == 'general_error' then
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ function nodeWizard.filter:eventHandler(event)
|
|||||||
self:resetGrid()
|
self:resetGrid()
|
||||||
self.grid:update()
|
self.grid:update()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
turtle.emptyInventory()
|
Milo:emptyInventory()
|
||||||
|
|
||||||
elseif event.type == 'remove_entry' then
|
elseif event.type == 'remove_entry' then
|
||||||
local row = self.grid:getSelected()
|
local row = self.grid:getSelected()
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ function pages.confirmation:validate()
|
|||||||
end
|
end
|
||||||
|
|
||||||
Milo:saveMachineRecipe(recipe, result, machine.name)
|
Milo:saveMachineRecipe(recipe, result, machine.name)
|
||||||
turtle.emptyInventory()
|
Milo:emptyInventory()
|
||||||
|
|
||||||
local displayName = itemDB:getName(result)
|
local displayName = itemDB:getName(result)
|
||||||
UI:setPage('listing', {
|
UI:setPage('listing', {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ function pages.turtleCraft:validate()
|
|||||||
if recipe then
|
if recipe then
|
||||||
local displayName = itemDB:getName(recipe)
|
local displayName = itemDB:getName(recipe)
|
||||||
|
|
||||||
turtle.emptyInventory()
|
Milo:emptyInventory()
|
||||||
|
|
||||||
UI:setPage('listing', {
|
UI:setPage('listing', {
|
||||||
filter = displayName,
|
filter = displayName,
|
||||||
|
|||||||
9
miloApps/.package
Normal file
9
miloApps/.package
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
required = {
|
||||||
|
'turtle',
|
||||||
|
},
|
||||||
|
title = 'Milo extra applications',
|
||||||
|
repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/miloApps',
|
||||||
|
description = [[Programs for turtles in a Milo network]],
|
||||||
|
licence = 'MIT',
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
local Pathing = require('opus.pathfind')
|
local Pathing = require('turtle.pathfind')
|
||||||
local Point = require('opus.point')
|
local Point = require('opus.point')
|
||||||
local Util = require('opus.util')
|
local Util = require('opus.util')
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
[ 'gps' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/gps/.package',
|
[ 'gps' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/gps/.package',
|
||||||
[ 'mbs' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/mbs/.package',
|
[ 'mbs' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/mbs/.package',
|
||||||
[ 'milo' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/milo/.package',
|
[ 'milo' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/milo/.package',
|
||||||
|
[ 'miloApps' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/miloApps/.package',
|
||||||
[ 'miners' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/miners/.package',
|
[ 'miners' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/miners/.package',
|
||||||
[ 'monitor' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/monitor/.package',
|
[ 'monitor' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/monitor/.package',
|
||||||
[ 'neural' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/neural/.package',
|
[ 'neural' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/neural/.package',
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
required = {
|
required = {
|
||||||
'core',
|
'core',
|
||||||
|
'turtle',
|
||||||
},
|
},
|
||||||
title = 'Move resources around with turtles',
|
title = 'Move resources around with turtles',
|
||||||
repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/pickup',
|
repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/pickup',
|
||||||
|
|||||||
175
turtle/apis/jumper/core/bheap.lua
Normal file
175
turtle/apis/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
turtle/apis/jumper/core/node.lua
Normal file
41
turtle/apis/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
turtle/apis/jumper/core/path.lua
Normal file
67
turtle/apis/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
turtle/apis/jumper/core/utils.lua
Normal file
57
turtle/apis/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
|
||||||
101
turtle/apis/jumper/grid.lua
Normal file
101
turtle/apis/jumper/grid.lua
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
--- 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
|
||||||
104
turtle/apis/jumper/pathfinder.lua
Normal file
104
turtle/apis/jumper/pathfinder.lua
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
--[[
|
||||||
|
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
|
||||||
77
turtle/apis/jumper/search/astar.lua
Normal file
77
turtle/apis/jumper/search/astar.lua
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
-- 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
|
||||||
256
turtle/apis/pathfind.lua
Normal file
256
turtle/apis/pathfind.lua
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
local Grid = require('turtle.jumper.grid')
|
||||||
|
local Pathfinder = require('turtle.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,
|
||||||
|
}
|
||||||
1273
turtle/autorun/6.tl3.lua
Normal file
1273
turtle/autorun/6.tl3.lua
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user