1
0
mirror of https://github.com/kepler155c/opus synced 2025-10-20 18:27:40 +00:00

refactor + cleanup

This commit is contained in:
kepler155c@gmail.com
2017-10-27 20:24:48 -04:00
parent cac15722b8
commit 1b9450017d
12 changed files with 371 additions and 430 deletions

View File

@@ -1,14 +1,14 @@
--- A light implementation of Binary heaps data structure.
-- While running a search, some search algorithms (Astar, Dijkstra, Jump Point Search) have to maintains
-- a list of nodes called __open list__. Retrieve from this list the lowest cost node can be quite slow,
-- as it normally requires to skim through the full set of nodes stored in this list. This becomes a real
-- problem especially when dozens of nodes are being processed (on large maps).
-- a list of nodes called __open list__. Retrieve from this list the lowest cost node can be quite slow,
-- as it normally requires to skim through the full set of nodes stored in this list. This becomes a real
-- problem especially when dozens of nodes are being processed (on large maps).
--
-- The current module implements a <a href="http://www.policyalmanac.org/games/binaryHeaps.htm">binary heap</a>
-- data structure, from which the search algorithm will instantiate an open list, and cache the nodes being
-- examined during a search. As such, retrieving the lower-cost node is faster and globally makes the search end
-- data structure, from which the search algorithm will instantiate an open list, and cache the nodes being
-- examined during a search. As such, retrieving the lower-cost node is faster and globally makes the search end
-- up quickly.
--
--
-- This module is internally used by the library on purpose.
-- It should normally not be used explicitely, yet it remains fully accessible.
--
@@ -23,7 +23,7 @@ if (...) then
-- Dependency
local Utils = require((...):gsub('%.bheap$','.utils'))
-- Local reference
local floor = math.floor
@@ -40,7 +40,7 @@ if (...) then
else pIndex = (index-1)/2
end
if not heap._sort(heap._heap[pIndex], heap._heap[index]) then
heap._heap[pIndex], heap._heap[index] =
heap._heap[pIndex], heap._heap[index] =
heap._heap[index], heap._heap[pIndex]
percolate_up(heap, pIndex)
end
@@ -89,7 +89,7 @@ if (...) then
-- @class function
-- @treturn bool __true__ of no item is queued in the heap, __false__ otherwise
-- @usage
-- if myHeap:empty() then
-- if myHeap:empty() then
-- print('Heap is empty!')
-- end
function heap:empty()
@@ -129,7 +129,7 @@ if (...) then
-- @class function
-- @treturn value a value previously pushed into the heap
-- @usage
-- while not myHeap:empty() do
-- while not myHeap:empty() do
-- local lowestValue = myHeap:pop()
-- ...
-- end
@@ -148,18 +148,18 @@ if (...) then
end
--- Restores the `heap` property.
-- Reorders the `heap` with respect to the comparison function being used.
-- When given argument __item__ (a value existing in the `heap`), will sort from that very item in the `heap`.
-- Otherwise, the whole `heap` will be cheacked.
-- Reorders the `heap` with respect to the comparison function being used.
-- When given argument __item__ (a value existing in the `heap`), will sort from that very item in the `heap`.
-- Otherwise, the whole `heap` will be cheacked.
-- @class function
-- @tparam[opt] value item the modified value
-- @treturn heap self (the calling `heap` itself, can be chained)
-- @usage myHeap:heapify()
-- @usage myHeap:heapify()
function heap:heapify(item)
if self._size == 0 then return end
if item then
local i = Utils.indexOf(self._heap,item)
if i then
if i then
percolate_down(self, i)
percolate_up(self, i)
end

View File

