-- level_snake.lua
-- Turtle leveler in a snake pattern with auto-unload and fueling routine.
-- Usage: run level_snake.lua (the script shows a simple TUI on start)
-- CONFIG DEFAULTS
local DEFAULT_MAX_HEIGHT = 128
local DEFAULT_FUEL_TARGET = 200 -- minimum fuel level to reach after refueling
-- State: coordinates relative to starting position
-- x increases forward from start (initial facing), y increases to the right of initial facing, z increases upward
local pos = { x = 0, y = 0, z = 0 }
local facing = 0 -- 0 = +x (start forward), 1 = +y (right), 2 = -x (back), 3 = -y (left)
local startFacing = 0
local startPos = { x = 0, y = 0, z = 0 } -- origin
local sleepShort = function() sleep(0.2) end
-- Turning helpers
local function turnLeft()
turtle.turnLeft()
facing = (facing + 3) % 4
end
local function turnRight()
turtle.turnRight()
facing = (facing + 1) % 4
end
local function turnAround()
turtle.turnRight(); turtle.turnRight()
facing = (facing + 2) % 4
end
-- Movement with retries
local function tryForward(maxTries)
maxTries = maxTries or 200
local tries = 0
while not turtle.forward() do
tries = tries + 1
if tries > maxTries then return false, "forward blocked" end
if turtle.detect() then turtle.dig() else pcall(turtle.attack) end
sleepShort()
end
if facing == 0 then pos.x = pos.x + 1
elseif facing == 1 then pos.y = pos.y + 1
elseif facing == 2 then pos.x = pos.x - 1
elseif facing == 3 then pos.y = pos.y - 1
end
return true
end
local function tryUp(maxTries)
maxTries = maxTries or 200
local tries = 0
while not turtle.up() do
tries = tries + 1
if tries > maxTries then return false, "up blocked" end
if turtle.detectUp() then turtle.digUp() else pcall(turtle.attackUp) end
sleepShort()
end
pos.z = pos.z + 1
return true
end
local function tryDown(maxTries)
maxTries = maxTries or 200
local tries = 0
while not turtle.down() do
tries = tries + 1
if tries > maxTries then return false, "down blocked" end
if turtle.detectDown() then turtle.digDown() else pcall(turtle.attackDown) end
sleepShort()
end
pos.z = pos.z - 1
return true
end
local function safeDigUp() while turtle.detectUp() do turtle.digUp(); sleepShort() end end
local function safeDigDown() while turtle.detectDown() do turtle.digDown(); sleepShort() end end
local function safeDigForward() while turtle.detect() do turtle.dig(); sleepShort() end end
local function isInventoryFull()
for i = 1, 16 do
if turtle.getItemCount(i) == 0 then return false end
end
return true
end
-- Direction/origin navigation
local function faceDir(dir)
local diff = (dir - facing) % 4
if diff == 1 then turnRight()
elseif diff == 2 then turnAround()
elseif diff == 3 then turnLeft()
end
end
local function moveLineTo(targetX, targetY)
if pos.x < targetX then
faceDir(0)
while pos.x < targetX do local ok, err = tryForward(); if not ok then return false, err end end
elseif pos.x > targetX then
faceDir(2)
while pos.x > targetX do local ok, err = tryForward(); if not ok then return false, err end end
end
if pos.y < targetY then
faceDir(1)
while pos.y < targetY do local ok, err = tryForward(); if not ok then return false, err end end
elseif pos.y > targetY then
faceDir(3)
while pos.y > targetY do local ok, err = tryForward(); if not ok then return false, err end end
end
return true
end
local function moveToStartLevel()
while pos.z > startPos.z do local ok, err = tryDown(); if not ok then return false, err end end
while pos.z < startPos.z do local ok, err = tryUp(); if not ok then return false, err end end
return true
end
local function goHome()
local ok, err = moveToStartLevel(); if not ok then return false, err end
ok, err = moveLineTo(startPos.x, startPos.y); if not ok then return false, err end
faceDir(startFacing)
return true
end
-- Fixed: deposit checks slot counts before/after drop instead of treating drop() as item count
local function depositToChestBehind()
-- Face the chest behind the start (turn around)
turnAround()
local leftover = false
for s = 1, 16 do
local before = turtle.getItemCount(s)
if before > 0 then
turtle.select(s)
-- Try to drop all from this slot
local okDrop = turtle.drop()
local after = turtle.getItemCount(s)
if after > 0 then
leftover = true
end
-- If block in front isn't an inventory, okDrop may be false; still treat as leftover
if not okDrop then leftover = true end
end
end
if not leftover then
-- Keep facing turned-around for the subsequent "move left while still turned around"
return true, false
end
-- Chest full or blocked: step left (relative to current facing) and dump to ground
turnLeft()
local moved, _ = tryForward()
turnRight()
if not moved then
-- Can't step aside; drop forward anyway
for s = 1, 16 do
if turtle.getItemCount(s) > 0 then turtle.select(s); turtle.drop() end
end
else
for s = 1, 16 do
if turtle.getItemCount(s) > 0 then turtle.select(s); turtle.drop() end
end
-- Step back to align with start
turnRight(); tryForward(); turnLeft()
end
return true, true
end
-- Fuel search left of start while still turned-around
local function searchAndRefuel(fuelTarget)
fuelTarget = fuelTarget or DEFAULT_FUEL_TARGET
local fl = turtle.getFuelLevel()
if fl == "unlimited" then return true end
-- Walk up to 8 steps to the left, trying to suck and refuel
for step = 1, 8 do
-- move left while preserving current facing
turnLeft()
local okMove = select(1, tryForward())
turnRight()
if not okMove then break end
-- Try pulling from adjacent inventories
for _ = 1, 4 do
pcall(turtle.suck)
pcall(turtle.suckDown)
pcall(turtle.suckUp)
sleepShort()
end
-- Try to refuel from anything in inventory
for s = 1, 16 do
if turtle.getItemCount(s) > 0 then
turtle.select(s)
if turtle.refuel(1) then
-- Keep consuming this stack until target hit or stack ends
while turtle.getItemCount(s) > 0 do
local lvl = turtle.getFuelLevel()
if lvl == "unlimited" or lvl >= fuelTarget then return true end
if not turtle.refuel(1) then break end
sleepShort()
end
end
end
end
local lvl = turtle.getFuelLevel()
if lvl == "unlimited" or lvl >= fuelTarget then return true end
end
return turtle.getFuelLevel() == "unlimited" or turtle.getFuelLevel() >= fuelTarget
end
-- Column process: your original up/down rule
local maxHeightLimit = DEFAULT_MAX_HEIGHT
local function processColumn()
local loopLimit, loopCount = 2000, 0
while true do
loopCount = loopCount + 1
if loopCount > loopLimit then
print("Loop limit reached in processColumn; aborting column.")
return false
end
if turtle.detectUp() then
safeDigUp()
local ok, err = tryUp()
if not ok then print("Error moving up: " .. tostring(err)); return false end
if pos.z > maxHeightLimit then
print("Reached max height limit, stopping vertical work here.")
break
end
else
if turtle.detectDown() then
break
else
if pos.z == startPos.z then
break
else
local ok, err = tryDown()
if not ok then print("Error moving down: " .. tostring(err)); return false end
end
end
end
end
return true
end
-- Resume state
local resumeState = nil
local function saveResume()
resumeState = { pos = { x = pos.x, y = pos.y, z = pos.z }, facing = facing }
end
local function restoreResume()
if not resumeState then return false, "no resume state" end
local target = resumeState.pos
while pos.z > target.z do local ok, err = tryDown(); if not ok then return false, err end end
while pos.z < target.z do local ok, err = tryUp(); if not ok then return false, err end end
local ok, err = moveLineTo(target.x, target.y); if not ok then return false, err end
faceDir(resumeState.facing)
resumeState = nil
return true
end
-- TUI
local function promptNumber(prompt, default)
write(prompt)
if default then write(" [" .. tostring(default) .. "]") end
write(": ")
local val = read()
if val == "" or val == nil then return default end
local n = tonumber(val)
return n or default
end
-- Main traversal (snake)
local function mainLoop(length, width, maxHeight, fuelTarget)
maxHeightLimit = maxHeight or DEFAULT_MAX_HEIGHT
print(("Starting snake-leveler: length=%d width=%d maxHeight=%d"):format(length, width, maxHeightLimit))
for row = 1, width do
local forwardRow = (row % 2 == 1)
if forwardRow then faceDir(0) else faceDir(2) end
for col = 1, length do
print(("Row %d col %d (x=%d y=%d z=%d f=%d)"):format(row, col, pos.x, pos.y, pos.z, facing))
local ok = processColumn()
if not ok then print("Error in processColumn; aborting."); return false end
-- Unload/refuel if full
if isInventoryFull() then
print("Inventory full: returning to base to unload.")
saveResume()
local okHome, err = goHome()
if not okHome then print("Failed to go home: " .. tostring(err)); return false end
local deposited = select(1, depositToChestBehind())
if not deposited then print("Failed to deposit items at home."); return false end
print("Searching for fuel...")
local refueled = searchAndRefuel(fuelTarget)
if refueled then print("Refueled.") else print("Could not reach desired fuel target. Continuing anyway.") end
local okRestore, errR = restoreResume()
if not okRestore then print("Failed to restore resume position: " .. tostring(errR)); return false end
print("Resumed work.")
end
if col < length then
local okf, errf = tryForward()
if not okf then print("Failed to move to next column: " .. tostring(errf)); return false end
end
end
if row < width then
if forwardRow then
turnRight(); local ok, err = tryForward(); if not ok then print("Failed to step to next row: " .. tostring(err)); return false end; turnRight()
else
turnLeft(); local ok, err = tryForward(); if not ok then print("Failed to step to next row: " .. tostring(err)); return false end; turnLeft()
end
end
end
print("Finished area traversal.")
return true
end
-- Startup TUI
print("=== Turtle Snake Leveler ===")
print("Place turtle at the bottom corner of the area facing along the first row.")
print("Place a chest directly behind the turtle (one block behind the starting facing) to receive drops.")
local length = promptNumber("Enter length (columns per row)", 10)
local width = promptNumber("Enter width (number of rows)", 3)
local maxH = promptNumber("Optional: max relative height to climb (safety)", DEFAULT_MAX_HEIGHT)
local fuelTarget = promptNumber("Optional: desired fuel level after refuel", DEFAULT_FUEL_TARGET)
print(("Parameters: length=%s, width=%s, maxHeight=%s, fuelTarget=%s"):format(tostring(length), tostring(width), tostring(maxH), tostring(fuelTarget)))
print("Press ENTER to start or CTRL+T to cancel.")
read()
local ok, err = pcall(function() return mainLoop(length, width, maxH, fuelTarget) end)
if not ok then print("Run-time error: " .. tostring(err)) end
print("Script done.")