CCCPaste

CC:Tweaked Advanced Chunk Miner

------------------------------------------------------------
-- Advanced Chunk Miner (single-file)
-- - Turtle starts in the SOUTH-WEST corner, facing NORTH.
-- - Chest is directly SOUTH (behind the turtle) at (0, startY, 1).
-- - Prompts for number of layers to mine (C option).
-- Features:
--   * Tag-based ore detection (forge:ores) with name fallback
--   * Ore logging to "ore_log.txt"
--   * Robust drop-off & refuel routine (returns to chest, dumps, returns)
--   * State save/restore (resume) to "miner_state"
--   * Optional torch placement every TORCH_INTERVAL steps (0 to disable)
--   * Will attempt to bypass ore blocks (move up-forward-down) to preserve them
------------------------------------------------------------

-- ============ USER CONFIG =============
local WIDTH = 16
local LENGTH = 16
local TORCH_INTERVAL = 0   -- set >0 to place torch every N forward steps (placeUp)
local AUTO_CHEST = false   -- not implemented auto-place; left as future hook
local STATE_FILE = "miner_state"
local ORE_LOG = "ore_log.txt"
local startY = 0

-- Turtle starts facing NORTH in SW corner
-- Coordinates system: x increases East, z increases South, y increases up
local x, y, z = 0, startY, 0
local dir = 0 -- 0 = north, 1 = east, 2 = south, 3 = west

-- Chest is directly SOUTH behind start: (0, startY, 1)
local chestX, chestY, chestZ = 0, startY, 1
local chestFaceDir = 0 -- to interact with chest we should face north (0) when at chest pos

-- ============ UTIL: persistence & logging ============
local function saveState(t)
    local ok, err
    local f = fs.open(STATE_FILE, "w")
    if not f then
        print("Warning: can't open state file for writing.")
        return
    end
    f.write(textutils.serialize(t))
    f.close()
end

local function loadState()
    if not fs.exists(STATE_FILE) then return nil end
    local f = fs.open(STATE_FILE, "r")
    if not f then return nil end
    local content = f.readAll()
    f.close()
    local ok, data = pcall(textutils.unserialize, content)
    if ok then return data end
    return nil
end

local function appendOreLog(s)
    local f = fs.open(ORE_LOG, "a")
    if not f then
        print("Warning: couldn't open ore log.")
        return
    end
    f.writeLine(s)
    f.close()
end

-- ============ MOVEMENT HELPERS (update x,y,z,dir) ============
local function turnRight()
    turtle.turnRight()
    dir = (dir + 1) % 4
end

local function turnLeft()
    turtle.turnLeft()
    dir = (dir + 3) % 4
end

local function faceDirection(target)
    while dir ~= target do turnRight() end
end

