379 lines
10 KiB
Lua
379 lines
10 KiB
Lua
-- Mine a hole of depth by width by heigth, starting from 0, 0, -1 relative to
|
|
-- current position and facing
|
|
|
|
-- +x = right of start, +y = up of start, +z = forward of start
|
|
local FUEL_RESERVE = 800
|
|
local FUEL_REFUEL_UNIT = 8 -- how many items to refuel with at a time
|
|
local FULL_CHECK_SLOT = 14 -- slot to check for "fullness"
|
|
|
|
|
|
-- what size is good for a medium-size piece
|
|
local MED_DIM = 11
|
|
|
|
-- what size of a dimension is considered big enough to split into medium-size
|
|
-- pieces
|
|
local BIG_DIM = MED_DIM * 3
|
|
|
|
local V_ZERO = vector.new(0, 0, 0)
|
|
local UNIT_X = vector.new(1, 0, 0)
|
|
local UNIT_Y = vector.new(0, 1, 0)
|
|
local UNIT_Z = vector.new(0, 0, 1)
|
|
|
|
local FORWARD, RIGHT, BACK, LEFT = 0, 1, 2, 3
|
|
local mode = { v = vector.new(0, 0, 0), facing = FORWARD }
|
|
mode.mine = { forward = true }
|
|
|
|
---------------------- BLOCK MATH ------------------------
|
|
|
|
function copy_v(v)
|
|
return vector.new(
|
|
v.x,
|
|
v.y,
|
|
v.z
|
|
)
|
|
end
|
|
|
|
local bmetatable, new_block
|
|
-- what is this? a type that holds a starting position and a dimention as
|
|
-- two vectors
|
|
-- TODO learn about metatables
|
|
local Block = {
|
|
-- orient our corner and offset to a vector
|
|
orient = function (self, pos)
|
|
self.v1 = self.v1 - pos
|
|
local dims = {
|
|
x = { self.v1.x, self.v1.x + self.v2.x },
|
|
y = { self.v1.y, self.v1.y + self.v2.y },
|
|
z = { self.v1.z, self.v1.z + self.v2.z }
|
|
}
|
|
for dim, points in pairs(dims) do
|
|
-- pick the closer point
|
|
-- return to point-offset format
|
|
local point, offset
|
|
if math.abs(points[1]) <= math.abs(points[2]) then
|
|
point = points[1]
|
|
offset = points[2] - points[1]
|
|
else
|
|
point = points[2]
|
|
offset = points[1] - points[2]
|
|
end
|
|
self.v1[dim] = point
|
|
self.v2[dim] = offset
|
|
end
|
|
self.v1 = self.v1 + pos
|
|
return self
|
|
end,
|
|
take = function (self, amt, dir)
|
|
-- TODO
|
|
if dir ~= "x" and dir ~= "y" and dir ~= "z" then return nil end
|
|
|
|
local block_offset = -1
|
|
if self.v2[dir] < 0 then
|
|
amt = amt * -1
|
|
block_offset = 1
|
|
end
|
|
|
|
local new, remainder = self:copy(), self
|
|
new.v2[dir] = amt + block_offset
|
|
remainder.v1[dir] = remainder.v1[dir] + amt
|
|
remainder.v2[dir] = remainder.v2[dir] - amt
|
|
|
|
return new, remainder
|
|
end,
|
|
copy = function (self)
|
|
return new_block(
|
|
vector.new(self.v1.x, self.v1.y, self.v1.z),
|
|
vector.new(self.v2.x, self.v2.y, self.v2.z)
|
|
)
|
|
end,
|
|
tostring = function (self)
|
|
-- TODO revisit?
|
|
return string.format('%s %s', self.v1:tostring(), self.v2:tostring())
|
|
end
|
|
}
|
|
|
|
bmetatable = {
|
|
__name = "Block",
|
|
__index = Block,
|
|
}
|
|
|
|
new_block = function (v1, v2)
|
|
return setmetatable(
|
|
{ v1 = v1, v2 = v2 },
|
|
bmetatable
|
|
)
|
|
end
|
|
|
|
---------------------- BASIC MOVEMENT ---------------------
|
|
|
|
function mineSides ()
|
|
if mode.mine.up then
|
|
turtle.digUp()
|
|
end
|
|
if mode.mine.down then
|
|
turtle.digDown()
|
|
end
|
|
end
|
|
|
|
function moveInLine (delta)
|
|
mineSides()
|
|
while delta > 0 do
|
|
if mode.mine.forward then
|
|
turtle.dig()
|
|
end
|
|
local success = turtle.forward()
|
|
if success then
|
|
mineSides()
|
|
delta = delta - 1
|
|
end
|
|
end
|
|
end
|
|
|
|
function moveUpDown (delta)
|
|
while delta > 0 do
|
|
if mode.mine.forward then
|
|
turtle.digUp()
|
|
end
|
|
local success = turtle.up()
|
|
if success then
|
|
delta = delta - 1
|
|
end
|
|
end
|
|
while delta < 0 do
|
|
if mode.mine.forward then
|
|
turtle.digDown()
|
|
end
|
|
local success = turtle.down()
|
|
if success then
|
|
delta = delta + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
function turnToFace (new_direction)
|
|
local delta = new_direction - mode.facing
|
|
if delta > 2 then
|
|
delta = delta - 4
|
|
elseif delta < -2 then
|
|
delta = delta + 4
|
|
end
|
|
while delta > 0 do
|
|
turtle.turnRight()
|
|
delta = delta - 1
|
|
end
|
|
while delta < 0 do
|
|
turtle.turnLeft()
|
|
delta = delta + 1
|
|
end
|
|
mode.facing = new_direction
|
|
end
|
|
|
|
function getCurrentPos()
|
|
return mode.v
|
|
end
|
|
|
|
-- move to a specific point
|
|
function moveAbs (v)
|
|
-- print(string.format("Moving to (%s)", v:tostring()))
|
|
move(v - getCurrentPos())
|
|
end
|
|
|
|
-- move relative to the turtle's current location
|
|
function move (v)
|
|
-- x
|
|
if v.x > 0 then
|
|
turnToFace(RIGHT)
|
|
moveInLine(v.x)
|
|
elseif v.x < 0 then
|
|
turnToFace(LEFT)
|
|
moveInLine(-v.x)
|
|
end
|
|
mode.v.x = mode.v.x + v.x
|
|
-- z
|
|
if v.z > 0 then
|
|
turnToFace(FORWARD)
|
|
moveInLine(v.z)
|
|
end
|
|
if v.z < 0 then
|
|
turnToFace(BACK)
|
|
moveInLine(-v.z)
|
|
end
|
|
mode.v.z = mode.v.z + v.z
|
|
-- y
|
|
moveUpDown(v.y)
|
|
mode.v.y = mode.v.y + v.y
|
|
end
|
|
|
|
-------------- DROPPING OFF AND REFUELING -------------------
|
|
|
|
function refuelUntil (amt)
|
|
while turtle.getFuelLevel() < amt do
|
|
if not turtle.refuel(FUEL_REFUEL_UNIT) then break end
|
|
end
|
|
end
|
|
|
|
function dropOffItems (starting_point, go_back)
|
|
print("Time to drop off what I got")
|
|
-- Turn off mining
|
|
mode.mine = { forward = true }
|
|
-- Return to origin, facing BACK
|
|
moveAbs(starting_point)
|
|
moveAbs(V_ZERO)
|
|
turnToFace(BACK)
|
|
-- deposit items
|
|
for slot = 2, 16 do
|
|
turtle.select(slot)
|
|
turtle.drop()
|
|
end
|
|
turtle.select(1)
|
|
-- Return to the mining zone, facing forward
|
|
if go_back then
|
|
moveAbs(starting_point)
|
|
end
|
|
end
|
|
|
|
function inventoryFull ()
|
|
-- leave a little extra space just in case
|
|
return turtle.getItemCount(FULL_CHECK_SLOT) > 0
|
|
end
|
|
|
|
function checkAndDropOff(starting_point)
|
|
if inventoryFull() then dropOffItems(starting_point, true) end
|
|
refuelUntil(FUEL_RESERVE)
|
|
end
|
|
|
|
function mine (block)
|
|
-- assumptions: block is mineable in a single pass and oriented to the
|
|
-- currentPos
|
|
-- rough algo draft:
|
|
-- 1. go to "starting point" at one end of block
|
|
-- a. don't move vertically if you don't have to
|
|
-- b. set mining mode beforehand
|
|
-- 2. move to the ending point
|
|
local offset = 1
|
|
if block.v2.y < 0 then offset = -1 end
|
|
local height = block.v2.y + offset
|
|
local start_v = copy_v(block.v1)
|
|
local end_v = block.v1 + block.v2
|
|
if height > 2 then
|
|
start_v = start_v + UNIT_Y
|
|
end_v = end_v - UNIT_Y
|
|
elseif height < -2 then
|
|
start_v = start_v - UNIT_Y
|
|
end_v = end_v + UNIT_Y
|
|
end
|
|
moveAbs(start_v)
|
|
if height % 3 == 0 then
|
|
mode.mine = { up = true, forward = true, down = true }
|
|
elseif height == 2 then
|
|
mode.mine = { forward = true, up = true }
|
|
end_v = end_v - UNIT_Y
|
|
elseif height == -2 then
|
|
mode.mine = { forward = true, down = true }
|
|
end_v = end_v + UNIT_Y
|
|
end
|
|
moveAbs(end_v)
|
|
end
|
|
|
|
function canMine (block)
|
|
local tooHigh = math.abs(block.v2.y) > 2
|
|
-- we can only mine in a 1-wide strip at a time
|
|
local tooWide = (math.abs(block.v2.x) > 0) and (math.abs(block.v2.z) > 0)
|
|
return not (tooHigh or tooWide)
|
|
end
|
|
|
|
function splitHorizontal (block)
|
|
-- TODO make this less naive?
|
|
local take_amt = 1
|
|
local bigBlock = function (block)
|
|
return (block.v2.x >= BIG_DIM) or (block.v2.z >= BIG_DIM)
|
|
end
|
|
if bigBlock(block) then
|
|
take_amt = MED_DIM
|
|
end
|
|
-- decide whether to take a slice off of the x or z direction
|
|
-- (criteria: bite a piece off the bigger dimension)
|
|
if math.abs(block.v2.z) >= math.abs(block.v2.x) then
|
|
return block:take(take_amt, "z")
|
|
else
|
|
return block:take(take_amt, "x")
|
|
end
|
|
end
|
|
|
|
function split (block)
|
|
local tooHigh = function (block)
|
|
return math.abs(block.v2.y) > 2
|
|
end
|
|
if tooHigh(block) then
|
|
return block:take(3, "y")
|
|
else
|
|
return splitHorizontal(block)
|
|
end
|
|
end
|
|
|
|
function process (stack, starting_point)
|
|
refuelUntil(FUEL_RESERVE)
|
|
mode.mine = { forward = true }
|
|
if #stack == 0 then return nil end
|
|
checkAndDropOff(starting_point)
|
|
local working = table.remove(stack):orient(getCurrentPos())
|
|
print("Working:", working:tostring())
|
|
if canMine(working) then
|
|
mine(working)
|
|
else
|
|
local new, remainder = split(working)
|
|
table.insert(stack, remainder)
|
|
table.insert(stack, new)
|
|
end
|
|
return process (stack, starting_point)
|
|
end
|
|
|
|
function run (v1, v2)
|
|
-- TODO calculate starting_point, the place turtle will move to before
|
|
-- moving to the first corner of block
|
|
local block = new_block(v1, v2 - v1)
|
|
local starting_point = V_ZERO
|
|
|
|
process({ block }, starting_point)
|
|
-- return to base
|
|
dropOffItems(starting_point, false)
|
|
turnToFace(FORWARD)
|
|
end
|
|
|
|
function usage ()
|
|
print("Usage: mine x1 y1 z1 x2 y2 z2 OR mine x1 y1 z1 OR mine x1 y1 z1 \
|
|
x2 y2 z2 x_turtle y_turtle z_turtle.")
|
|
print("Mine from the first set of coordinates to the second in a \
|
|
rectangular prism.")
|
|
print("The coordinates are relative to the facing of the turtle at the \
|
|
start of the program.")
|
|
print("If invoked with only three arguments, act as if they are the last \
|
|
three arguments and substitute 0 0 0 for the first three.")
|
|
print("If invoked with nine arguments, interpret the first two sets as \
|
|
world coordinates of the rectangular prism and the third set as \
|
|
the coordinates of the turtle's starting position, \
|
|
ASSUMING FACING NORTH.")
|
|
print("Fuel goes in the top-left slot of the turtle's inventory.")
|
|
end
|
|
|
|
function argsToNumbers ()
|
|
local num_args = {}
|
|
for i, v in ipairs(arg) do
|
|
num_args[i] = tonumber(v)
|
|
end
|
|
return num_args
|
|
end
|
|
|
|
local arg = argsToNumbers()
|
|
if #arg == 3 then
|
|
run(V_ZERO, vector.new(arg[1], arg[2], arg[3]))
|
|
elseif #arg == 6 then
|
|
run(vector.new(arg[1], arg[2], arg[3]), vector.new(arg[4], arg[5], arg[6]))
|
|
elseif #arg == 9 then
|
|
local turtle_pos = vector.new(arg[7], arg[8], arg[9])
|
|
local v1, v2 = vector.new(arg[1], arg[2], arg[3])
|
|
, vector.new(arg[4], arg[5], arg[6])
|
|
run(v1 - turtle_pos, v2 - turtle_pos)
|
|
else
|
|
usage()
|
|
end
|