-- mine_volume.lua (CC:Tweaked Turtle)
-- Mines a prompted rectangular volume from a prompted relative start offset.
-- Optionally fills voids below the lowest Y of that volume with cobble (slot 2).
-- Assumptions:
-- - A fuel container is directly below start (y = -1).
-- - A dump container is directly below that (y = -2).
-- - Slot 2 contains cobblestone at start. Never fully consume or dump it.
-- ======= User-tunable constants =======
local MIN_FUEL_BUFFER = 500
local COBBLE_SLOT = 2
local COBBLE_RESERVE = 16
local RETURN_INTERVAL_STEPS = 400
-- ======================================
local t = turtle
-- state
local pos = {x=0,y=0,z=0} -- start is (0,0,0)
-- heading: 0=+X, 1=+Z, 2=-X, 3=-Z
local heading = 0
local start_heading = heading -- capture initial facing to restore at the end
-- utility: safe read
local function read_num(prompt)
while true do
io.write(prompt); local s = read()
local n = tonumber(s)
if n and math.floor(n) == n then return n end
print("Enter an integer.")
end
end
local function read_yesno(prompt)
while true do
io.write(prompt.." [y/n]: "); local s = (read() or ""):lower()
if s=="y" or s=="yes" then return true end
if s=="n" or s=="no" then return false end
end
end
-- movement wrappers
local function select_nonempty()
for i=1,16 do if t.getItemCount(i)>0 then t.select(i); return true end end
return false
end
local function ensure_dig_forward()
local tries = 0
while t.detect() do
t.dig(); sleep(0.1); tries = tries + 1
if tries>50 then break end
end
end
local function ensure_dig_up()
local tries = 0
while t.detectUp() do
t.digUp(); sleep(0.1); tries = tries + 1
if tries>50 then break end
end
end
local function ensure_dig_down()
local tries = 0
while t.detectDown() do
t.digDown(); sleep(0.1); tries = tries + 1
if tries>50 then break end
end
end
local function forward()
local tries=0
while not t.forward() do
ensure_dig_forward()
if not t.forward() then
t.attack(); sleep(0.1)
tries=tries+1; if tries>50 then return false end
end
end
if heading==0 then pos.x=pos.x+1
elseif heading==1 then pos.z=pos.z+1
elseif heading==2 then pos.x=pos.x-1
else pos.z=pos.z-1 end
return true
end
local function up()
local tries=0
while not t.up() do
ensure_dig_up()
if not t.up() then
t.attackUp(); sleep(0.1)
tries=tries+1; if tries>50 then return false end
end
end
pos.y=pos.y+1; return true
end
local function down()
local tries=0
while not t.down() do
ensure_dig_down()
if not t.down() then
t.attackDown(); sleep(0.1)
tries=tries+1; if tries>50 then return false end
end
end
pos.y=pos.y-1; return true
end
local function turnLeft() heading=(heading+3)%4; t.turnLeft() end
local function turnRight() heading=(heading+1)%4; t.turnRight() end
local function face(dir)
while heading~=dir do
local diff = (dir - heading) % 4
if diff==1 then turnRight()
elseif diff==3 then turnLeft()
else turnRight() end
end
end
local function mine_step_forward()
ensure_dig_forward()
forward()
ensure_dig_up()
ensure_dig_down()
end
-- inventory helpers
local function free_slots()
local free=0
for i=1,16 do
if i~=COBBLE_SLOT and t.getItemCount(i)==0 then free=free+1 end
end
return free
end
local function need_dump()
return free_slots()==0
end
-- refuel from the fuel container directly below start
local function refuel_from_home()
for _=1,8 do
for i=1,16 do
if i~=COBBLE_SLOT then
t.select(i)
t.suckDown(64)
if t.refuel(64) then
if turtle.getFuelLevel()>=MIN_FUEL_BUFFER then return end
else
t.dropDown(64)
end
end
end
sleep(0.2)
end
end
-- go_home with optional final facing
local function go_home(final_face_dir)
while pos.y>0 do up() end
while pos.y<0 do down() end
if pos.x>0 then face(2); for _=1,pos.x do forward() end
elseif pos.x<0 then face(0); for _=1,-pos.x do forward() end end
if pos.z>0 then face(3); for _=1,pos.z do forward() end
elseif pos.z<0 then face(1); for _=1,-pos.z do forward() end end
if final_face_dir~=nil then face(final_face_dir) end
end
-- service pocket near dump chest; preserves caller state
local function ensure_dump_service()
local saved = {x=pos.x,y=pos.y,z=pos.z,h=heading}
-- assume starting from home
face(0)
ensure_dig_forward(); forward() -- (1,0,0)
ensure_dig_down(); down() -- (1,-1,0)
ensure_dig_down(); down() -- (1,-2,0)
face(2); if t.detect() then t.dig() end
forward() -- (0,-2,0)
face(0); forward() -- (1,-2,0)
up(); up() -- (1,0,0)
face(2); forward() -- (0,0,0)
face(saved.h)
-- restore explicit pos since we tracked moves
pos = {x=saved.x,y=saved.y,z=saved.z}; heading = saved.h
end
-- dump inventory except reserved cobble; preserves caller state and returns to home
local function dump_at_home()
local saved = {x=pos.x,y=pos.y,z=pos.z,h=heading}
-- to cavity
face(0); forward() -- (1,0,0); pos updated
down(); down() -- (1,-2,0)
face(2); forward() -- (0,-2,0)
face(2)
for i=1,16 do
if i~=COBBLE_SLOT then
local cnt = t.getItemCount(i)
if cnt>0 then t.select(i); t.drop(64) end
end
end
-- back to home
face(0); forward() -- (1,-2,0)
up(); up() -- (1,0,0)
face(2); forward() -- (0,0,0)
face(saved.h)
pos = {x=saved.x,y=saved.y,z=saved.z}; heading = saved.h
end
local function goto_rel(tx,ty,tz)
while pos.y < ty do up() end
while pos.y > ty do down() end
if pos.x < tx then face(0); for _=1, tx-pos.x do mine_step_forward() end
elseif pos.x > tx then face(2); for _=1, pos.x-tx do mine_step_forward() end end
if pos.z < tz then face(1); for _=1, tz-pos.z do mine_step_forward() end
elseif pos.z > tz then face(3); for _=1, pos.z-tz do mine_step_forward() end end
end
local function ensure_fuel()
if t.getFuelLevel()=="unlimited" then return end
if t.getFuelLevel() < MIN_FUEL_BUFFER then
local saved = {x=pos.x,y=pos.y,z=pos.z,h=heading}
go_home()
refuel_from_home()
ensure_dump_service()
goto_rel(saved.x,saved.y,saved.z); face(saved.h)
end
end
local function maybe_fill_void_below(filling_enabled)
if not filling_enabled then return end
if t.detectDown() then return end
local cnt = t.getItemCount(COBBLE_SLOT)
if cnt and cnt > COBBLE_RESERVE then
t.select(COBBLE_SLOT)
t.placeDown()
end
end
local function mine_cell(floorY, filling_enabled)
ensure_dig_up()
ensure_dig_down()
if pos.y==floorY then maybe_fill_void_below(filling_enabled) end
end
local step_counter = 0
local function periodic_service()
step_counter = step_counter + 1
if step_counter >= RETURN_INTERVAL_STEPS or need_dump() or (t.getFuelLevel()~="unlimited" and t.getFuelLevel()<MIN_FUEL_BUFFER) then
local saved = {x=pos.x,y=pos.y,z=pos.z,h=heading}
go_home()
refuel_from_home()
ensure_dump_service()
dump_at_home()
goto_rel(saved.x,saved.y,saved.z); face(saved.h)
step_counter = 0
end
end
-- ===== Main =====
print("Enter volume dimensions (X width, Z length, Y height) in blocks.")
local X = math.abs(read_num("X (>=1): "))
local Z = math.abs(read_num("Z (>=1): "))
local Y = math.abs(read_num("Y (>=1): "))
print("Enter relative start offset vector from current position.")
local sx = read_num("Start offset dx (right +, left -): ")
local sy = read_num("Start offset dy (up +, down -): ")
local sz = read_num("Start offset dz (forward +, back -): ")
local fill_voids = read_yesno("Fill empty space below the volume’s lowest Y with cobble from slot 2?")
print("Preparing…")
ensure_fuel()
go_home() -- ensure pos=(0,0,0)
ensure_dump_service()
refuel_from_home()
-- move to start offset
goto_rel(sx, sy, sz)
-- define floorY in current relative coordinates
local floorY = pos.y
-- mine by layers from floor up
for layer=0,(Y-1) do
goto_rel(pos.x, floorY + layer, pos.z)
local dirX = 1
goto_rel(sx, floorY + layer, sz)
face(0)
for rz=0,(Z-1) do
if dirX==1 then face(0) else face(2) end
for rx=0,(X-1) do
mine_cell(floorY, fill_voids)
if not (rx==X-1 and rz==Z-1) then
if rx < X-1 then
ensure_fuel()
mine_step_forward()
end
end
periodic_service()
end
if rz < Z-1 then
face(1); mine_step_forward()
dirX = 1 - dirX
end
end
end
-- finish: return home, unload/refuel, then restore original facing
go_home()
ensure_dump_service()
dump_at_home()
refuel_from_home()
face(start_heading)
print("Done.")
Comments