cc-stuff/mine.lua
2025-12-01 09:25:20 -05:00

349 lines
9.1 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"
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
-- 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)
-- TODO similar to "normalize"
error("not yet implemented")
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
end
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
}
bmetatable = {
__name = "Block",
__index = Block,
}
local 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 (%d, %d, %d)", v.x, v.y, v.z))
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 = mode.v + UNIT_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 = mode.v + UNIT_Z
-- y
moveUpDown(v.y)
mode.v = mode.v + UNIT_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)
-- Turn off mining
local mine_state = mode.mine
mode.mine = { forward = true }
-- Note current position (assuming facing FORWARD)
local pos_state = vector.new(mode.v.x, mode.v.y, mode.v.z)
-- 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 current position, facing forward
if go_back then
moveAbs(starting_point)
moveAbs(pos_state)
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(FULL_CHECK_SLOT) > 0
end
function moveAndCheck (v, starting_point)
move(v)
if inventoryFull() then dropOffItems(starting_point, true) end
refuelUntil(FUEL_RESERVE)
end
function mine (block)
-- TODO adjust for new paradigm
-- assumptions: block is mineable in a single pass
-- 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. refuel beforehand
-- 2. move to the ending point
-- 3. drop off items if necessary
move(start_v)
move(end_v)
end
function canMine (block)
local notTooHigh = block.v2.y > 3
-- we can only mine in a 1-wide strip at a time
local notTooWide = block.v2.x > 1 and block.v2.z > 1
return not (tooHigh or tooWide)
end
function splitHorizontal (block)
-- TODO make this less naive?
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)
mode.mine = { forward = true }
moveAbs(starting_point)
-- END
-- TODO
if #stack == 0 then return nil end
local working = table.remove(stack)
if canMine(working) then
mine(working)
else
local new, remainder = split(working, mode.v)
table.insert(stack, remainder)
table.insert(stack, new)
end
return process (stack)
end
function calculateBlock (v1, v2)
-- 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", v1.x, v2.x)
normalize("y", v1.y, v2.y)
normalize("z", v1.z, v2.z)
return new_block(
vector.new(block.x, block.y, block.z)
vector.new(block.xoff, block.yoff, block.zoff)
)
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)
process({ block })
-- return to base
dropOffItems(starting_point)
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