-- safer forward: tries to avoid digging ores by inspecting first.
-- if blocked by ore, attempts up(); forward(); down() bypass. If bypass fails, will dig non-ore blocks.
local function forwardSafe()
    -- quick attempt
    if turtle.forward() then
        if dir == 0 then z = z - 1
        elseif dir == 1 then x = x + 1
        elseif dir == 2 then z = z + 1
        else x = x - 1 end
        return true
    end

    -- blocked: inspect the block ahead
    local ok, data = turtle.inspect()
    if ok then
        local function isOreByData(d)
            if not d then return false end
            if d.tags and d.tags["forge:ores"] == true then return true end
            if d.name and tostring(d.name):find("_ore") then return true end
            return false
        end

        if isOreByData(data) then
            -- try bypass: up, forward, down
            print("Bypassing ore at forward: " .. (data.name or "unknown"))
            if turtle.up() then
                y = y + 1
                if turtle.forward() then
                    if dir == 0 then z = z - 1
                    elseif dir == 1 then x = x + 1
                    elseif dir == 2 then z = z + 1
                    else x = x - 1 end
                    -- attempt to go back down
                    if turtle.down() then
                        y = y - 1
                        return true
                    else
                        -- can't go down: try to continue (still okay)
                        return true
                    end
                else
                    -- couldn't forward after up: try to dig non-ore above and retry
                    local ok2, upData = turtle.inspectUp()
                    if ok2 and not isOreByData(upData) then
                        turtle.digUp()
                        if turtle.up() then
                            y = y + 1
                            if turtle.forward() then
                                if dir == 0 then z = z - 1
                                elseif dir == 1 then x = x + 1
                                elseif dir == 2 then z = z + 1
                                else x = x - 1 end
                                if turtle.down() then y = y - 1 end
                                return true
                            end
                        end
                    end
                end
            end
            -- fallback: do not dig the ore. signal blocked.
            return false
        else
            -- it's not an ore — dig until it's clear
            while not turtle.forward() do
                print("Digging non-ore forward...")
                turtle.dig()
                sleep(0.2)
            end
            if dir == 0 then z = z - 1
            elseif dir == 1 then x = x + 1
            elseif dir == 2 then z = z + 1
            else x = x - 1 end
            return true
        end
    else
        -- couldn't inspect, try digging as last resort
        while not turtle.forward() do
            turtle.dig()
            sleep(0.2)
        end
        if dir == 0 then z = z - 1
        elseif dir == 1 then x = x + 1
        elseif dir == 2 then z = z + 1
        else x = x - 1 end
        return true
    end
end

local function upSafe()
    while not turtle.up() do
        local ok, d = turtle.inspectUp()
        if ok and d then
            -- if ore above, do not dig it; attempt to move by forward-first if possible
            if (d.tags and d.tags["forge:ores"]) or (d.name and tostring(d.name):find("_ore")) then
                -- can't go up through ore; abort
                return false
            else
                turtle.digUp()
            end
        else
            turtle.digUp()
        end
        sleep(0.2)
    end
    y = y + 1
    return true
end

local function downSafe()
    while not turtle.down() do
        local ok, d = turtle.inspectDown()
        if ok and d then
            if (d.tags and d.tags["forge:ores"]) or (d.name and tostring(d.name):find("_ore")) then
                return false
            else
                turtle.digDown()
            end
        else
            turtle.digDown()
        end
        sleep(0.2)
    end
    y = y - 1
    return true
end

-- absolute goTo using safe moves (vertical first)
local function goTo(tx, ty, tz)
    -- vertical
    while y < ty do
        if not upSafe() then error("Cannot move up while going to target") end
    end
    while y > ty do
        if not downSafe() then error("Cannot move down while going to target") end
    end

    -- X
    if x < tx then
        faceDirection(1)
        while x < tx do forwardSafe() end
    elseif x > tx then
        faceDirection(3)
        while x > tx do forwardSafe() end
    end

    -- Z
    if z < tz then
        faceDirection(2)
        while z < tz do forwardSafe() end
    elseif z > tz then
        faceDirection(0)
        while z > tz do forwardSafe() end
    end
end

-- ============ INVENTORY & FUEL ============
local function refuel()
    if turtle.getFuelLevel() == "unlimited" then return true end
    for i = 1, 16 do
        turtle.select(i)
        if turtle.refuel(0) then turtle.refuel(1) end
    end
    return turtle.getFuelLevel() > 0
end

local function isInventoryFull()
    for i = 1, 16 do
        if turtle.getItemCount(i) == 0 then return false end
    end
    return true
end

local function dumpInventoryAtChest()
    -- assume we are at chest location and facing chest facing north
    for i = 1, 16 do
        turtle.select(i)
        turtle.drop()
    end
end

-- dropOff: saves current position, goes to chest, dumps, refuels, returns, restores dir
local function dropOff()
    print("Inventory full — returning to chest.")
    local sx, sy, sz, sd = x, y, z, dir

    -- go to chest coordinates
    goTo(chestX, chestY, chestZ)

    -- face chest (chest is SOUTH of start, when at chestX,chestY,chestZ we face NORTH to access it)
    faceDirection(chestFaceDir)

    dumpInventoryAtChest()
    refuel()

    -- return to saved spot
    goTo(sx, sy, sz)
    faceDirection(sd)
    print("Returned to work location.")
