cc-stuff/mine.lua
2025-08-18 19:07:18 -04:00

297 lines
8 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 = forward of start, +z = up of start
local FUEL_RESERVE = 800
local FORWARD, RIGHT, BACK, LEFT = 0, 1, 2, 3
local mode = { x = 0, y = 0, z = 0, facing = FORWARD }
mode.mine = { forward = true }
local block = {}
function refuelUntil (amt)
while turtle.getFuelLevel() < amt do
if not turtle.refuel(8) then break end
end
end
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
turtle.forward()
mineSides()
delta = delta - 1
end
end
function moveUpDown (delta)
local delta = delta
while delta > 0 do
if mode.mine.forward then
turtle.digUp()
end
turtle.up()
delta = delta - 1
end
while delta < 0 do
if mode.mine.forward then
turtle.digDown()
end
turtle.down()
delta = delta + 1
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
-- move to a specific point
function moveAbs (x, y, z)
-- print(string.format("Moving to (%d, %d, %d)", x, y, z))
move(x - mode.x, y - mode.y, z - mode.z)
end
-- move relative to the turtle's current location
function move (x, y, z)
-- x
if x > 0 then
turnToFace(RIGHT)
moveInLine(x)
elseif x < 0 then
turnToFace(LEFT)
moveInLine(-x)
end
mode.x = mode.x + x
-- y
if y > 0 then
turnToFace(FORWARD)
moveInLine(y)
end
if y < 0 then
turnToFace(BACK)
moveInLine(-y)
end
mode.y = mode.y + y
-- z
moveUpDown(z)
mode.z = mode.z + z
end
function dropOffItems (go_back)
-- Turn off mining
local mine_state = mode.mine
mode.mine = { forward = true }
-- Note current position (assuming facing FORWARD)
local pos_state = { x = mode.x, y = mode.y, z = mode.z }
-- Return to origin, facing BACK
moveAbs(mode.x, mode.y, 0)
moveAbs(0, 0, 0)
turnToFace(BACK)
-- deposit items
for slot = 2, 16 do
turtle.select(slot)
turtle.drop()
end
turtle.select(1)
-- Return to current position, facing forward
if go_back then moveAbs(pos_state.x, pos_state.y, pos_state.z) end
-- Turn mining back on, if it was on
mode.mine = mine_state
end
function inventoryFull ()
-- leave a little extra space just in case
return turtle.getItemCount(15) > 0
end
function moveAndCheck (x, y, z)
move(x, y, z)
if inventoryFull() then dropOffItems(true) end
refuelUntil(FUEL_RESERVE)
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)
-- fuel and go to starting corner
refuelUntil(FUEL_RESERVE)
mode.mine = { forward = true }
moveAbs(block.x, block.y, block.z)
local z_dir
if block.zoff > 1 then z_dir = 1 else z_dir = -1 end
local remaining_h = math.abs(block.z + block.zoff - mode.z) + 1
-- Initial pass; leave remaining_h at a nice multiple of 3
local excess = remaining_h % 3
if excess > 0 then
minePlane(block.xoff, block.yoff, (excess - 1) * z_dir)
remaining_h = remaining_h - excess
else
move(0, 0, z_dir)
minePlane(block.xoff, block.yoff, 3)
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
function calculateBlock (x1, y1, z1, x2, y2, z2)
-- Using the coordinates, construct a block of certain dimensions, a
-- certain distance away.
local block = {}
local normalize = function (dim, c1, c2)
-- pick the closest point
-- offset goes to the other point
local corner, offset
if math.abs(c1) <= math.abs(c2) then
corner, offset = c1, c2 - c1
else
corner, offset = c2, c1 - c2
end
block[dim] = corner
block[dim .. "off"] = offset
end
normalize("x", x1, x2)
normalize("y", y1, y2)
normalize("z", z1, z2)
return block
end
function run (x1, y1, z1, x2, y2, z2)
local block = calculateBlock(x1, y1, z1, x2, y2, z2)
mine(block)
-- return to base
mode.mine = {}
dropOffItems()
turnToFace(FORWARD)
end
function usage ()
print("Usage: mine x1 y1 z1 x2 y2 z2 OR mine x1 y1 z1")
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 -1 for the first three.")
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
-- 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
run(arg[1], arg[2], arg[3], arg[4], arg[5], arg[6])
else
usage()
end