@@ -7,59 +7,26 @@
-- made with regards of their `f` cost. From a given node being examined, the `pathfinder` will expand the search
-- to the next neighbouring node having the lowest `f` cost. See `core.bheap` for more details.
--
if (...) then
--- The `Node` class.<br/>
-- This class is callable.
-- Therefore,_ <code>Node(...)</code> _acts as a shortcut to_ <code>Node:new(...)</code>.
-- @type Node
local Node = {}
Node.__index = Node
--- Inits a new `node`
-- @class function
-- @tparam int x the x-coordinate of the node on the collision map
-- @tparam int y the y-coordinate of the node on the collision map
-- @treturn node a new `node`
-- @usage local node = Node(3,4)
function Node:new(x,y,z)
return setmetatable({_x = x, _y = y, _z = z }, Node)
return setmetatable({x = x, y = y, z = z }, Node)
end
-- Enables the use of operator '<' to compare nodes.
-- Will be used to sort a collection of nodes in a binary heap on the basis of their F-cost
function Node.__lt(A,B) return (A._f < B._f) end
--- Returns x-coordinate of a `node`
-- @class function
-- @treturn number the x-coordinate of the `node`
-- @usage local x = node:getX()
function Node:getX() return self._x end
--- Returns y-coordinate of a `node`
-- @class function
-- @treturn number the y-coordinate of the `node`
-- @usage local y = node:getY()
function Node:getY() return self._y end
function Node:getZ() return self._z end
--- Returns x and y coordinates of a `node`
-- @class function
-- @treturn number the x-coordinate of the `node`
-- @treturn number the y-coordinate of the `node`
-- @usage local x, y = node:getPos()
function Node:getPos() return self._x, self._y, self._z end
function Node:getX() return self.x end
function Node:getY() return self.y end
function Node:getZ() return self.z end
--- Clears temporary cached attributes of a `node`.
-- Deletes the attributes cached within a given node after a pathfinding call.
-- This function is internally used by the search algorithms, so you should not use it explicitely.
-- @class function
-- @treturn node self (the calling `node` itself, can be chained)
-- @usage
-- local thisNode = Node(1,2)
-- thisNode:reset()
function Node:reset()
self._g, self._h, self._f = nil, nil, nil
self._opened, self._closed, self._parent = nil, nil, nil

View File