end

-- ============ ORE DETECTION ============
local function isOreByData(d)
    if not d then return false end
    if d.tags and d.tags["forge:ores"] == true then return true end
    if d.name and tostring(d.name):find("_ore") then return true end
    return false
end

local function safeDigFrontAndLogIfOre()
    local ok, data = turtle.inspect()
    if ok and isOreByData(data) then
        local msg = string.format("ORE FOUND (front) @ %d,%d,%d  name=%s", x, y, z, tostring(data.name))
        print(msg)
        appendOreLog(msg)
        return false
    else
        turtle.dig()
        return true
    end
end

local function safeDigUpAndLogIfOre()
    local ok, data = turtle.inspectUp()
    if ok and isOreByData(data) then
        local msg = string.format("ORE FOUND (up) @ %d,%d,%d  name=%s", x, y, z, tostring(data.name))
        print(msg)
        appendOreLog(msg)
        return false
    else
        turtle.digUp()
        return true
    end
end

local function safeDigDownAndLogIfOre()
    local ok, data = turtle.inspectDown()
    if ok and isOreByData(data) then
        local msg = string.format("ORE FOUND (down) @ %d,%d,%d  name=%s", x, y, z, tostring(data.name))
        print(msg)
        appendOreLog(msg)
        return false
    else
        turtle.digDown()
        return true
    end
end

-- ============ MINING LOGIC ============
local stepCounter = 0
local function maybePlaceTorch()
    if TORCH_INTERVAL > 0 and (stepCounter % TORCH_INTERVAL) == 0 then
        -- place torches up if possible (user should have torches in selected slot)
        local ok, data = turtle.inspectUp()
        if not ok then
            turtle.placeUp() -- best-effort
        end
    end
end

local function mineLayer()
    -- assumes starting in the SW corner facing north, will mine WIDTH x LENGTH area
    for row = 1, WIDTH do
        for col = 1, LENGTH - 1 do
            if isInventoryFull() then dropOff() end
            refuel()
            -- dig the block in front unless it's an ore (we'll log ores)
            safeDigFrontAndLogIfOre()
            -- move forward safely (bypass ore if needed)
            local moved = forwardSafe()
            if not moved then
                -- if forward blocked by ore and bypass failed, attempt to step right-left route:
                -- try sidestep: right, forward, left. This is complex; we'll attempt one pattern.
                print("Attempting sidestep around obstacle...")
                turnRight()
                if forwardSafe() then
                    turnLeft()
                    -- now continue
                else
                    -- revert
                    turnLeft()
                    -- try left then right
                    turnLeft()
                    if forwardSafe() then
                        turnRight()
                    else
                        -- stuck: try to break non-ore blocks in front and continue
                        turnRight()
                        print("Stuck; attempting to dig forward (non-ore) until clear.")
                        while not forwardSafe() do
                            turtle.dig()
                            sleep(0.2)
                        end
                    end
                end
            end
            stepCounter = stepCounter + 1
            maybePlaceTorch()
        end

        -- end of row: move to next row if required
        if row < WIDTH then
            if (row % 2) == 1 then
                -- turned approach: currently facing north; to move one block east and turn south
                turnRight()
                safeDigFrontAndLogIfOre()
                forwardSafe()
                turnRight()
            else
                turnLeft()
                safeDigFrontAndLogIfOre()
                forwardSafe()
                turnLeft()
            end
            stepCounter = stepCounter + 1
            maybePlaceTorch()
        end
    end
end

-- ============ MAIN CONTROL ============

-- Load saved state (if present)
local saved = loadState()
local targetLayers = nil
local currentLayer = 1

