Created
June 3, 2026 05:30
-
-
Save r33drichards/11c767eb3b9df683d848fc72b86b0728 to your computer and use it in GitHub Desktop.
sanddiver.lua -- CC:Tweaked dock-dive sand quarry (transient-down-fail fix)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- sanddiver.lua -- DOCK-DIVE sand/gravel QUARRY turtle for CC: Tweaked (MC 1.21.8). | |
| -- | |
| -- WHY THIS EXISTS (the real failure): the sibling top-down sandminer.lua assumes | |
| -- the turtle STARTS ON SAND and digs straight down through solid sand. In-world | |
| -- it was placed on a DOCK over WATER, so the block directly below the start is a | |
| -- dock plank / water, NOT sand -> a top-down sandOnly miner immediately reports | |
| -- "rock floor at depth 0" and deposits 0. sanddiver fixes this by DIVING through | |
| -- the dock + water column FIRST, finding the submerged sand/gravel BANK, and only | |
| -- THEN running the (proven) boustrophedon quarry across the bank. | |
| -- | |
| -- SITE GEOMETRY (the baked-in mental model, scanned in-world): | |
| -- start : AIR (turtle sits here on the dock surface, facing the field) | |
| -- -1 : DOCK PLANK (SOLID -- must be dug to begin the descent) | |
| -- ~-2..-9: WATER (turtle.down() SUCCEEDS; inspectDown is FALSE for water) | |
| -- then : the BANK (sand/gravel, a few blocks thick -- e.g. y52..y50) | |
| -- then : STONE floor (the bank bottoms out on rock) | |
| -- The chest sits on the dock directly BEHIND the start (at z=-1 relative to it). | |
| -- | |
| -- WHAT IT DOES, per cycle: | |
| -- 1) DESCEND: dig the dock plank, then descend through the water column. Keep | |
| -- descending while the block below is AIR/WATER. Stop when inspectDown shows | |
| -- SAND/GRAVEL (the bank top -> quarry here) OR shows STONE/other-solid before | |
| -- any sand (-> "no sand found below", stop gracefully, deposit 0, no loop). | |
| -- 2) QUARRY: clear the sand/gravel bank over a width x length footprint using a | |
| -- boustrophedon snake, descending layer by layer ONLY while the block below | |
| -- is sand/gravel (stop at stone). FALLING sand is handled by dig-until-drained | |
| -- loops. sandOnly: never digs non-sand (the single dock plank dug to descend | |
| -- is the only non-sand block touched, and it's expected). | |
| -- 3) RETURN + DEPOSIT: climb back UP through the water column to the dock, return | |
| -- to the start cell, face the chest BEHIND the start, VERIFY it is a chest | |
| -- (drop() ejects to the world otherwise), deposit. Refuel from inventory. | |
| -- | |
| -- NEVER STRANDS: before every venturing move it reserves enough fuel to climb the | |
| -- full dive depth + water column back to the dock AND walk to the chest. refuel(0) | |
| -- burns ANY fuel; fuel is never deposited. The dive depth (water column height) is | |
| -- a real per-cycle fuel cost and is folded into the home-cost math. | |
| -- | |
| -- RESUME AFTER RESTART: dive depth + footprint cursor are persisted every step. | |
| -- Re-place the turtle on the dock start corner and re-run: it loads the state and | |
| -- continues (re-diving to the recorded bank top, re-entering the snake at the exact | |
| -- cell) instead of restarting the job. | |
| -- | |
| -- Block matching is namespace-agnostic substring matching (no numeric IDs), so it | |
| -- works on vanilla + modded + 1.21.8. | |
| ------------------------------------------------------------ CONFIG | |
| local CFG = { | |
| width = 8, -- footprint X across the bank (>=1) | |
| length = 8, -- footprint Z across the bank (>=1) | |
| -- bank_depth = max sand/gravel LAYERS to mine downward through the bank. The | |
| -- turtle also stops a column early when the block below is non-sand (stone | |
| -- floor), so this is an UPPER BOUND -- set it generously for "until rock". | |
| bank_depth = 8, | |
| -- max_dive = safety bound on how deep to search for the bank below the dock | |
| -- (dock plank + water column). If no sand is found within this many blocks the | |
| -- turtle reports "no sand found below" and stops. Set comfortably above the real | |
| -- water depth (real site is ~8-9 to the bank top). | |
| max_dive = 32, | |
| reserve_fuel = 0, -- extra fuel kept on top of the computed return cost | |
| sandOnly = true, -- true = only dig sand/gravel; false = plain quarry | |
| state_file = "sanddiver.state", -- progress persistence (resume after restart) | |
| } | |
| -- Test/automation hook: a global `_SANDDIVER_CFG` table (if present) overrides the | |
| -- fields above. On a real turtle this global is never set, so CFG stands. | |
| if type(_SANDDIVER_CFG) == "table" then | |
| for k, v in pairs(_SANDDIVER_CFG) do CFG[k] = v end | |
| end | |
| ------------------------------------------------------------ | |
| -- ---- block classification ------------------------------------------------ | |
| local function isFallable(name) | |
| if not name then return false end | |
| return name:find("sand") ~= nil or name:find("gravel") ~= nil | |
| end | |
| local function isMineable(name) | |
| if not CFG.sandOnly then return true end | |
| return isFallable(name) | |
| end | |
| local function isChest(name) | |
| return name ~= nil and name:find("chest") ~= nil | |
| end | |
| -- ---- pose / bookkeeping --------------------------------------------------- | |
| -- We track position relative to the BANK-TOP entry corner (the cell directly over | |
| -- the bank that the dive lands on), EXCEPT for `depth` which counts layers below | |
| -- that bank-top corner. `diveDepth` is the vertical distance from the dock start | |
| -- down to the bank top (dock plank + water column). Total height to climb home is | |
| -- therefore diveDepth + depth. | |
| local xPos, zPos = 0, 0 -- horizontal offset from the bank-top entry corner | |
| local depth = 0 -- bank layers descended below the bank top (>=0) | |
| local diveDepth = 0 -- blocks from dock start down to the bank top (>=0) | |
| local xDir, zDir = 0, 1 -- facing; start facing +z (into the field) | |
| local deposited = 0 -- items dropped into the chest (telemetry) | |
| local stopped = false -- graceful stop flag (hit rock floor in sandOnly) | |
| local noSand = false -- dive found no bank (stone/solid below the water) | |
| local diving = false -- true while in the descend phase (suppress capacity) | |
| -- ---- progress cursor (drives the snake AND the resume) -------------------- | |
| local curLayer = 0 -- 0-based index of bank layer currently being mined | |
| local layerCell = 0 -- 0-based index of NEXT cell to enter this layer | |
| local progress = 0 -- total cells entered (monotonic, all layers) | |
| local function turnLeft() turtle.turnLeft(); xDir, zDir = -zDir, xDir end | |
| local function turnRight() turtle.turnRight(); xDir, zDir = zDir, -xDir end | |
| local function turnAround() turnRight(); turnRight() end | |
| -- ---- snake geometry (shared by walker + resume; single source of truth) --- | |
| local function cellToXZ(idx) | |
| local col = math.floor(idx / CFG.length) | |
| local row = idx % CFG.length | |
| local z | |
| if col % 2 == 0 then z = row else z = (CFG.length - 1) - row end | |
| return col, z | |
| end | |
| local LAYER_CELLS = CFG.width * CFG.length | |
| -- ---- fuel ----------------------------------------------------------------- | |
| -- Cost to get back to the dock chest from wherever we are: walk to the bank-top | |
| -- entry corner (|x|+|z|), climb the bank layers we've descended (depth) PLUS the | |
| -- whole dive column (diveDepth) up to the dock, then a couple to face/reach chest. | |
| local function homeCost() | |
| return math.abs(xPos) + math.abs(zPos) + depth + diveDepth + 2 + CFG.reserve_fuel | |
| end | |
| local function refuelTo(need) | |
| if turtle.getFuelLevel() == "unlimited" then return true end | |
| if turtle.getFuelLevel() >= need then return true end | |
| for slot = 1, 16 do | |
| if turtle.getItemCount(slot) > 0 then | |
| turtle.select(slot) | |
| if turtle.refuel(0) then | |
| while turtle.getItemCount(slot) > 0 and turtle.getFuelLevel() < need do | |
| turtle.refuel(1) | |
| end | |
| end | |
| end | |
| if turtle.getFuelLevel() ~= "unlimited" and turtle.getFuelLevel() >= need then | |
| turtle.select(1); return true | |
| end | |
| end | |
| turtle.select(1) | |
| return turtle.getFuelLevel() == "unlimited" or turtle.getFuelLevel() >= need | |
| end | |
| -- ---- state persistence ---------------------------------------------------- | |
| local function saveState() | |
| local f = fs and fs.open and fs.open(CFG.state_file, "w") | |
| if not f then return end | |
| local data = { | |
| xPos=xPos, zPos=zPos, depth=depth, diveDepth=diveDepth, | |
| xDir=xDir, zDir=zDir, | |
| curLayer=curLayer, layerCell=layerCell, progress=progress, | |
| deposited=deposited, stopped=stopped, noSand=noSand, | |
| width=CFG.width, length=CFG.length, bankDepthMax=CFG.bank_depth, | |
| } | |
| if textutils and textutils.serialize then | |
| f.write(textutils.serialize(data)) | |
| else | |
| local parts = {} | |
| for k, v in pairs(data) do parts[#parts+1] = k.."="..tostring(v) end | |
| f.write("{"..table.concat(parts, ",").."}") | |
| end | |
| f.close() | |
| end | |
| local function loadState() | |
| if not (fs and fs.exists and fs.exists(CFG.state_file)) then return nil end | |
| local f = fs.open(CFG.state_file, "r") | |
| if not f then return nil end | |
| local body = f.readAll() | |
| f.close() | |
| if not body or #body == 0 then return nil end | |
| local data | |
| if textutils and textutils.unserialize then | |
| data = textutils.unserialize(body) | |
| else | |
| data = load("return "..body, "state", "t", {}) | |
| if data then data = data() end | |
| end | |
| if type(data) ~= "table" then return nil end | |
| if data.width ~= CFG.width or data.length ~= CFG.length then | |
| print("State file footprint differs from CONFIG -- ignoring, fresh start.") | |
| return nil | |
| end | |
| return data | |
| end | |
| local function applyState(data) | |
| xPos, zPos = data.xPos or 0, data.zPos or 0 | |
| depth = data.depth or 0 | |
| diveDepth = data.diveDepth or 0 | |
| xDir, zDir = data.xDir or 0, data.zDir or 1 | |
| curLayer = data.curLayer or 0 | |
| layerCell = data.layerCell or 0 | |
| progress = data.progress or 0 | |
| deposited = data.deposited or 0 | |
| stopped = data.stopped or false | |
| noSand = data.noSand or false | |
| end | |
| local function clearState() | |
| if fs and fs.exists and fs.exists(CFG.state_file) and fs.delete then | |
| fs.delete(CFG.state_file) | |
| end | |
| end | |
| -- ---- deposit safety ------------------------------------------------------- | |
| local goTo -- forward declaration | |
| local function depositAll() | |
| local ok, item = turtle.inspect() | |
| if not (ok and isChest(item.name)) then | |
| print("DEPOSIT ABORT: not facing a chest -- refusing to drop (would eject).") | |
| return false | |
| end | |
| for slot = 1, 16 do | |
| local n = turtle.getItemCount(slot) | |
| if n > 0 then | |
| turtle.select(slot) | |
| -- NEVER deposit burnable fuel: keep every unit so we can always return. | |
| if turtle.refuel(0) and turtle.getFuelLevel() ~= "unlimited" then | |
| -- fuel slot: keep it. | |
| else | |
| if turtle.drop() then deposited = deposited + n end | |
| end | |
| end | |
| end | |
| turtle.select(1) | |
| return true | |
| end | |
| local function invFull() | |
| for slot = 1, 16 do | |
| if turtle.getItemCount(slot) == 0 then return false end | |
| end | |
| return true | |
| end | |
| -- ---- digging (FALLING-BLOCK AWARE) ---------------------------------------- | |
| local function digForward() | |
| while turtle.detect() do | |
| local ok, item = turtle.inspect() | |
| if ok and not isMineable(item.name) then return "rock" end | |
| if not turtle.dig() then return "rock" end | |
| end | |
| return "ok" | |
| end | |
| local function digDown() | |
| while turtle.detectDown() do | |
| local ok, item = turtle.inspectDown() | |
| if ok and not isMineable(item.name) then return "rock" end | |
| if not turtle.digDown() then return "rock" end | |
| end | |
| return "ok" | |
| end | |
| local function digUp() | |
| while turtle.detectUp() do | |
| local ok, item = turtle.inspectUp() | |
| if ok and not isMineable(item.name) then return "rock" end | |
| if not turtle.digUp() then return "rock" end | |
| end | |
| return "ok" | |
| end | |
| -- ---- capacity guard (return to dock + deposit + refuel when needed) -------- | |
| -- Declared before movement so forward()/down() can call it. returnAndDeposit is | |
| -- defined later; we reference it through a forward declaration. | |
| local returnAndDeposit | |
| local function ensureCapacity() | |
| if diving then return end -- the dive itself can't deposit; guard later | |
| if not refuelTo(homeCost()) then | |
| returnAndDeposit() | |
| end | |
| if invFull() then | |
| returnAndDeposit() | |
| end | |
| end | |
| -- ---- movement primitives (dig-through, falling aware) --------------------- | |
| local function forward() | |
| ensureCapacity() | |
| if stopped then return false end | |
| for _ = 1, 64 do | |
| if turtle.forward() then | |
| xPos = xPos + xDir | |
| zPos = zPos + zDir | |
| return true | |
| end | |
| if turtle.detect() then | |
| if digForward() == "rock" then return false end | |
| else | |
| return false | |
| end | |
| end | |
| return false | |
| end | |
| -- Descend one BANK layer (clears falling sand below). Returns true on success, | |
| -- false if blocked by rock (the bank bottomed out on stone). Used in the quarry. | |
| local function down() | |
| ensureCapacity() | |
| if stopped then return false end | |
| for _ = 1, 64 do | |
| if turtle.down() then | |
| depth = depth + 1 | |
| return true | |
| end | |
| if turtle.detectDown() then | |
| if digDown() == "rock" then return false end | |
| else | |
| return false | |
| end | |
| end | |
| return false | |
| end | |
| -- Ascend one layer (trip home, through bank or water). Dig up if needed. | |
| local function up() | |
| for _ = 1, 64 do | |
| if turtle.up() then | |
| depth = depth - 1 | |
| return true | |
| end | |
| if turtle.detectUp() then | |
| if digUp() == "rock" then return false end | |
| else | |
| return false | |
| end | |
| end | |
| return false | |
| end | |
| -- ---- goTo: navigate back to (x, depthBelowBank, z) facing (xd,zd) ---------- | |
| -- Operates entirely WITHIN the bank/quarry region (depth is bank layers below the | |
| -- bank top). It does NOT touch the dive column -- the dive ascent/descent is done | |
| -- separately by climbToDock / reDive. Climbs toward the bank top first so travel | |
| -- goes through already-cleared bank space. | |
| function goTo(x, y, z, xd, zd) | |
| while depth > y do | |
| if not up() then break end | |
| end | |
| if xPos > x then | |
| while xDir ~= -1 do turnLeft() end | |
| while xPos > x do if turtle.forward() then xPos = xPos - 1 elseif turtle.detect() then digForward() else break end end | |
| elseif xPos < x then | |
| while xDir ~= 1 do turnLeft() end | |
| while xPos < x do if turtle.forward() then xPos = xPos + 1 elseif turtle.detect() then digForward() else break end end | |
| end | |
| if zPos > z then | |
| while zDir ~= -1 do turnLeft() end | |
| while zPos > z do if turtle.forward() then zPos = zPos - 1 elseif turtle.detect() then digForward() else break end end | |
| elseif zPos < z then | |
| while zDir ~= 1 do turnLeft() end | |
| while zPos < z do if turtle.forward() then zPos = zPos + 1 elseif turtle.detect() then digForward() else break end end | |
| end | |
| while depth < y do | |
| if not down() then break end | |
| end | |
| while zDir ~= zd or xDir ~= xd do turnLeft() end | |
| end | |
| -- ---- DIVE: dock start -> bank top ----------------------------------------- | |
| -- From the dock start (depth-into-bank == 0, diveDepth == 0), dig the dock plank | |
| -- and descend through the water column. Keep going while the block below is | |
| -- AIR/WATER (inspectDown FALSE). Stop when the block below is SAND/GRAVEL (the | |
| -- bank top: we are now sitting ON it, ready to quarry) -> returns true. If the | |
| -- block below is non-sand SOLID (stone) before any sand is seen, set noSand and | |
| -- return false ("no sand found below"). diveDepth counts each downward step. | |
| local function dive() | |
| diving = true | |
| -- ensure we can at least climb back the way we came as we go (dive fuel cost). | |
| for step = 1, CFG.max_dive do | |
| -- Reserve fuel to climb back what we've dived plus reach the chest. | |
| if not refuelTo(diveDepth + 2 + CFG.reserve_fuel + 1) then | |
| -- can't even guarantee return -- abort the dive, climb back what we did. | |
| print("Low fuel during dive -- aborting descent.") | |
| diving = false | |
| return false | |
| end | |
| local solid, item = turtle.inspectDown() | |
| if solid then | |
| if isFallable(item.name) then | |
| -- bank top reached: we are sitting directly above sand/gravel. | |
| diving = false | |
| return true | |
| end | |
| -- non-sand solid below (e.g. the dock plank on the very first step, or | |
| -- stone). The dock plank must be dug to descend; stone => no bank. | |
| if isFallable(item.name) == false and not isMineable(item.name) then | |
| -- It's non-mineable in sandOnly terms (dock plank, stone, etc). | |
| -- The FIRST solid we ever meet is the dock plank we are allowed to dig | |
| -- to start the descent. Distinguish "dock plank to dig through" from | |
| -- "stone floor == no bank": we dig it ONLY if it is a non-sand block we | |
| -- have to pass to keep diving AND we have not yet entered water. After we | |
| -- have descended into water, a solid non-sand block below means rock -> | |
| -- no bank. | |
| if diveDepth == 0 then | |
| -- the dock plank directly under the start: dig it to begin the dive. | |
| turtle.digDown() | |
| -- do not increment diveDepth yet; loop re-inspects the now-open cell. | |
| else | |
| -- solid non-sand encountered after diving: this is the rock floor and | |
| -- there was no sand bank above it -> no sand found. | |
| noSand = true | |
| diving = false | |
| return false | |
| end | |
| else | |
| turtle.digDown() | |
| end | |
| else | |
| -- AIR or WATER below: descend one block. A down() into freshly-opened water | |
| -- can FAIL TRANSIENTLY (real CC: the water settles over a tick), with | |
| -- detectDown()/inspectDown() still FALSE (water/air below). A single failed | |
| -- down() must NOT abort the dive -- RETRY it, waiting briefly, up to a bound. | |
| -- Only conclude "truly blocked" if, after retries, a genuinely SOLID, | |
| -- detectable, non-mineable block has appeared below (a real obstruction). | |
| local moved = turtle.down() | |
| if not moved then | |
| for _ = 1, 20 do | |
| -- A solid block appeared below: handle it like the top of the loop -- | |
| -- dig it if mineable (incl. the dock plank pre-water), else it's the | |
| -- rock floor (or an obstruction) -> stop the dive cleanly. | |
| local s, it = turtle.inspectDown() | |
| if s then | |
| if isMineable(it.name) or diveDepth == 0 then | |
| turtle.digDown() | |
| else | |
| noSand = true | |
| diving = false | |
| return false | |
| end | |
| else | |
| -- still water/air below but the move failed: transient -- wait + retry. | |
| if sleep then sleep(0.2) end | |
| end | |
| moved = turtle.down() | |
| if moved then break end | |
| end | |
| end | |
| if moved then | |
| diveDepth = diveDepth + 1 | |
| saveState() | |
| else | |
| -- after the bounded retries we still couldn't descend and there is no | |
| -- solid block to mine below: give up cleanly rather than spinning. | |
| diving = false | |
| return false | |
| end | |
| end | |
| end | |
| -- exhausted max_dive without finding sand: treat as no bank. | |
| noSand = true | |
| diving = false | |
| return false | |
| end | |
| -- ---- climb back up the dive column to the dock ---------------------------- | |
| -- After the quarry phase the turtle is back at the bank-top entry corner (depth==0 | |
| -- relative to the bank). Climb diveDepth blocks up the water column to the dock. | |
| local function climbToDock() | |
| while diveDepth > 0 do | |
| -- RAW vertical move that touches ONLY diveDepth (NOT the bank-depth `depth`). | |
| if turtle.up() then | |
| diveDepth = diveDepth - 1 | |
| saveState() | |
| elseif turtle.detectUp() then | |
| if digUp() == "rock" then break end -- solid non-sand above: give up cleanly | |
| else | |
| break -- transient block we can't pass: stop | |
| end | |
| end | |
| end | |
| -- re-dive to the recorded bank top during RESUME (state restored diveDepth>0 but | |
| -- the live turtle was replaced at the dock). Descends exactly diveDepth blocks, | |
| -- digging the dock plank on the first step. | |
| local function reDive(targetDive) | |
| diving = true | |
| while diveDepth < targetDive do | |
| local solid, item = turtle.inspectDown() | |
| if solid then | |
| turtle.digDown() | |
| -- loop re-inspects; for water/air below it will descend next iteration. | |
| else | |
| if turtle.down() then | |
| diveDepth = diveDepth + 1 | |
| else | |
| if turtle.detectDown() then turtle.digDown() else break end | |
| end | |
| end | |
| end | |
| diving = false | |
| end | |
| -- ---- returnAndDeposit (defined now; referenced by ensureCapacity above) ---- | |
| -- Climb out of the bank to the bank top, walk to the entry corner, climb the dive | |
| -- column to the dock, face the chest behind start, deposit, refuel, then re-dive | |
| -- and walk back to the saved cell to resume. | |
| returnAndDeposit = function() | |
| local sx, sd, sz, sxd, szd = xPos, depth, zPos, xDir, zDir | |
| local savedDive = diveDepth | |
| print("Returning to dock to deposit / refuel...") | |
| goTo(0, 0, 0, 0, 1) -- to bank-top entry corner, facing +z | |
| climbToDock() -- up the water column to the dock surface | |
| turnAround() -- face chest behind start (-z) | |
| if not depositAll() then | |
| print("STOP: no chest behind start; not depositing.") | |
| stopped = true | |
| turnAround() | |
| saveState() | |
| return | |
| end | |
| local want = homeCost() + (CFG.width + CFG.length + depth + savedDive) | |
| if not refuelTo(want) then | |
| print("Low fuel: waiting for fuel in inventory...") | |
| while not refuelTo(want) do | |
| os.pullEvent("turtle_inventory") | |
| refuelTo(want) | |
| end | |
| end | |
| turnAround() -- face field again | |
| saveState() | |
| if not stopped then | |
| print("Resuming...") | |
| reDive(savedDive) -- back down the water column to the bank | |
| goTo(sx, sd, sz, sxd, szd) -- back to the saved quarry cell | |
| end | |
| end | |
| -- ---- one horizontal bank layer (boustrophedon snake), CURSOR-DRIVEN -------- | |
| local function mineLayer() | |
| if layerCell == 0 then | |
| layerCell = 1 | |
| progress = progress + 1 | |
| saveState() | |
| end | |
| while layerCell < LAYER_CELLS do | |
| local fromCol = math.floor((layerCell - 1) / CFG.length) | |
| local toCol = math.floor(layerCell / CFG.length) | |
| if toCol == fromCol then | |
| if stopped then return "stop" end | |
| if not forward() then return "rock" end | |
| else | |
| if (fromCol % 2) == 0 then | |
| turnRight() | |
| if not forward() then return "rock" end | |
| turnRight() | |
| else | |
| turnLeft() | |
| if not forward() then return "rock" end | |
| turnLeft() | |
| end | |
| end | |
| layerCell = layerCell + 1 | |
| progress = progress + 1 | |
| saveState() | |
| end | |
| return "ok" | |
| end | |
| -- =========================================================================== | |
| -- MAIN | |
| -- =========================================================================== | |
| print("sanddiver starting ("..CFG.width.."x"..CFG.length..", bank_depth<=".. | |
| CFG.bank_depth..", max_dive="..CFG.max_dive..", sandOnly=".. | |
| tostring(CFG.sandOnly)..")") | |
| -- RESUME: load state if present (turtle re-placed on the dock start corner). | |
| local resumed = false | |
| local saved = loadState() | |
| if saved then | |
| applyState(saved) | |
| print("RESUMING from saved state: diveDepth="..diveDepth.." layer="..curLayer.. | |
| " cell="..layerCell.."/"..LAYER_CELLS.." bankDepth="..depth) | |
| resumed = true | |
| end | |
| -- Verify a chest is behind the start BEFORE doing anything, so we never strand | |
| -- with a full inventory and nowhere safe to drop. | |
| turnAround() | |
| local cok, citem = turtle.inspect() | |
| turnAround() | |
| if not (cok and isChest(citem.name)) then | |
| printError("There must be a CHEST directly behind me on the dock! Exiting (mined nothing).") | |
| return | |
| end | |
| -- Need enough fuel to dive, quarry a layer, and climb fully back to the dock. | |
| if not refuelTo(CFG.max_dive + CFG.width + CFG.length + CFG.bank_depth + 4) then | |
| printError("Out of fuel and none to burn in inventory. Add fuel. Exiting.") | |
| return | |
| end | |
| if resumed then | |
| -- The world reset the pose to the dock start corner. Reset live pose, then | |
| -- re-dive to the recorded bank top and navigate to the saved quarry cell. | |
| if noSand then | |
| print("Saved state says no sand below -- nothing to do.") | |
| clearState() | |
| return | |
| end | |
| local goDepth = depth | |
| local goX, goZ = xPos, zPos | |
| local goXd, goZd = xDir, zDir | |
| local targetDive = diveDepth | |
| xPos, zPos, depth, diveDepth = 0, 0, 0, 0 | |
| xDir, zDir = 0, 1 | |
| if not stopped then | |
| reDive(targetDive) | |
| goTo(goX, goDepth, goZ, goXd, goZd) | |
| end | |
| end | |
| -- ---- DESCEND PHASE (only if we are still at the dock, i.e. not resumed deep) - | |
| if not resumed then | |
| print("Diving for the sand bank...") | |
| if not dive() then | |
| if noSand then | |
| print("No sand found below (only rock under the water) -- stopping. Deposited 0.") | |
| else | |
| print("Dive aborted before finding sand -- stopping.") | |
| end | |
| -- climb back to the dock so we don't strand in the water, then stop. | |
| climbToDock() | |
| stopped = true | |
| clearState() | |
| print("Done. Deposited "..deposited.." items total.") | |
| return | |
| end | |
| print("Found the bank at dive depth "..diveDepth.." -- quarrying.") | |
| saveState() | |
| end | |
| -- ---- QUARRY PHASE ---------------------------------------------------------- | |
| -- On a FRESH dive we are sitting in the water ON the bank top (block below is | |
| -- sand): descend one layer to get INTO the bank before snake-mining. On a RESUME | |
| -- the reDive+goTo above already restored us to the exact saved bank depth/cell, | |
| -- so we must NOT descend again (that would skip/re-dig a layer). | |
| if not resumed and not stopped and not noSand then | |
| if not down() then | |
| print("Bank top vanished before entry -- stopping.") | |
| stopped = true | |
| end | |
| end | |
| while not stopped and not noSand and curLayer < CFG.bank_depth do | |
| local r = mineLayer() | |
| if r == "rock" then | |
| print("Hit non-sand in bank layer "..(curLayer+1).." (sandOnly) -- stopping.") | |
| break | |
| end | |
| if r == "stop" or stopped then break end | |
| -- finished this layer: go to its start corner to descend cleanly. | |
| goTo(0, depth, 0, 0, 1) | |
| curLayer = curLayer + 1 | |
| layerCell = 0 | |
| saveState() | |
| if curLayer < CFG.bank_depth then | |
| if not down() then | |
| print("Reached rock floor of the bank at bank-depth "..depth.." -- done descending.") | |
| break | |
| end | |
| saveState() | |
| end | |
| end | |
| -- ---- RETURN + FINAL DEPOSIT ------------------------------------------------ | |
| print("Quarry complete -- returning to the dock.") | |
| goTo(0, 0, 0, 0, 1) -- bank-top entry corner | |
| climbToDock() -- up the water column to the dock | |
| turnAround() -- face chest behind start | |
| if depositAll() then | |
| print("Deposited "..deposited.." items into the chest.") | |
| else | |
| print("WARNING: could not deposit (no chest). Items kept in turtle.") | |
| end | |
| turnAround() | |
| clearState() | |
| print("Done. Deposited "..deposited.." items total.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment