Compare commits

..

No commits in common. "d0ab016dcf6eaa6c4b7dc2c0c450782deb838f09" and "457d7e16618baa3e00d5e276e0f25caaab46019a" have entirely different histories.

364
mine.lua
View file

@ -1,91 +1,20 @@
-- Mine a hole of depth by width by heigth, starting from 0, 0, -1 relative to -- Mine a hole of depth by width by heigth, starting from 0, 0, -1 relative to
-- current position and facing -- current position and facing
-- +x = right of start, +y = up of start, +z = forward of start -- +x = right of start, +y = forward of start, +z = up of start
local FUEL_RESERVE = 800 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"
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 FORWARD, RIGHT, BACK, LEFT = 0, 1, 2, 3
local mode = { v = vector.new(0, 0, 0), facing = FORWARD } local mode = { x = 0, y = 0, z = 0, facing = FORWARD }
mode.mine = { forward = true } mode.mine = { forward = true }
local block = {}
---------------------- BLOCK MATH ------------------------ function refuelUntil (amt)
while turtle.getFuelLevel() < amt do
function copy_v(v) if not turtle.refuel(8) then break end
return vector.new(
v.x,
v.y,
v.z,
)
end
local bmetatable
-- 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)
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
return self
end,
take = function (self, amt, dir)
-- TODO
if dir ~= "x" and dir ~= "y" and dir ~= "z" then return nil end
local new, remainder = self:copy(), self
new.v1[dir] = amt
remainder.v1[dir] = remainder.v1[dir] + amt
remainder.v2[dir] = remainder.v2[dir] - amt
return new, remainder
end,
copy = function (self)
return Block.new(
vector.new(self.v1.x, self.v1.y, self.v1.z),
vector.new(self.v2.x, self.v2.y, self.v2.z),
)
end end
}
bmetatable = {
__name = "Block",
__index = Block,
}
local new_block = function (v1, v2)
return setmetatable(
{ v1 = v1, v2 = v2 },
bmetatable
)
end end
---------------------- BASIC MOVEMENT ---------------------
function mineSides () function mineSides ()
if mode.mine.up then if mode.mine.up then
turtle.digUp() turtle.digUp()
@ -110,6 +39,7 @@ function moveInLine (delta)
end end
function moveUpDown (delta) function moveUpDown (delta)
local delta = delta
while delta > 0 do while delta > 0 do
if mode.mine.forward then if mode.mine.forward then
turtle.digUp() turtle.digUp()
@ -148,58 +78,47 @@ function turnToFace (new_direction)
mode.facing = new_direction mode.facing = new_direction
end end
function getCurrentPos()
return mode.v
end
-- move to a specific point -- move to a specific point
function moveAbs (v) function moveAbs (x, y, z)
-- print(string.format("Moving to (%d, %d, %d)", v.x, v.y, v.z)) -- print(string.format("Moving to (%d, %d, %d)", x, y, z))
move(v - getCurrentPos()) move(x - mode.x, y - mode.y, z - mode.z)
end end
-- move relative to the turtle's current location -- move relative to the turtle's current location
function move (v) function move (x, y, z)
-- x -- x
if v.x > 0 then if x > 0 then
turnToFace(RIGHT) turnToFace(RIGHT)
moveInLine(v.x) moveInLine(x)
elseif v.x < 0 then elseif x < 0 then
turnToFace(LEFT) turnToFace(LEFT)
moveInLine(-v.x) moveInLine(-x)
end end
mode.v = mode.v + UNIT_X mode.x = mode.x + x
-- z -- z
if v.z > 0 then if z > 0 then
turnToFace(FORWARD) turnToFace(FORWARD)
moveInLine(v.z) moveInLine(z)
end end
if v.z < 0 then if z < 0 then
turnToFace(BACK) turnToFace(BACK)
moveInLine(-v.z) moveInLine(-z)
end end
mode.v = mode.v + UNIT_Z mode.z = mode.z + z
-- y -- y
moveUpDown(v.y) moveUpDown(y)
mode.v = mode.v + UNIT_Y mode.y = mode.y + y
end end
-------------- DROPPING OFF AND REFUELING ------------------- function dropOffItems (go_back)
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)
-- Turn off mining -- Turn off mining
local mine_state = mode.mine
mode.mine = { forward = true } mode.mine = { forward = true }
-- Note current position (assuming facing FORWARD) -- Note current position (assuming facing FORWARD)
local pos_state = vector.new(mode.v.x, mode.v.y, mode.v.z) local pos_state = { x = mode.x, y = mode.y, z = mode.z }
-- Return to origin, facing BACK -- Return to origin, facing BACK
moveAbs(starting_point) moveAbs(mode.x, mode.y, 0)
moveAbs(V_ZERO) moveAbs(0, 0, 0)
turnToFace(BACK) turnToFace(BACK)
-- deposit items -- deposit items
for slot = 2, 16 do for slot = 2, 16 do
@ -208,102 +127,124 @@ function dropOffItems (starting_point, go_back)
end end
turtle.select(1) turtle.select(1)
-- Return to current position, facing forward -- Return to current position, facing forward
if go_back then if go_back then moveAbs(pos_state.x, pos_state.y, pos_state.z) end
moveAbs(starting_point) -- Turn mining back on, if it was on
moveAbs(pos_state) mode.mine = mine_state
end
end end
function inventoryFull () function inventoryFull ()
-- leave a little extra space just in case -- leave a little extra space just in case
return turtle.getItemCount(FULL_CHECK_SLOT) > 0 return turtle.getItemCount(15) > 0
end end
function checkAndDropOff(starting_point) function moveAndCheck (x, y, z)
if inventoryFull() then dropOffItems(starting_point, true) end move(x, y, z)
if inventoryFull() then dropOffItems(true) end
refuelUntil(FUEL_RESERVE) refuelUntil(FUEL_RESERVE)
end end
function generateNextSteps (w, l)
-- assumptions:
-- 0 for l or w means don't move in that axis
-- 1 for w means mine on right side, -1 means left side
-- any greater absolute value of w assumes that the turtle starts in a
-- column of three and can mine on both sides out of the gate
local DIRECTION = {}
if w < 0 then DIRECTION.X = -1 else DIRECTION.X = 1 end
if l < 0 then DIRECTION.Y = -1 else DIRECTION.Y = 1 end
local remaining_width = math.abs(w) + 1
local column_len = math.abs(l)
local step_buffer = {}
return function ()
local next_step = {}
if #step_buffer == 0 then
if remaining_width < 1 then return nil end
-- Algorithm:
-- 1. mine l blocks in the y-direction
next_step = { x = 0, y = DIRECTION.Y * column_len }
table.insert(step_buffer, next_step)
DIRECTION.Y = -DIRECTION.Y
remaining_width = remaining_width - 1
next_step = { x = 0, y = 0 }
if remaining_width > 0 then
-- 2. go in the x direction for the next y-sweep
next_step.x = DIRECTION.X
table.insert(step_buffer, next_step)
end
end
next_step = table.remove(step_buffer, 1)
return next_step.x, next_step.y
end
end
function minePlane (w, l, h)
-- mine a plane starting from the current location l blocks in the
-- y-direction and w blocks in the x-direction
-- print(string.format("Mining a plane of %d by %d by %d", w, l, h))
mode.mine = { forward = true }
if h == 1 then
mode.mine.up = true
elseif h == -1 then
mode.mine.down = true
elseif h ~= 0 then
mode.mine.up = true
mode.mine.down = true
end
for x, y in generateNextSteps(w, l) do
-- print(string.format("Moving %d, %d relative to current pos.",
-- x, y))
moveAndCheck(x, y, 0)
end
end
function mine (block) function mine (block)
-- assumptions: block is mineable in a single pass and oriented to the -- fuel and go to starting corner
-- 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 height = block.v2.y
if height % 3 == 0 then
mode.mine = { up = true, forward = true, down = true }
elseif height == 2 then
mode.mine = { forward = true, up = true }
elseif height == -2 then
mode.mine = { forward = true, down = true }
end
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
move(start_v)
move(end_v)
end
function canMine (block)
local tooHigh = block.v2.y > 3
-- we can only mine in a 1-wide strip at a time
local tooWide = block.v2.x > 1 and block.v2.z > 1
return not (tooHigh or tooWide)
end
function splitHorizontal (block)
-- TODO make this less naive?
-- Future work: potentially split a large block into smaller blocks
-- based on currentPos (think splitting down the middle instead of
-- splitting off of one end)
-- Future work: decide whether to take a slice off of the x or z direction
-- (criteria?)
return block:take(1, "x")
end
function split (block)
local tooHigh = function (block)
return block.v2.y > 3
end
block = block:orient(getCurrentPos())
if tooHigh(block) then
return block:take(3, "y")
else
return splitHorizontal(block)
end
end
function process (stack, starting_point)
-- START code taken from `mine`
-- fuel and go to starting point
refuelUntil(FUEL_RESERVE) refuelUntil(FUEL_RESERVE)
mode.mine = { forward = true } mode.mine = { forward = true }
moveAbs(starting_point) moveAbs(block.x, block.y, block.z)
-- END local z_dir
-- TODO if block.zoff > 1 then z_dir = 1 else z_dir = -1 end
if #stack == 0 then return nil end local remaining_h = math.abs(block.z + block.zoff - mode.z) + 1
local working = table.remove(stack):orient(getCurrentPos()) -- Initial pass; leave remaining_h at a nice multiple of 3
if canMine(working) then local excess = remaining_h % 3
checkAndDropOff(starting_point) if excess > 0 then
mine(working) minePlane(block.xoff, block.yoff, (excess - 1) * z_dir)
if excess == 1 then
move(0, 0, -z_dir)
end
remaining_h = remaining_h - excess
else else
local new, remainder = split(working, mode.v) move(0, 0, z_dir)
table.insert(stack, remainder) minePlane(block.xoff, block.yoff, 3)
table.insert(stack, new) remaining_h = remaining_h - 3
end
-- Start mining planes
while remaining_h > 0 do
-- calculate orientation of next plane
local w, l
if mode.y == block.y then
l = block.yoff
else
l = -block.yoff
end
local diff_from_start = math.abs(mode.x - block.x)
-- print(string.format("Distance from start: %d", diff_from_start))
if mode.x == block.x then
w = block.xoff
else
w = -block.xoff
end
-- move down into the new plane and mine it
move(0, 0, z_dir * 3)
minePlane(w, l, 3)
remaining_h = remaining_h - 3
end end
return process (stack)
end end
function calculateBlock (v1, v2) function calculateBlock (x1, y1, z1, x2, y2, z2)
-- Using the coordinates, construct a block of certain dimensions, a -- Using the coordinates, construct a block of certain dimensions, a
-- certain distance away. -- certain distance away.
local block = {} local block = {}
@ -319,39 +260,30 @@ function calculateBlock (v1, v2)
block[dim] = corner block[dim] = corner
block[dim .. "off"] = offset block[dim .. "off"] = offset
end end
normalize("x", v1.x, v2.x) normalize("x", x1, x2)
normalize("y", v1.y, v2.y) normalize("y", y1, y2)
normalize("z", v1.z, v2.z) normalize("z", z1, z2)
return new_block( return block
vector.new(block.x, block.y, block.z)
vector.new(block.xoff, block.yoff, block.zoff)
)
end end
function run (v1, v2) function run (x1, y1, z1, x2, y2, z2)
-- TODO calculate starting_point, the place turtle will move to before local block = calculateBlock(x1, y1, z1, x2, y2, z2)
-- moving to the first corner of block mine(block)
local block = new_block(v1, v2 - v1)
process({ block })
-- return to base -- return to base
dropOffItems(starting_point) mode.mine = {}
dropOffItems()
turnToFace(FORWARD) turnToFace(FORWARD)
end end
function usage () function usage ()
print("Usage: mine x1 y1 z1 x2 y2 z2 OR mine x1 y1 z1 OR mine x1 y1 z1 print("Usage: mine x1 y1 z1 x2 y2 z2 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 \ print("Mine from the first set of coordinates to the second in a \
rectangular prism.") rectangular prism.")
print("The coordinates are relative to the facing of the turtle at the \ print("The coordinates are relative to the facing of the turtle at the \
start of the program.") start of the program.")
print("If invoked with only three arguments, act as if they are the last \ 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.") three arguments and substitute 0 0 -1 for the first three.")
print("If invoked with nine arguments, interpret the first two sets as print("Fuel goes in the top-left slot of the turtle's inventory")
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 end
function argsToNumbers () function argsToNumbers ()
@ -364,14 +296,12 @@ end
local arg = argsToNumbers() local arg = argsToNumbers()
if #arg == 3 then if #arg == 3 then
run(V_ZERO, vector.new(arg[1], arg[2], arg[3])) -- TODO make the 3 arg version the same as it used to be
-- i.e. `mine 2 3 1` digs a 2-wide, 3-long, 1-deep hole starting under the
-- bot
run(0, 0, -1, arg[1], arg[2], arg[3])
elseif #arg == 6 then elseif #arg == 6 then
run(vector.new(arg[1], arg[2], arg[3]), vector.new(arg[4], arg[5], arg[6])) run(arg[1], arg[2], arg[3], 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 else
usage() usage()
end end