if saved then
    -- saved may include: x,y,z,dir,currentLayer,targetLayers,stepCounter
    if saved.targetLayers then
        targetLayers = saved.targetLayers
        currentLayer = saved.currentLayer or 1
        x = saved.x or x; y = saved.y or y; z = saved.z or z; dir = saved.dir or dir
        stepCounter = saved.stepCounter or stepCounter
        print("Resuming saved run. Current layer:", currentLayer, "Target layers:", targetLayers)
    else
        -- old save format: ignore and start fresh
        print("Found save but no target layers — starting fresh.")
    end
end

-- if no targetLayers yet, prompt user
if not targetLayers then
    print("How many layers would you like to mine? (enter a number, e.g. 4)")
    local input = read()
    local n = tonumber(input)
    if n and n > 0 then
        targetLayers = math.floor(n)
    else
        print("Invalid input. Defaulting to 1 layer.")
        targetLayers = 1
    end
    currentLayer = 1
end

-- sanity check
if targetLayers < 1 then targetLayers = 1 end

print(string.format("Starting mining: %d layers (starting at layer %d).", targetLayers, currentLayer))

-- main loop: mine layer-by-layer
local success = true
while currentLayer <= targetLayers do
    print(string.format("Mining layer %d / %d at y=%d", currentLayer, targetLayers, y))
    -- mine current layer
    mineLayer()

    -- after finishing layer, attempt to move down to next layer
    -- but first try safeDigDown — if it's an ore, we won't dig it and will try to bypass
    local downOk, downData = turtle.inspectDown()
    if downOk and isOreByData(downData) then
        -- if there's ore immediately below the turtle's current position, try move forward then down to find a clear spot
        print("Ore below turtle; attempting to find clear spot to descend...")
        -- try to move to a known clear adjacent position and descend
        local descended = false
        -- try north
        faceDirection(0)
        if forwardSafe() and downSafe() then
            descended = true
        else
            -- try east
            faceDirection(1)
            if forwardSafe() and downSafe() then
                descended = true
            else
                -- try south
                faceDirection(2)
                if forwardSafe() and downSafe() then
                    descended = true
                else
                    -- try west
                    faceDirection(3)
                    if forwardSafe() and downSafe() then
                        descended = true
                    end
                end
            end
        end

        if not descended then
            print("Unable to find safe descend spot. Attempting to dig down if non-ore.")
            if not safeDigDownAndLogIfOre() then
                -- blocked by ore and cannot bypass: stop
                print("Blocked by ore below and cannot bypass. Stopping.")
                success = false
                break
            else
                if not downSafe() then
                    print("Could not move down after digging. Stopping.")
                    success = false
                    break
                end
            end
        end
    else
        -- no ore below — dig down and descend
        if not safeDigDownAndLogIfOre() then
            -- ore detected and logged, but safeDigDown returned false (we preserved ore)
            -- attempt bypass: move forward then down etc (attempt a direct down)
            if not downSafe() then
                print("Cannot move down. Stopping.")
                success = false
                break
            end
        else
            -- we dug down (or it was air) — now move down
            if not downSafe() then
                print("Can't move down; stopping.")
                success = false
                break
            end
        end
    end

    -- flip orientation to continue next layer in same pattern
    -- we want turtle to face same direction to start the next layer sweep; rotate 180
    turnRight() turnRight()

    -- increment layer and save state
    currentLayer = currentLayer + 1
    saveState{ x = x, y = y, z = z, dir = dir, currentLayer = currentLayer, targetLayers = targetLayers, stepCounter = stepCounter }
end

-- finished or aborted: save final state
saveState{ x = x, y = y, z = z, dir = dir, currentLayer = currentLayer, targetLayers = targetLayers, stepCounter = stepCounter }

if success then
    print("Mining complete for requested layers.")
else
    print("Mining stopped prematurely. Check logs and environment.")
end

print("Final position:", x, y, z, "dir:", dir)