@@ -7,27 +7,18 @@
--
if (...) then
--- The `Path` class.<br/>
-- This class is callable.
-- Therefore, <em><code>Path(...)</code></em> acts as a shortcut to <em><code>Path:new(...)</code></em>.
-- @type Path
local t_remove = table.remove
local Path = {}
Path.__index = Path
--- Inits a new `path`.
-- @class function
-- @treturn path a `path`
-- @usage local p = Path()
function Path:new()
return setmetatable({_nodes = {}}, Path)
end
--- Iterates on each single `node` along a `path`. At each step of iteration,
-- returns the `node` plus a count value. Aliased as @{Path:nodes}
-- @class function
-- @treturn node a `node`
-- @treturn int the count for the number of nodes
-- @see Path:nodes
-- @usage
-- for node, count in p:iter() do
-- ...
@@ -42,6 +33,32 @@ if (...) then
end
end
--- `Path` compression modifier. Given a `path`, eliminates useless nodes to return a lighter `path`
-- consisting of straight moves. Does the opposite of @{Path:fill}
-- @class function
-- @treturn path self (the calling `path` itself, can be chained)
-- @see Path:fill
-- @usage p:filter()
function Path:filter()
local i = 2
local xi,yi,zi,dx,dy,dz, olddx, olddy, olddz
xi,yi,zi = self._nodes[i].x, self._nodes[i].y, self._nodes[i].z
dx, dy,dz = xi - self._nodes[i-1].x, yi-self._nodes[i-1].y, zi-self._nodes[i-1].z
while true do
olddx, olddy, olddz = dx, dy, dz
if self._nodes[i+1] then
i = i+1
xi, yi, zi = self._nodes[i].x, self._nodes[i].y, self._nodes[i].z
dx, dy, dz = xi - self._nodes[i-1].x, yi - self._nodes[i-1].y, zi - self._nodes[i-1].z
if olddx == dx and olddy == dy and olddz == dz then
t_remove(self._nodes, i-1)
i = i - 1
end
else break end
end
return self
end
return setmetatable(Path,
{__call = function(_,...)
return Path:new(...)

View File

@@ -25,18 +25,9 @@ if (...) then
{x = 0, y = 0, z = 1} --[[U]], {x = 0, y = -0, z = -1}, --[[D]]
}
--- The `Grid` class.<br/>
-- This class is callable.
-- Therefore,_ <code>Grid(...)</code> _acts as a shortcut to_ <code>Grid:new(...)</code>.
-- @type Grid
local Grid = {}
Grid.__index = Grid
--- Inits a new `grid`
-- @class function
-- @tparam table Map dimensions
-- or a `string` with line-break chars (<code>\n</code> or <code>\r</code>) as row delimiters.
-- @treturn grid a new `grid` instance
function Grid:new(dim)
local newGrid = { }
newGrid._min_x, newGrid._max_x = dim.x, dim.ex
@@ -49,73 +40,38 @@ if (...) then
return setmetatable(newGrid,Grid)
end
--- Checks if `node` at [x,y] is __walkable__.
-- Will check if `node` at location [x,y] both *exists* on the collision map and *is walkable*
-- @class function
-- @tparam int x the x-location of the node
-- @tparam int y the y-location of the node
-- @tparam int z the z-location of the node
--
function Grid:isWalkableAt(x, y, z)
local node = self:getNodeAt(x,y,z)
return node and node.walkable ~= 1
end
--- Returns the `grid` width.
-- @class function
-- @treturn int the `grid` width
-- @usage print(myGrid:getWidth())
function Grid:getWidth()
return self._width
end
--- Returns the `grid` height.
-- @class function
-- @treturn int the `grid` height
-- @usage print(myGrid:getHeight())
function Grid:getHeight()
return self._height
end
--- Returns the set of nodes.
-- @class function
-- @treturn {{node,...},...} an array of nodes
-- @usage local nodes = myGrid:getNodes()
function Grid:getNodes()
return self._nodes
end
--- Returns the `grid` bounds. Returned values corresponds to the upper-left
-- and lower-right coordinates (in tile units) of the actual `grid` instance.
-- @class function
-- @treturn int the upper-left corner x-coordinate
-- @treturn int the upper-left corner y-coordinate
-- @treturn int the lower-right corner x-coordinate
-- @treturn int the lower-right corner y-coordinate
-- @usage local left_x, left_y, right_x, right_y = myGrid:getBounds()
function Grid:getBounds()
return self._min_x, self._min_y, self._min_z, self._max_x, self._max_y, self._max_z
end
--- Returns neighbours. The returned value is an array of __walkable__ nodes neighbouring a given `node`.
-- @class function
-- @tparam node node a given `node`
-- @tparam[opt] string|int|func walkable the value for walkable locations
-- in the collision map array (see @{Grid:new}).
-- Defaults to __false__ when omitted.
-- @treturn {node,...} an array of nodes neighbouring a given node
-- @usage
-- local aNode = myGrid:getNodeAt(5,6)
-- local neighbours = myGrid:getNeighbours(aNode, 0, true)
function Grid:getNeighbours(node)
local neighbours = {}
for i = 1,#straightOffsets do
local n = self:getNodeAt(
node._x + straightOffsets[i].x,
node._y + straightOffsets[i].y,
node._z + straightOffsets[i].z
node.x + straightOffsets[i].x,
node.y + straightOffsets[i].y,
node.z + straightOffsets[i].z
)
if n and self:isWalkableAt(n._x, n._y, n._z) then
if n and self:isWalkableAt(n.x, n.y, n.z) then
neighbours[#neighbours+1] = n
end
end
@@ -123,17 +79,7 @@ if (...) then
return neighbours
end
--- Returns the `node` at location [x,y,z].
-- @class function
-- @name Grid:getNodeAt
-- @tparam int x the x-coordinate coordinate
-- @tparam int y the y-coordinate coordinate
-- @tparam int z the z-coordinate coordinate
-- @treturn node a `node`
-- @usage local aNode = myGrid:getNodeAt(2,2)
-- Gets the node at location <x,y> on a preprocessed grid
function Grid:getNodeAt(x,y,z)
function Grid:getNodeAt(x,y,z)
if not x or not y or not z then return end
if Utils.outOfRange(x,self._min_x,self._max_x) then return end
if Utils.outOfRange(y,self._min_y,self._max_y) then return end

View File

@@ -28,11 +28,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
--]]
--- The Pathfinder class
--
-- Implementation of the `pathfinder` class.
local _VERSION = ""
local _RELEASEDATE = ""
@@ -49,8 +44,6 @@ if (...) then
--- Finders (search algorithms implemented). Refers to the search algorithms actually implemented in Jumper.
-- <li>[A*](http://en.wikipedia.org/wiki/A*_search_algorithm)</li>
-- @finder Finders
-- @see Pathfinder:getFinders
local Finders = {
['ASTAR'] = require (_PATH .. '.search.astar'),
}
@@ -62,21 +55,9 @@ if (...) then
-- Performs a traceback from the goal node to the start node
-- Only happens when the path was found
--- The `Pathfinder` class.<br/>
-- This class is callable.
-- Therefore,_ <code>Pathfinder(...)</code> _acts as a shortcut to_ <code>Pathfinder:new(...)</code>.
-- @type Pathfinder
local Pathfinder = {}
Pathfinder.__index = Pathfinder
--- Inits a new `pathfinder`
-- @class function
-- @tparam grid grid a `grid`
-- @tparam[opt] string finderName the name of the `Finder` (search algorithm) to be used for search.
-- Defaults to `ASTAR` when not given (see @{Pathfinder:getFinders}).
-- @treturn pathfinder a new `pathfinder` instance
-- @usage
-- local finder = Pathfinder:new(myGrid, 'ASTAR')
function Pathfinder:new(heuristic)
local newPathfinder = {}
setmetatable(newPathfinder, Pathfinder)
@@ -85,23 +66,13 @@ if (...) then
return newPathfinder
end
--- Sets the `grid`. Defines the given `grid` as the one on which the `pathfinder` will perform the search.
-- @class function
-- @tparam grid grid a `grid`
-- @treturn pathfinder self (the calling `pathfinder` itself, can be chained)
-- @usage myFinder:setGrid(myGrid)
function Pathfinder:setGrid(grid)
self._grid = grid
return self
end
--- Calculates a `path`. Returns the `path` from location __[startX, startY]__ to location __[endX, endY]__.
--- Calculates a `path`. Returns the `path` from start to end location
-- Both locations must exist on the collision map. The starting location can be unwalkable.
-- @class function
-- @tparam int startX the x-coordinate for the starting location
-- @tparam int startY the y-coordinate for the starting location
-- @tparam int endX the x-coordinate for the goal location
-- @tparam int endY the y-coordinate for the goal location
-- @treturn path a path (array of nodes) when found, otherwise nil
-- @usage local path = myFinder:getPath(1,1,5,5)
function Pathfinder:getPath(startX, startY, startZ, ih, endX, endY, endZ, oh)
@@ -112,8 +83,8 @@ if (...) then
return nil
end
startNode._heading = ih
endNode._heading = oh
startNode.heading = ih
endNode.heading = oh
assert(startNode, ('Invalid location [%d, %d, %d]'):format(startX, startY, startZ))
assert(endNode and self._grid:isWalkableAt(endX, endY, endZ),

View File

@@ -18,7 +18,7 @@ if (...) then
if node._g + mCost < neighbour._g then
neighbour._parent = node
neighbour._g = node._g + mCost
neighbour._heading = heading
neighbour.heading = heading
end
end
@@ -64,10 +64,10 @@ if (...) then
end
--[[
printf('x:%d y:%d z:%d g:%d', node._x, node._y, node._z, node._g)
printf('x:%d y:%d z:%d g:%d', node.x, node.y, node.z, node._g)
for i = 1,#neighbours do
local n = neighbours[i]
printf('x:%d y:%d z:%d f:%f g:%f h:%d', n._x, n._y, n._z, n._f, n._g, n._heading or -1)
printf('x:%d y:%d z:%d f:%f g:%f h:%d', n.x, n.y, n.z, n._f, n._g, n.heading or -1)
end
--]]

View File

@@ -2,6 +2,40 @@ local Util = require('util')
local Point = { }
Point.facings = {
[ 0 ] = { xd = 1, zd = 0, yd = 0, heading = 0, direction = 'east' },
[ 1 ] = { xd = 0, zd = 1, yd = 0, heading = 1, direction = 'south' },
[ 2 ] = { xd = -1, zd = 0, yd = 0, heading = 2, direction = 'west' },
[ 3 ] = { xd = 0, zd = -1, yd = 0, heading = 3, direction = 'north' },
}
Point.directions = {
[ 4 ] = { xd = 0, zd = 0, yd = 1, heading = 4, direction = 'up' },
[ 5 ] = { xd = 0, zd = 0, yd = -1, heading = 5, direction = 'down' },
}
Point.headings = {
[ 0 ] = Point.facings[0],
[ 1 ] = Point.facings[1],
[ 2 ] = Point.facings[2],
[ 3 ] = Point.facings[3],
[ 4 ] = Point.directions[4],
[ 5 ] = Point.directions[5],
east = Point.facings[0],
south = Point.facings[1],
west = Point.facings[2],
north = Point.facings[3],
up = Point.directions[4],
down = Point.directions[5],
}
Point.EAST = 0
Point.SOUTH = 1
Point.WEST = 2
Point.NORTH = 3
Point.UP = 4
Point.DOWN = 5
function Point.copy(pt)
return { x = pt.x, y = pt.y, z = pt.z }
end
@@ -122,6 +156,10 @@ end
-- given a set of points, find the one taking the least moves
function Point.closest(reference, pts)
if #pts == 1 then
return pts[1]
end
local lpt, lm -- lowest
for _,pt in pairs(pts) do
local m = Point.calculateMoves(reference, pt)
@@ -148,21 +186,87 @@ end
function Point.adjacentPoints(pt)
local pts = { }
local headings = {
[ 0 ] = { xd = 1, zd = 0, yd = 0, heading = 0, direction = 'east' },
[ 1 ] = { xd = 0, zd = 1, yd = 0, heading = 1, direction = 'south' },
[ 2 ] = { xd = -1, zd = 0, yd = 0, heading = 2, direction = 'west' },
[ 3 ] = { xd = 0, zd = -1, yd = 0, heading = 3, direction = 'north' },
[ 4 ] = { xd = 0, zd = 0, yd = 1, heading = 4, direction = 'up' },
[ 5 ] = { xd = 0, zd = 0, yd = -1, heading = 5, direction = 'down' }
}
for _, hi in pairs(headings) do
for i = 0, 5 do
local hi = Point.headings[i]
table.insert(pts, { x = pt.x + hi.xd, y = pt.y + hi.yd, z = pt.z + hi.zd })
end
return pts
end
-- get the point nearest A that is in the direction of B
function Point.nearestTo(pta, ptb)
local heading
if pta.x < ptb.x then
heading = 0
elseif pta.z < ptb.z then
heading = 1
elseif pta.x > ptb.x then
heading = 2
elseif pta.z > ptb.z then
heading = 3
elseif pta.y < ptb.y then
heading = 4
elseif pta.y > ptb.y then
heading = 5
end
if heading then
return {
x = pta.x + Point.headings[heading].xd,
y = pta.y + Point.headings[heading].yd,
z = pta.z + Point.headings[heading].zd,
}
end
return pta -- error ?
end
function Point.rotate(pt, facing)
local x, z = pt.x, pt.z
if facing == 1 then
pt.x = z
pt.z = -x
elseif facing == 2 then
pt.x = -x
pt.z = -z
elseif facing == 3 then
pt.x = -z
pt.z = x
end
end
function Point.makeBox(pt1, pt2)
return {
x = pt1.x,
y = pt1.y,
z = pt1.z,
ex = pt2.x,
ey = pt2.y,
ez = pt2.z,
}
end
-- expand box to include point
function Point.expandBox(box, pt)
if pt.x < box.x then
box.x = pt.x
elseif pt.x > box.ex then
box.ex = pt.x
end
if pt.y < box.y then
box.y = pt.y
elseif pt.y > box.ey then
box.ey = pt.y
end
if pt.z < box.z then
box.z = pt.z
elseif pt.z > box.ez then
box.ez = pt.z
end
end
function Point.normalizeBox(box)
return {
x = math.min(box.x, box.ex),
@@ -183,20 +287,6 @@ function Point.inBox(pt, box)
pt.z <= box.ez
end
function Point.rotate(pt, facing)
local x, z = pt.x, pt.z
if facing == 1 then
pt.x = z
pt.z = -x
elseif facing == 2 then
pt.x = -x
pt.z = -z
elseif facing == 3 then
pt.x = -z
pt.z = x
end
end
return Point
--[[

View File

@@ -1,7 +1,7 @@
_G.requireInjector()
local Grid = require ("jumper.grid")
local Pathfinder = require ("jumper.pathfinder")
local Grid = require('jumper.grid')
local Pathfinder = require('jumper.pathfinder')
local Point = require('point')
local Util = require('util')
@@ -19,74 +19,46 @@ 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 sx, sz, sy = turtle.point.x, turtle.point.z, turtle.point.y
local ex, ez, ey = turtle.point.x, turtle.point.z, turtle.point.y
local box = Point.makeBox(turtle.point, turtle.point)
local function adjust(pt)
if pt.x < sx then
sx = pt.x
elseif pt.x > ex then
ex = pt.x
end
if pt.y < sy then
sy = pt.y
elseif pt.y > ey then
ey = pt.y
end
if pt.z < sz then
sz = pt.z
elseif pt.z > ez then
ez = pt.z
end
end
adjust(dest)
Point.expandBox(box, dest)
for _,d in pairs(dests) do
adjust(d)
Point.expandBox(box, d)
end
for _,b in pairs(blocks) do
adjust(b)
Point.expandBox(box, b)
end
-- expand one block out in all directions
if boundingBox then
sx = math.max(sx - 1, boundingBox.x)
sz = math.max(sz - 1, boundingBox.z)
sy = math.max(sy - 1, boundingBox.y)
ex = math.min(ex + 1, boundingBox.ex)
ez = math.min(ez + 1, boundingBox.ez)
ey = math.min(ey + 1, boundingBox.ey)
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
sx = sx - 1
sz = sz - 1
sy = sy - 1
ex = ex + 1
ez = ez + 1
ey = ey + 1
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 {
ex = ex,
ez = ez,
ey = ey,
x = sx,
z = sz,
y = sy
}
return box
end
local function nodeToPoint(node)
return { x = node:getX(), z = node:getZ(), y = node:getY() }
return { x = node.x, y = node.y, z = node.z, heading = node.heading }
end
local heuristic = function(n, node)
local m, h = Point.calculateMoves(
{ x = node._x, y = node._y, z = node._z, heading = node._heading },
{ x = n._x, y = n._y, z = n._z, heading = n._heading })
return m, h
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)
@@ -123,9 +95,6 @@ local function addSensorBlocks(blocks, sblocks)
end
local function selectDestination(pts, box, grid)
if #pts == 1 then
return pts[1]
end
while #pts > 0 do
local pt = Point.closest(turtle.point, pts)
if box and not Point.inBox(pt, box) then
@@ -143,16 +112,15 @@ 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 = nil
local grid = nil
local lastDim
local grid
if box then
box = Point.normalizeBox(box)
end
-- Creates a pathfinder object
local myFinder = Pathfinder(heuristic)
local finder = Pathfinder(heuristic)
while turtle.point.x ~= dest.x or turtle.point.z ~= dest.z or turtle.point.y ~= dest.y do
@@ -163,7 +131,7 @@ local function pathTo(dest, options)
if not lastDim or not dimsAreEqual(dim, lastDim) then
-- Creates a grid object
grid = Grid(dim)
myFinder:setGrid(grid)
finder:setGrid(grid)
lastDim = dim
end
@@ -173,7 +141,6 @@ local function pathTo(dest, options)
dest = selectDestination(dests, box, grid)
if not dest then
-- error('failed to reach destination')
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
@@ -185,24 +152,44 @@ local function pathTo(dest, options)
local endPt = dest
-- Calculates the path, and its length
local path = myFinder:getPath(
local path = finder:getPath(
startPt.x, startPt.y, startPt.z, turtle.point.heading,
endPt.x, endPt.y, endPt.z, dest.heading)
if not path then
Util.removeByValue(dests, dest)
else
path:filter()
for node in path:nodes() do
local pt = nodeToPoint(node)
if turtle.abort then
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 -- IS THIS RIGHT ??
if not turtle.gotoSingleTurn(pt.x, pt.z, pt.y) then
table.insert(blocks, pt)
-- when encountering obstacles
if not turtle.gotoSingleTurn(pt.x, pt.z, pt.y, pt.heading) then
local bpt = Point.nearestTo(turtle.point, pt)
table.insert(blocks, 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

View File

@@ -3085,6 +3085,9 @@ function UI.Dialog:setParent()
if not self.width then
self.width = self.parent.width - 11
end
if self.width > self.parent.width then
self.width = self.parent.width
end
self.x = math.floor((self.parent.width - self.width) / 2) + 1
self.y = math.floor((self.parent.height - self.height) / 2) + 1
UI.Page.setParent(self)