mirror of
https://github.com/kepler155c/opus
synced 2024-12-27 00:50:26 +00:00
512 lines
12 KiB
Lua
512 lines
12 KiB
Lua
require = requireInjector(getfenv(1))
|
|
local UI = require('ui')
|
|
local GPS = require('gps')
|
|
local Socket = require('socket')
|
|
|
|
multishell.setTitle(multishell.getCurrent(), 'Shapes')
|
|
|
|
local args = { ... }
|
|
local turtleId = args[1] or error('Supply turtle ID')
|
|
turtleId = tonumber(turtleId)
|
|
|
|
local script = [[
|
|
|
|
require = requireInjector(getfenv(1))
|
|
local GPS = require('gps')
|
|
local ChestProvider = require('chestProvider18')
|
|
local Point = require('point')
|
|
|
|
local itemProvider
|
|
|
|
function dumpInventory()
|
|
|
|
for i = 1, 16 do
|
|
local qty = turtle.getItemCount(i)
|
|
if qty > 0 then
|
|
itemProvider:insert(i, qty)
|
|
end
|
|
if turtle.getItemCount(i) ~= 0 then
|
|
print('Provider is full or missing - make space or replace')
|
|
print('Press enter to continue')
|
|
read()
|
|
end
|
|
end
|
|
turtle.select(1)
|
|
end
|
|
|
|
local function refuel()
|
|
while turtle.getFuelLevel() < 4000 do
|
|
print('Refueling')
|
|
turtle.select(1)
|
|
|
|
itemProvider:provide({ id = 'minecraft:coal', dmg = 0 }, 64, 1)
|
|
if turtle.getItemCount(1) == 0 then
|
|
print('Out of fuel, add fuel to chest/ME system')
|
|
turtle.status = 'waiting'
|
|
os.sleep(5)
|
|
else
|
|
turtle.refuel(64)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function goto(pt)
|
|
while not turtle.gotoPoint(pt) do
|
|
print('stuck')
|
|
os.sleep(5)
|
|
end
|
|
end
|
|
|
|
local function pathTo(pt)
|
|
while not turtle.pathfind(pt) do
|
|
print('stuck')
|
|
os.sleep(5)
|
|
end
|
|
end
|
|
|
|
local function resupply()
|
|
|
|
if data.suppliesPt then
|
|
pathTo(data.suppliesPt)
|
|
|
|
itemProvider = ChestProvider({ direction = 'up', wrapSide = 'bottom' })
|
|
dumpInventory()
|
|
refuel()
|
|
end
|
|
end
|
|
|
|
local function makePlane(y)
|
|
local pt = { x = math.min(data.startPt.x, data.endPt.x),
|
|
ex = math.max(data.startPt.x, data.endPt.x),
|
|
z = math.min(data.startPt.z, data.endPt.z),
|
|
ez = math.max(data.startPt.z, data.endPt.z) }
|
|
|
|
local blocks = { }
|
|
for z = pt.z, pt.ez do
|
|
for x = pt.x, pt.ex do
|
|
table.insert(blocks, { x = x, y = y, z = z })
|
|
end
|
|
end
|
|
|
|
return blocks
|
|
end
|
|
|
|
local function optimizeRoute(plane, ptb)
|
|
|
|
local maxDistance = 99999999
|
|
|
|
local function getNearestNeighbor(p, pt, threshold)
|
|
local key, block, heading
|
|
local moves = maxDistance
|
|
|
|
local function getMoves(b, k)
|
|
local distance = math.abs(pt.x - b.x) + math.abs(pt.z - b.z)
|
|
|
|
if distance < moves then
|
|
-- this operation is expensive - only run if distance is close
|
|
local c, h = Point.calculateMoves(pt, b, distance)
|
|
if c < moves then
|
|
block = b
|
|
key = k
|
|
moves = c
|
|
heading = h
|
|
end
|
|
end
|
|
end
|
|
|
|
local function blockReady(b)
|
|
return not b.u
|
|
end
|
|
|
|
local mid = pt.index
|
|
local forward = mid + 1
|
|
local backward = mid - 1
|
|
while forward <= #p or backward > 0 do
|
|
if forward <= #p then
|
|
local b = p[forward]
|
|
if blockReady(b) then
|
|
getMoves(b, forward)
|
|
if moves <= threshold then
|
|
break
|
|
end
|
|
if moves < maxDistance and math.abs(b.z - pt.z) > moves and pt.index > 0 then
|
|
forward = #p
|
|
end
|
|
end
|
|
forward = forward + 1
|
|
end
|
|
if backward > 0 then
|
|
local b = p[backward]
|
|
if blockReady(b) then
|
|
getMoves(b, backward)
|
|
if moves <= threshold then
|
|
break
|
|
end
|
|
if moves < maxDistance and math.abs(pt.z - b.z) > moves then
|
|
backward = 0
|
|
end
|
|
end
|
|
backward = backward - 1
|
|
end
|
|
end
|
|
pt.x = block.x
|
|
pt.z = block.z
|
|
pt.y = block.y
|
|
pt.heading = heading
|
|
pt.index = key
|
|
block.u = true
|
|
return block
|
|
end
|
|
|
|
local throttle = Util.throttle()
|
|
local t = { }
|
|
ptb.index = 0
|
|
local threshold = 0
|
|
for i = 1, #plane do
|
|
local b = getNearestNeighbor(plane, ptb, threshold)
|
|
table.insert(t, b)
|
|
throttle()
|
|
threshold = 1
|
|
end
|
|
|
|
return t
|
|
end
|
|
|
|
local function clear()
|
|
|
|
local pt = Util.shallowCopy(data.startPt)
|
|
pt.y = math.min(data.startPt.y, data.endPt.y)
|
|
pt.heading = 0
|
|
|
|
local osy = pt.y
|
|
local sy = osy + 1
|
|
local ey = math.max(data.startPt.y, data.endPt.y)
|
|
local firstPlane = true
|
|
|
|
resupply()
|
|
|
|
while true do
|
|
|
|
if sy > ey then
|
|
sy = ey
|
|
end
|
|
|
|
local plane = makePlane(sy)
|
|
plane = optimizeRoute(plane, pt)
|
|
|
|
if firstPlane then
|
|
turtle.pathfind(plane[1])
|
|
turtle.setPolicy(turtle.policies.digAttack)
|
|
firstPlane = false
|
|
end
|
|
|
|
for _,b in ipairs(plane) do
|
|
turtle.gotoPoint(b)
|
|
if sy < ey then
|
|
turtle.digUp()
|
|
end
|
|
if sy > osy then
|
|
turtle.digDown()
|
|
end
|
|
if turtle.abort then
|
|
break
|
|
end
|
|
end
|
|
|
|
if turtle.abort then
|
|
break
|
|
end
|
|
if sy + 1 >= ey then
|
|
break
|
|
end
|
|
|
|
sy = sy + 3
|
|
end
|
|
turtle.setPolicy(turtle.policies.none)
|
|
resupply()
|
|
end
|
|
|
|
turtle.run(function()
|
|
turtle.status = 'Clearing'
|
|
|
|
if turtle.enableGPS() then
|
|
|
|
local pt = Util.shallowCopy(turtle.point)
|
|
local s, m = pcall(clear)
|
|
pathTo(pt)
|
|
|
|
if not s and m then
|
|
error(m)
|
|
read()
|
|
end
|
|
end
|
|
end)
|
|
]]
|
|
|
|
local levelScript = [[
|
|
|
|
require = requireInjector(getfenv(1))
|
|
local Point = require('point')
|
|
|
|
local checkedNodes = { }
|
|
local nodes = { }
|
|
local box = { }
|
|
|
|
local function inBox(pt, box)
|
|
return pt.x >= box.x and
|
|
pt.y >= box.y and
|
|
pt.z >= box.z and
|
|
pt.x <= box.ex and
|
|
pt.y <= box.ey and
|
|
pt.z <= box.ez
|
|
end
|
|
|
|
local function toKey(pt)
|
|
return table.concat({ pt.x, pt.y, pt.z }, ':')
|
|
end
|
|
|
|
local function addNode(node)
|
|
|
|
for i = 0, 5 do
|
|
local hi = turtle.getHeadingInfo(i)
|
|
local testNode = { x = node.x + hi.xd, y = node.y + hi.yd, z = node.z + hi.zd }
|
|
|
|
if inBox(testNode, box) then
|
|
local key = toKey(testNode)
|
|
if not checkedNodes[key] then
|
|
nodes[key] = testNode
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function dig(action)
|
|
|
|
local directions = {
|
|
top = 'up',
|
|
bottom = 'down',
|
|
}
|
|
|
|
-- convert to up, down, north, south, east, west
|
|
local direction = directions[action.side] or
|
|
turtle.getHeadingInfo(turtle.point.heading).direction
|
|
|
|
local hi = turtle.getHeadingInfo(direction)
|
|
local node = { x = turtle.point.x + hi.xd, y = turtle.point.y + hi.yd, z = turtle.point.z + hi.zd }
|
|
if inBox(node, box) then
|
|
|
|
local key = toKey(node)
|
|
checkedNodes[key] = true
|
|
nodes[key] = nil
|
|
|
|
if action.dig() then
|
|
addNode(node)
|
|
repeat until not action.dig() -- sand, etc
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
local function move(action)
|
|
if action == 'turn' then
|
|
dig(turtle.getAction('forward'))
|
|
elseif action == 'up' then
|
|
dig(turtle.getAction('up'))
|
|
elseif action == 'down' then
|
|
dig(turtle.getAction('down'))
|
|
elseif action == 'back' then
|
|
dig(turtle.getAction('up'))
|
|
dig(turtle.getAction('down'))
|
|
end
|
|
end
|
|
|
|
local function getAdjacentPoint(pt)
|
|
local t = { }
|
|
table.insert(t, pt)
|
|
for i = 0, 5 do
|
|
local hi = turtle.getHeadingInfo(i)
|
|
local heading
|
|
if i < 4 then
|
|
heading = (hi.heading + 2) % 4
|
|
end
|
|
table.insert(t, { x = pt.x + hi.xd, z = pt.z + hi.zd, y = pt.y + hi.yd, heading = heading })
|
|
end
|
|
|
|
return Point.closest2(turtle.getPoint(), t)
|
|
end
|
|
|
|
local function level()
|
|
|
|
box.x = math.min(data.startPt.x, data.endPt.x)
|
|
box.y = math.min(data.startPt.y, data.endPt.y)
|
|
box.z = math.min(data.startPt.z, data.endPt.z)
|
|
box.ex = math.max(data.startPt.x, data.endPt.x)
|
|
box.ey = math.max(data.startPt.y, data.endPt.y)
|
|
box.ez = math.max(data.startPt.z, data.endPt.z)
|
|
|
|
turtle.pathfind(data.firstPt)
|
|
|
|
turtle.setPolicy("attack", { dig = dig }, "assuredMove")
|
|
turtle.setMoveCallback(move)
|
|
|
|
repeat
|
|
local key = toKey(turtle.point)
|
|
|
|
checkedNodes[key] = true
|
|
nodes[key] = nil
|
|
|
|
dig(turtle.getAction('down'))
|
|
dig(turtle.getAction('up'))
|
|
dig(turtle.getAction('forward'))
|
|
|
|
print(string.format('%d nodes remaining', Util.size(nodes)))
|
|
|
|
if Util.size(nodes) == 0 then
|
|
break
|
|
end
|
|
|
|
local node = Point.closest2(turtle.point, nodes)
|
|
node = getAdjacentPoint(node)
|
|
if not turtle.gotoPoint(node) then
|
|
break
|
|
end
|
|
until turtle.abort
|
|
|
|
turtle.resetState()
|
|
end
|
|
|
|
local s, m = turtle.run(function()
|
|
turtle.status = 'Leveling'
|
|
|
|
if turtle.enableGPS() then
|
|
|
|
local pt = Util.shallowCopy(turtle.point)
|
|
local s, m = pcall(level)
|
|
turtle.pathfind(pt)
|
|
|
|
if not s and m then
|
|
error(m)
|
|
end
|
|
end
|
|
end)
|
|
|
|
if not s then
|
|
error(m)
|
|
end
|
|
]]
|
|
|
|
|
|
local data = Util.readTable('/usr/config/shapes') or { }
|
|
|
|
local page = UI.Page {
|
|
titleBar = UI.TitleBar { title = 'Shapes' },
|
|
info = UI.Window { x = 5, y = 3, height = 1 },
|
|
startCoord = UI.Button { x = 2, y = 6, text = 'Start ', event = 'startCoord' },
|
|
endCoord = UI.Button { x = 2, y = 8, text = 'End ', event = 'endCoord' },
|
|
supplies = UI.Button { x = 2, y = 10, text = 'Supplies', event = 'supplies' },
|
|
first = UI.Button { x = 2, y = 11, text = 'First', event = 'firstCoord' },
|
|
cancel = UI.Button { rx = 2, ry = -2, text = 'Abort', event = 'cancel' },
|
|
begin = UI.Button { rx = -7, ry = -2, text = 'Begin', event = 'begin' },
|
|
accelerators = { q = 'quit' },
|
|
notification = UI.Notification(),
|
|
statusBar = UI.StatusBar(),
|
|
}
|
|
|
|
function page.info:draw()
|
|
|
|
local function size(a, b)
|
|
return (math.abs(a.x - b.x) + 1) *
|
|
(math.abs(a.y - b.y) + 1) *
|
|
(math.abs(a.z - b.z) + 1)
|
|
end
|
|
|
|
self:clear()
|
|
if not data.startPt then
|
|
self:write(1, 1, 'Set starting corner')
|
|
elseif not data.endPt then
|
|
self:write(1, 1, 'Set ending corner')
|
|
else
|
|
self:write(1, 1, 'Blocks: ' .. size(data.startPt, data.endPt))
|
|
end
|
|
end
|
|
|
|
function page:getPoint()
|
|
local pt = GPS.getPoint()
|
|
if not pt then
|
|
self.notification:error('GPS not available')
|
|
end
|
|
return pt
|
|
end
|
|
|
|
function page:runFunction(id, script)
|
|
|
|
Util.writeFile('script.tmp', script)
|
|
self.notification:info('Connecting')
|
|
local fn, msg = loadstring(script, 'script')
|
|
if not fn then
|
|
self.notification:error('Error in script')
|
|
debug(msg)
|
|
return
|
|
end
|
|
|
|
local socket = Socket.connect(id, 161)
|
|
if not socket then
|
|
self.notification:error('Unable to connect')
|
|
return
|
|
end
|
|
socket:write({ type = 'script', args = script })
|
|
socket:close()
|
|
|
|
self.notification:success('Sent')
|
|
end
|
|
|
|
function page:eventHandler(event)
|
|
if event.type == 'startCoord' then
|
|
data.startPt = self:getPoint()
|
|
if data.startPt then
|
|
self.statusBar:setStatus('starting corner set')
|
|
Util.writeTable('/usr/config/shapes', data)
|
|
end
|
|
self:draw()
|
|
elseif event.type == 'endCoord' then
|
|
data.endPt = self:getPoint()
|
|
if data.endPt then
|
|
self.statusBar:setStatus('ending corner set')
|
|
Util.writeTable('/usr/config/shapes', data)
|
|
end
|
|
self:draw()
|
|
elseif event.type == 'firstCoord' then
|
|
data.firstPt = self:getPoint()
|
|
if data.firstPt then
|
|
self.statusBar:setStatus('first point set')
|
|
Util.writeTable('/usr/config/shapes', data)
|
|
end
|
|
self:draw()
|
|
elseif event.type == 'supplies' then
|
|
data.suppliesPt = self:getPoint()
|
|
if data.suppliesPt then
|
|
self.statusBar:setStatus('supplies location set')
|
|
Util.writeTable('/usr/config/shapes', data)
|
|
end
|
|
elseif event.type == 'begin' then
|
|
if data.startPt and data.endPt then
|
|
local s = 'local data = ' .. textutils.serialize(data) .. levelScript
|
|
self:runFunction(turtleId, s)
|
|
else
|
|
self.notification:error('Corners not set')
|
|
end
|
|
self.statusBar:setStatus('')
|
|
elseif event.type == 'cancel' then
|
|
self:runFunction(turtleId, 'turtle.abortAction()')
|
|
self.statusBar:setStatus('')
|
|
else
|
|
return UI.Page.eventHandler(self, event)
|
|
end
|
|
return true
|
|
end
|
|
|
|
UI:setPage(page)
|
|
|
|
UI:pullEvents()
|
|
UI.term:reset()
|