CCCPaste

gFlatTerrain

-- 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.")