-- turtle_mine_coords.lua (CC:Tweaked)
-- Mine a rectangular volume defined by two GPS corners (x1,x2 then y1,y2 then z1,z2).
-- Uses GPS only to pick and reach the nearest corner, then mines with relative moves.
-- Slot 1 = fuel. Slots 2..16 = filler for water/lava. Rednet protocol tmine.v1.
local PROTO = "tmine.v1"
-- ---------- Modem / Rednet ----------
local function openAnyModem()
for _,side in ipairs(rs.getSides()) do
if peripheral.getType(side) == "modem" then
local m = peripheral.wrap(side)
if m and m.isWireless and m.isWireless() then rednet.open(side); return true end
end
end
return false
end
assert(openAnyModem(), "No wireless modem found/opened")
local function log(msg)
print(msg)
rednet.broadcast({id=os.getComputerID(), msg=msg, t=os.epoch("utc")}, PROTO)
end
-- ---------- GPS ----------
local function gpsPos() local x,y,z = gps.locate(2.0); return x,y,z end
do local x,y,z=gpsPos(); assert(x and y and z,"GPS not available"); log(("GPS lock: (%.1f, %.1f, %.1f)"):format(x,y,z)) end
-- ---------- Fuel ----------
local function refuel(minFuel)
if turtle.getFuelLevel()=="unlimited" then return true end
minFuel = minFuel or 400
if turtle.getFuelLevel() >= minFuel then return true end
local keep = turtle.getSelectedSlot()
turtle.select(1)
while turtle.getFuelLevel() < minFuel do
if turtle.getItemCount(1)==0 then turtle.select(keep); log("Fuel low; slot1 empty"); return false end
turtle.refuel(1)
end
turtle.select(keep); return true
end
-- ---------- Liquids / Filler ----------
local function selectFillSlot()
for s=2,16 do if turtle.getItemCount(s)>0 then turtle.select(s); return true end end
return false
end
local function isLiquid(info) return info and info.name and (info.name:find("water") or info.name:find("lava")) end
local function damFront() local ok,d=turtle.inspect(); if ok and isLiquid(d) then if selectFillSlot() then if turtle.place() then log("Dam front") end end end end
local function damUp() local ok,d=turtle.inspectUp(); if ok and isLiquid(d) then if selectFillSlot() then if turtle.placeUp() then log("Dam up") end end end end
local function damDown() local ok,d=turtle.inspectDown(); if ok and isLiquid(d) then if selectFillSlot() then if turtle.placeDown() then log("Dam down") end end end end
-- ---------- Dig-safe moves ----------
local function fwd()
while true do
damFront()
local ok,d=turtle.inspect(); if ok and not isLiquid(d) then turtle.dig() end
if turtle.forward() then assertInside("forward"); return true end
sleep(0.1)
end
end
local function up()
while true do
damUp()
local ok,d=turtle.inspectUp(); if ok and not isLiquid(d) then turtle.digUp() end
if turtle.up() then assertInside("up"); return true end
sleep(0.1)
end
end
local function down()
while true do
damDown()
local ok,d=turtle.inspectDown(); if ok and not isLiquid(d) then turtle.digDown() end
if turtle.down() then assertInside("down"); return true end
sleep(0.1)
end
end
---------- Heading control ----------
local heading = 0 -- 0=+X,1=+Z,2=-X,3=-Z
local function right() turtle.turnRight(); heading=(heading+1)%4 end
local function left() turtle.turnLeft(); heading=(heading+3)%4 end
local function face(dir)
local d=(dir-heading)%4
if d==1 then right()
elseif d==2 then right(); right()
elseif d==3 then left()
end
end
-- ---------- Move to absolute GPS corner (one-time) ----------
local function moveAxis(axis, delta)
if delta==0 then return end
if axis=="x" then
face(delta>0 and 0 or 2); for i=1,math.abs(delta) do refuel(200); fwd() end
elseif axis=="z" then
face(delta>0 and 1 or 3); for i=1,math.abs(delta) do refuel(200); fwd() end
elseif axis=="y" then
local step = delta>0 and up or down; for i=1,math.abs(delta) do refuel(200); step() end
end
end
local function gotoXYZ(tx,ty,tz)
local x,y,z = gpsPos(); assert(x,"GPS lost")
moveAxis("x", math.floor(tx - x + 0.5))
x,y,z = gpsPos()
moveAxis("z", math.floor(tz - z + 0.5))
x,y,z = gpsPos()
moveAxis("y", math.floor(ty - y + 0.5))
end
-- ---------- Vertical step actions ----------
local function step_3layer()
damUp(); local okU,du=turtle.inspectUp(); if okU and not isLiquid(du) then turtle.digUp() end
damDown(); local okD,dd=turtle.inspectDown(); if okD and not isLiquid(dd) then turtle.digDown() end
end
local function step_2layer()
damDown(); local okD,dd=turtle.inspectDown(); if okD and not isLiquid(dd) then turtle.digDown() end
end
local function step_1layer() end
-- ---------- Serpentine over XZ (relative, bounded) ----------
-- Start at the chosen origin corner on the current Y layer.
-- enterDirX: 0 for +X travel on first row, 2 for -X.
-- Directions based on chosen corner
local DIR_X_POS = (sx==1) and 0 or 2 -- face(+X in world)
local DIR_X_NEG = (sx==1) and 2 or 0 -- face(-X in world)
local DIR_Z_STEP= (sz==1) and 1 or 3 -- step toward interior along Z
local DIR_Z_BACK= (sz==1) and 3 or 1 -- return toward origin along Z
-- enterDirX must be either DIR_X_POS or DIR_X_NEG
local function serpentine_pass(nx, nz, enterDirX, stepAction)
-- First row
face(enterDirX)
stepAction()
for i=2,nx do fwd(); stepAction() end
-- Remaining rows
local curDir = enterDirX
for row=1,(nz-1) do
-- advance one row along Z (toward interior)
face(DIR_Z_STEP); fwd()
-- flip X direction
curDir = (curDir==DIR_X_POS) and DIR_X_NEG or DIR_X_POS
face(curDir)
stepAction()
for i=2,nx do fwd(); stepAction() end
end
-- Return to origin corner in-plane
if (nz % 2)==1 then
-- We ended on the far X edge; go back along X to origin edge
face((curDir==DIR_X_POS) and DIR_X_NEG or DIR_X_POS)
for i=2,nx do fwd() end
end
-- Go back along Z to origin
face(DIR_Z_BACK)
for i=2,nz do fwd() end
end
-- ---------- Input ----------
local function readNum(p) term.write(p); local s=read(); local n=tonumber(s); assert(n,"number"); return n end
term.clear(); term.setCursorPos(1,1)
print("Volume Miner by Coordinates (slot1=fuel; 2..16 filler)")
print("Enter X limits first, then Y, then Z. Any order allowed within each pair.")
local x1 = readNum("x1: "); local x2 = readNum("x2: ")
local y1 = readNum("y1: "); local y2 = readNum("y2: ")
local z1 = readNum("z1: "); local z2 = readNum("z2: ")
local minX,maxX = math.floor(math.min(x1,x2)), math.floor(math.max(x1,x2))
local minY,maxY = math.floor(math.min(y1,y2)), math.floor(math.max(y1,y2))
local minZ,maxZ = math.floor(math.min(z1,z2)), math.floor(math.max(z1,z2))
local nx = maxX - minX + 1
local ny = maxY - minY + 1
local nz = maxZ - minZ + 1
assert(nx>0 and ny>0 and nz>0, "empty volume")
local function assertInside(where)
local x,y,z = gps.locate(2.0)
if not x then error("GPS lost during "..where, 0) end
if x < minX or x > maxX or y < minY or y > maxY or z < minZ or z > maxZ then
error(("ABORT: left volume at %s (%.0f,%.0f,%.0f)"):format(where,x,y,z), 0)
end
end
-- Choose the nearest of the four bottom-layer corners (we’ll handle vertical later)
local cx,cy,cz = gpsPos(); assert(cx,"GPS lost")
local candidates = {
{minX,minY,minZ}, {maxX,minY,minZ}, {minX,minY,maxZ}, {maxX,minY,maxZ},
}
local best,bd=nil,1e18
for _,c in ipairs(candidates) do
local d=math.abs(c[1]-cx)+math.abs(c[2]-cy)+math.abs(c[3]-cz)
if d<bd then bd=d; best=c end
end
local ox,oy,oz = table.unpack(best)
local sx = (ox==minX) and 1 or -1
local sz = (oz==minZ) and 1 or -1
local sy = 1 -- we will mine upward in chunks; oy is at minY
log(("Box X[%d..%d] Y[%d..%d] Z[%d..%d] -> %dx%dx%d"):format(minX,maxX,minY,maxY,minZ,maxZ,nx,ny,nz))
log(("Origin corner: (%d,%d,%d) dir sx=%d sz=%d"):format(ox,oy,oz,sx,sz))
-- Go to origin corner once
gotoXYZ(ox,oy,oz)
-- Orient first-row X direction based on sx
local enterDirX = (sx==1) and DIR_X_POS or DIR_X_NEG
-- Before each pass, ensure we are at the plane origin corner (we already are after our return)
local currentY = oy
local remaining = ny
-- Helper: raise/lower to specific absolute Y by relative move (no GPS during mining)
local function goToYAbs(targetY)
local x,y,z = gpsPos(); assert(x,"GPS lost")
local dy = targetY - y
if dy>0 then for i=1,dy do up() end
elseif dy<0 then for i=1,-dy do down() end end
end
assertInside("start")
-- Vertical policy loop
while remaining > 0 do
refuel(nx*nz*math.min(remaining,3) + 300)
if remaining >= 3 then
-- Middle of the 3-layer chunk
local midY = currentY + 1
goToYAbs(midY)
-- Serpentine on this layer, clearing up and down each cell
serpentine_pass(nx, nz, enterDirX, step_3layer); assertInside("after 3-layer")
currentY = currentY + 3
remaining = remaining - 3
elseif remaining == 2 then
-- Upper of the 2-layer chunk
local upperY = currentY + 1
goToYAbs(upperY)
serpentine_pass(nx, nz, enterDirX, step_2layer); assertInside("after 2-layer")
currentY = currentY + 2
remaining = remaining - 2
else -- remaining == 1
goToYAbs(currentY)
serpentine_pass(nx, nz, enterDirX, step_1layer); assertInside("after 1-layer")
currentY = currentY + 1
remaining = remaining - 1
end
-- After each pass, we are returned to the same XZ origin corner by serpentine_pass.
end
local ex,ey,ez = gpsPos()
log(("Complete. End GPS: (%.1f, %.1f, %.1f)"):format(ex or -1, ey or -1, ez or -1))
print("Mining complete.")
Comments