From a5aec5800e06dcfaf97963b218d352bf2d9f199d Mon Sep 17 00:00:00 2001 From: MayaTheShy Date: Fri, 20 Feb 2026 01:50:55 -0500 Subject: [PATCH] Refactor turtle.lua for v4: Implement eval/response protocol, streamline state management, enhance GPS and movement functions, and improve command handling. --- turtle.lua | 1169 +++++++++++++++------------------------------------- 1 file changed, 324 insertions(+), 845 deletions(-) diff --git a/turtle.lua b/turtle.lua index 86f9ebc..78a99ca 100644 --- a/turtle.lua +++ b/turtle.lua @@ -1,34 +1,26 @@ --- Advanced Autonomous Mining Turtle (FIXED v3) --- Features: GPS tracking, auto-mining, fuel management, smart navigation +-- Advanced Autonomous Mining Turtle v4 (Eval/Response Protocol) +-- Features: Server-driven state machine, UUID command correlation, +-- GPS tracking, local fallback intelligence, block scanning local CHANNEL_RECEIVE = 100 local CHANNEL_SEND = 101 local STATUS_CHANNEL = 102 --- State management +-- State tracking (lightweight - server drives behavior via eval) local state = { - mode = "idle", -- idle, manual, exploring, mining, returning + mode = "idle", position = nil, homePosition = nil, fuel = 0, inventory = {}, - target = nil, - path = {}, + facing = 0, lastStatusUpdate = 0, - stuckCounter = 0, - visitedPositions = {}, -- Track where we've been - lastPosition = nil, - stuckInHoleCounter = 0 + evalSupported = true, } -- Configuration local config = { - minFuelToReturn = 500, - minFuelToExplore = 1000, - statusUpdateInterval = 5, -- seconds - maxStuckAttempts = 3, - maxDistanceFromHome = 200, -- blocks - stay within reasonable range - pauseIfNoPlayersNearby = false, -- set true if chunk loading is concern + statusUpdateInterval = 5, valuableBlocks = { ["minecraft:coal_ore"] = 1, ["minecraft:iron_ore"] = 2, @@ -53,16 +45,17 @@ if not modem then error("No wireless modem found!") end modem.open(CHANNEL_RECEIVE) -print("📻 Modem opened on channel " .. CHANNEL_RECEIVE) -print(" Listening for commands...") - -print("Advanced Mining Turtle System") +print("Advanced Mining Turtle v4 (Eval Protocol)") print("ID: " .. os.getComputerID()) +print("Modem opened on channel " .. CHANNEL_RECEIVE) + +-- Store facing in global for eval access +_G._turtleFacing = 0 + +-- ========== GPS Functions ========== --- GPS Functions local function updatePosition() - -- Quick GPS check with short timeout - local x, y, z = gps.locate(2) -- 2 second timeout + local x, y, z = gps.locate(2) if x then state.position = {x = math.floor(x), y = math.floor(y), z = math.floor(z)} return true @@ -70,70 +63,24 @@ local function updatePosition() return false end --- Sync home position with server (via wireless broadcast) -local function syncHomeWithServer() - -- Request home position from server via webbridge - print("Requesting home position from server...") - modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { - type = "request_home", - turtleID = os.getComputerID() - }) - - -- Don't block - just request and continue - -- The response will be handled in the message processing loop - return true -end - --- Set home position (server-authoritative via wireless) -local function setHome() - print("Setting home position...") - - -- If we don't have current position, try to get it - -- But don't block if we already have it - if not state.position then - if not updatePosition() then - print("Failed to get GPS position for home") - return false - end - end - - -- Set locally immediately - state.homePosition = { - x = state.position.x, - y = state.position.y, - z = state.position.z - } - - -- Send to server via wireless (fire and forget) - modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { - type = "set_home", - turtleID = os.getComputerID(), - position = state.homePosition - }) - - print("✅ Home set at:", textutils.serialize(state.homePosition)) - print(" (Syncing with server in background)") - return true -end - --- Movement tracking -local facing = 0 -- 0=North(Z-), 1=East(X+), 2=South(Z+), 3=West(X-) +-- ========== Movement Functions (with position tracking) ========== local function updateFacingAfterTurn(right) if right then - facing = (facing + 1) % 4 + state.facing = (state.facing + 1) % 4 else - facing = (facing - 1) % 4 + state.facing = (state.facing - 1) % 4 end + _G._turtleFacing = state.facing end local function smartForward() local success = turtle.forward() if success and state.position then - if facing == 0 then state.position.z = state.position.z - 1 - elseif facing == 1 then state.position.x = state.position.x + 1 - elseif facing == 2 then state.position.z = state.position.z + 1 - elseif facing == 3 then state.position.x = state.position.x - 1 + if state.facing == 0 then state.position.z = state.position.z - 1 + elseif state.facing == 1 then state.position.x = state.position.x + 1 + elseif state.facing == 2 then state.position.z = state.position.z + 1 + elseif state.facing == 3 then state.position.x = state.position.x - 1 end end return success @@ -142,10 +89,10 @@ end local function smartBack() local success = turtle.back() if success and state.position then - if facing == 0 then state.position.z = state.position.z + 1 - elseif facing == 1 then state.position.x = state.position.x - 1 - elseif facing == 2 then state.position.z = state.position.z - 1 - elseif facing == 3 then state.position.x = state.position.x + 1 + if state.facing == 0 then state.position.z = state.position.z + 1 + elseif state.facing == 1 then state.position.x = state.position.x - 1 + elseif state.facing == 2 then state.position.z = state.position.z - 1 + elseif state.facing == 3 then state.position.x = state.position.x + 1 end end return success @@ -169,34 +116,27 @@ end local function smartTurnRight() local success = turtle.turnRight() - if success then - updateFacingAfterTurn(true) - end + if success then updateFacingAfterTurn(true) end return success end local function smartTurnLeft() local success = turtle.turnLeft() - if success then - updateFacingAfterTurn(false) - end + if success then updateFacingAfterTurn(false) end return success end --- Fuel management +-- ========== Fuel & Inventory ========== + local function updateFuel() state.fuel = turtle.getFuelLevel() return state.fuel end -local function needsRefuel() - return state.fuel ~= "unlimited" and state.fuel < config.minFuelToReturn -end - local function tryRefuel() for slot = 1, 16 do turtle.select(slot) - if turtle.refuel(0) then -- Check if it's fuel + if turtle.refuel(0) then local count = turtle.getItemCount() if count > 0 then turtle.refuel(1) @@ -207,7 +147,6 @@ local function tryRefuel() return false end --- Inventory management local function updateInventory() state.inventory = {} for slot = 1, 16 do @@ -231,205 +170,20 @@ local function inventoryFull() return true end -local function isValuableBlock(blockName) - return config.valuableBlocks[blockName] ~= nil -end +-- ========== Block Scanning ========== --- Check if block is unbreakable -local function isUnbreakable(blockName) - if not blockName then return false end - return string.find(blockName, "bedrock") or string.find(blockName, "barrier") -end - --- Mining functions -local function smartDig(direction) - direction = direction or "forward" - - local digFunc, inspectFunc - if direction == "forward" then - digFunc = turtle.dig - inspectFunc = turtle.inspect - elseif direction == "up" then - digFunc = turtle.digUp - inspectFunc = turtle.inspectUp - elseif direction == "down" then - digFunc = turtle.digDown - inspectFunc = turtle.inspectDown - end - - local hasBlock, data = inspectFunc() - if hasBlock then - if isUnbreakable(data.name) then - return false, false, data.name - end - local valuable = isValuableBlock(data.name) - digFunc() - return true, valuable, data.name - end - return false, false, nil -end - --- Pathfinding (improved with obstacle handling) -local function getDistance(pos1, pos2) - return math.abs(pos1.x - pos2.x) + math.abs(pos1.y - pos2.y) + math.abs(pos1.z - pos2.z) -end - --- Check if turtle is too far from home -local function isTooFarFromHome() - if not state.position or not state.homePosition then - return false - end - local distance = getDistance(state.position, state.homePosition) - return distance > config.maxDistanceFromHome -end - --- Check if it's safe to continue operations (for chunk loading concerns) -local function shouldPauseOperations() - if not config.pauseIfNoPlayersNearby then - return false -- Feature disabled - end - - -- You can add player detection logic here if using commands - -- For now, just check distance from home as a safety measure - return isTooFarFromHome() -end - --- Try to dig and move forward -local function forceForward() - for i = 1, 5 do - if smartForward() then - return true - end - smartDig("forward") - sleep(0.3) - end - return false -end - --- Try to dig and move up -local function forceUp() - for i = 1, 5 do - if smartUp() then - return true - end - smartDig("up") - sleep(0.3) - end - return false -end - --- Try to dig and move down -local function forceDown() - for i = 1, 5 do - if smartDown() then - return true - end - smartDig("down") - sleep(0.3) - end - return false -end - --- Advanced navigation with unstuck logic -local function navigateTowards(target) - if not state.position or not target then - return false - end - - local dx = target.x - state.position.x - local dy = target.y - state.position.y - local dz = target.z - state.position.z - - local lastPos = {x = state.position.x, y = state.position.y, z = state.position.z} - - -- Prioritize vertical movement if needed - if math.abs(dy) > 3 then - if dy > 0 then - if forceUp() then - state.stuckCounter = 0 - return false - end - else - if forceDown() then - state.stuckCounter = 0 - return false - end - end - end - - -- Horizontal movement - local targetFacing - if math.abs(dx) > math.abs(dz) then - targetFacing = dx > 0 and 1 or 3 - else - targetFacing = dz > 0 and 2 or 0 - end - - -- Face the right direction - while facing ~= targetFacing do - smartTurnRight() - end - - -- Try to move forward - if forceForward() then - state.stuckCounter = 0 - else - -- Stuck! Try alternative routes - state.stuckCounter = state.stuckCounter + 1 - print("Stuck counter: " .. state.stuckCounter) - - if state.stuckCounter > config.maxStuckAttempts then - print("Trying alternative path...") - state.stuckCounter = 0 - - -- Try going up to go over obstacle - if forceUp() then - return false - end - - -- Try going around - smartTurnRight() - if forceForward() then - return false - end - - smartTurnLeft() - smartTurnLeft() - if forceForward() then - return false - end - - -- Last resort: dig down and try lower path - smartTurnRight() - forceDown() - end - end - - -- Check if we're close enough - return getDistance(state.position, target) < 2 -end - --- Comprehensive block scanning - scans all 6 directions around the turtle local function scanAllDirections() - if not state.position then - return {} -- Can't determine block positions without knowing turtle position - end + if not state.position then return {} end local scannedBlocks = {} local myID = os.getComputerID() - -- Helper to add block with absolute world coordinates local function addBlock(relX, relY, relZ, blockData) if blockData and blockData.name then - local worldPos = { + table.insert(scannedBlocks, { x = state.position.x + relX, y = state.position.y + relY, - z = state.position.z + relZ - } - table.insert(scannedBlocks, { - x = worldPos.x, - y = worldPos.y, - z = worldPos.z, + z = state.position.z + relZ, name = blockData.name, metadata = blockData.metadata or 0, discoveredBy = myID @@ -437,104 +191,62 @@ local function scanAllDirections() end end - -- Scan up and down (always accessible) - local hasBlockUp, dataUp = turtle.inspectUp() - if hasBlockUp and dataUp then - addBlock(0, 1, 0, dataUp) - end + local hasBlock, data + hasBlock, data = turtle.inspectUp() + if hasBlock then addBlock(0, 1, 0, data) end - local hasBlockDown, dataDown = turtle.inspectDown() - if hasBlockDown and dataDown then - addBlock(0, -1, 0, dataDown) - end - - -- Scan all 4 horizontal directions by rotating - -- We'll scan forward, then rotate 4 times to get all sides - local originalFacing = facing + hasBlock, data = turtle.inspectDown() + if hasBlock then addBlock(0, -1, 0, data) end + local originalFacing = state.facing for i = 0, 3 do - local hasBlock, data = turtle.inspect() - if hasBlock and data then - -- Calculate relative position based on current facing + hasBlock, data = turtle.inspect() + if hasBlock then local relX, relZ = 0, 0 - if facing == 0 then relZ = -1 -- North - elseif facing == 1 then relX = 1 -- East - elseif facing == 2 then relZ = 1 -- South - elseif facing == 3 then relX = -1 -- West + if state.facing == 0 then relZ = -1 + elseif state.facing == 1 then relX = 1 + elseif state.facing == 2 then relZ = 1 + elseif state.facing == 3 then relX = -1 end addBlock(relX, 0, relZ, data) end - - -- Rotate to next direction (unless this is the last iteration) - if i < 3 then - smartTurnRight() - end + if i < 3 then smartTurnRight() end end - -- We've rotated 3 times (270 degrees), rotate once more to get back to original facing - -- Original facing is at position 0, we're now at position 3, so turn once more to get to 4 (same as 0) - if facing ~= originalFacing then + if state.facing ~= originalFacing then smartTurnRight() end return scannedBlocks end --- Send discovered blocks to server local function reportDiscoveredBlocks(blocks) if #blocks == 0 then return end - - -- Send blocks to webbridge which will forward to server modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { type = "blocks_discovered", turtleID = os.getComputerID(), blocks = blocks, timestamp = os.epoch("utc") }) - - print("📦 Reported " .. #blocks .. " blocks to server") end --- Status broadcasting -function broadcastStatus() +-- ========== Status Broadcasting ========== + +local function broadcastStatus() updateFuel() updateInventory() - -- Don't update position on every broadcast to avoid GPS delays - -- Position will be updated by movement functions - -- Debug: Log current position - if state.position then - print(string.format("Broadcasting position: X=%d Y=%d Z=%d", - state.position.x, state.position.y, state.position.z)) - else - print("Broadcasting with NO POSITION (waiting for GPS)") - end - - print("📡 Broadcasting status on channel " .. STATUS_CHANNEL) - print(" Turtle ID: " .. os.getComputerID()) - print(" Mode: " .. state.mode) - - -- Scan surrounding blocks for map visualization local surroundings = {} local hasBlock, data - -- Check forward hasBlock, data = turtle.inspect() - if hasBlock and data then - surroundings.forward = {name = data.name, metadata = data.metadata or 0} - end + if hasBlock then surroundings.forward = {name = data.name, metadata = data.metadata or 0} end - -- Check up hasBlock, data = turtle.inspectUp() - if hasBlock and data then - surroundings.up = {name = data.name, metadata = data.metadata or 0} - end + if hasBlock then surroundings.up = {name = data.name, metadata = data.metadata or 0} end - -- Check down hasBlock, data = turtle.inspectDown() - if hasBlock and data then - surroundings.down = {name = data.name, metadata = data.metadata or 0} - end + if hasBlock then surroundings.down = {name = data.name, metadata = data.metadata or 0} end local statusPacket = { type = "status", @@ -545,440 +257,165 @@ function broadcastStatus() fuel = state.fuel, inventoryCount = #state.inventory, inventory = state.inventory, - facing = facing, - surroundings = surroundings + facing = state.facing, + surroundings = surroundings, + evalSupported = true, } - print(" Packet type: " .. statusPacket.type) modem.transmit(STATUS_CHANNEL, CHANNEL_RECEIVE, statusPacket) - print(" ✅ Status broadcast complete") end --- Return to home step (non-blocking) -local returnHomeAttempts = 0 -local function returnHomeStep() - if not state.homePosition or not state.position then - print("No home or position set!") - state.mode = "idle" - broadcastStatus() - return true -- Done (failed) - end - - if getDistance(state.position, state.homePosition) <= 1 then - print("Arrived at home!") - state.mode = "idle" - state.stuckCounter = 0 - broadcastStatus() - return true -- Done (success) - end - - if returnHomeAttempts >= 1000 then - print("Could not reach home - gave up after 1000 attempts") - state.mode = "idle" - broadcastStatus() - return true -- Done (failed) - end - - updateFuel() - if needsRefuel() then - print("Low fuel, trying to refuel...") - if not tryRefuel() then - print("WARNING: Out of fuel!") - state.mode = "idle" - broadcastStatus() - return true -- Done (failed) - end - print("Refueled!") - end - - navigateTowards(state.homePosition) - returnHomeAttempts = returnHomeAttempts + 1 - - -- Update GPS position every few steps - if returnHomeAttempts % 10 == 0 then - updatePosition() - print("Distance to home: " .. getDistance(state.position, state.homePosition)) - end - - return false -- Not done yet -end +-- ========== Eval/Response Protocol ========== --- Start return home (just initialize) -local function returnHome() - print("Returning home...") - state.mode = "returning" - state.stuckCounter = 0 - returnHomeAttempts = 0 - broadcastStatus() -end - --- Helper functions for intelligent exploration -local function positionKey(pos) - if not pos then return nil end - return string.format("%d,%d,%d", pos.x, pos.y, pos.z) -end - -local function hasVisited(pos) - local key = positionKey(pos) - return state.visitedPositions[key] ~= nil -end - -local function markVisited(pos) - local key = positionKey(pos) - state.visitedPositions[key] = os.epoch("utc") -end - -local function isStuckInSamePosition() - if not state.lastPosition or not state.position then - return false - end - return state.lastPosition.x == state.position.x and - state.lastPosition.y == state.position.y and - state.lastPosition.z == state.position.z -end - -local function canMoveInDirection(direction) - if direction == "forward" then - local hasBlock = turtle.inspect() - return not hasBlock - elseif direction == "up" then - local hasBlock = turtle.inspectUp() - return not hasBlock - elseif direction == "down" then - local hasBlock = turtle.inspectDown() - return not hasBlock - end - return false -end - -local function isInHole() - -- Check if turtle is in a hole (blocks above preventing escape) - local aboveBlocked = turtle.inspectUp() - local forwardBlocked = turtle.inspect() - - -- If above is blocked and we can't move forward easily, we might be in a hole - return aboveBlocked and forwardBlocked -end - -local function tryClimbOut() - print("Attempting to climb out of hole...") - - -- Try to dig up and go up - for i = 1, 3 do - if canMoveInDirection("up") then - if smartUp() then - print("Climbed up 1 block") - state.stuckInHoleCounter = 0 - return true - end - else - smartDig("up") - sleep(0.1) - end +local function executeEval(uuid, code) + local fn, compileError = load(code, "eval", "t") + if not fn then + modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { + type = "eval_response", + uuid = uuid, + result = nil, + error = "Compile error: " .. tostring(compileError), + turtleID = os.getComputerID() + }) + return end - -- Try all horizontal directions - for i = 1, 4 do - if canMoveInDirection("forward") then - if smartForward() then - print("Moved forward out of hole") - state.stuckInHoleCounter = 0 - return true - end - else - smartDig("forward") - sleep(0.1) - if smartForward() then - print("Dug and moved forward") - state.stuckInHoleCounter = 0 - return true - end - end - smartTurnRight() - end + local ok, result = pcall(fn) - return false -end - --- Exploration algorithm with intelligent navigation -local function exploreStep() - -- Mark current position as visited - if state.position then - markVisited(state.position) - end - - -- Perform comprehensive scan of all surrounding blocks - local discoveredBlocks = scanAllDirections() - if #discoveredBlocks > 0 then - reportDiscoveredBlocks(discoveredBlocks) - end - - -- Check if stuck in same position - if isStuckInSamePosition() then - state.stuckInHoleCounter = state.stuckInHoleCounter + 1 - if state.stuckInHoleCounter > 5 then - print("⚠️ Stuck for " .. state.stuckInHoleCounter .. " steps!") - end + if ok then + modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { + type = "eval_response", + uuid = uuid, + result = result, + error = nil, + turtleID = os.getComputerID() + }) else - state.stuckInHoleCounter = 0 - end - - -- Save current position for next check - if state.position then - state.lastPosition = { - x = state.position.x, - y = state.position.y, - z = state.position.z - } - end - - -- Check all directions for valuable ores first (priority) - for _, direction in ipairs({"forward", "up", "down"}) do - local dug, valuable, blockName = smartDig(direction) - if valuable then - print("💎 Found: " .. blockName) - broadcastStatus() - end - end - - -- Only try to climb out if REALLY stuck (not just after a few steps) - if state.stuckInHoleCounter > 8 then - print("Trying to escape - stuck for too long") - if tryClimbOut() then - return - end - end - - -- Check what's around us - local hasBlockUp, dataUp = turtle.inspectUp() - local hasBlockForward, dataForward = turtle.inspect() - local hasBlockDown, dataDown = turtle.inspectDown() - - -- Calculate positions for each direction - local forwardPos = nil - local upPos = nil - local downPos = nil - - if state.position then - -- Forward position - forwardPos = {x = state.position.x, y = state.position.y, z = state.position.z} - if facing == 0 then forwardPos.z = forwardPos.z - 1 - elseif facing == 1 then forwardPos.x = forwardPos.x + 1 - elseif facing == 2 then forwardPos.z = forwardPos.z + 1 - elseif facing == 3 then forwardPos.x = forwardPos.x - 1 - end - - -- Up and down positions - upPos = {x = state.position.x, y = state.position.y + 1, z = state.position.z} - downPos = {x = state.position.x, y = state.position.y - 1, z = state.position.z} - end - - -- Check which directions have been visited - local forwardVisited = hasVisited(forwardPos) - local upVisited = hasVisited(upPos) - local downVisited = hasVisited(downPos) - - -- Main exploration logic - heavily favor horizontal movement - local r = math.random(1, 100) - - -- 75% of the time: horizontal exploration (forward movement + turns) - if r < 75 then - -- Try to move forward if unvisited - if not forwardVisited then - if hasBlockForward then - smartDig("forward") - end - if smartForward() then - return - end - end - - -- If forward is visited, try to find an unvisited direction - local foundUnvisited = false - for i = 1, 4 do - if state.position then - local testPos = {x = state.position.x, y = state.position.y, z = state.position.z} - if facing == 0 then testPos.z = testPos.z - 1 - elseif facing == 1 then testPos.x = testPos.x + 1 - elseif facing == 2 then testPos.z = testPos.z + 1 - elseif facing == 3 then testPos.x = testPos.x - 1 - end - - if not hasVisited(testPos) then - -- Found unvisited direction! - local hasBlock = turtle.inspect() - if hasBlock then - smartDig("forward") - end - if smartForward() then - foundUnvisited = true - return - end - end - end - - -- Only turn if we haven't found a direction yet - if i < 4 then - smartTurnRight() - end - end - - -- If all directions visited, just move forward anyway (dig if needed) - if not foundUnvisited then - if hasBlockForward then - smartDig("forward") - end - if not smartForward() then - -- Can't move forward at all, turn once and try again next step - smartTurnRight() - end - end - - -- 15% down (only if safe and not too deep) - elseif r < 90 and state.position and state.position.y > 15 and not downVisited then - if hasBlockDown then - smartDig("down") - end - smartDown() - - -- 10% up (only if needed - not visited or somewhat deep) - elseif r < 100 and (not upVisited or (state.position and state.position.y < 10)) then - if hasBlockUp then - smartDig("up") - end - smartUp() - - -- Fallback: just move forward - else - if hasBlockForward then - smartDig("forward") - end - if not smartForward() then - -- Turn if can't move - smartTurnRight() - end + modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { + type = "eval_response", + uuid = uuid, + result = nil, + error = "Runtime error: " .. tostring(result), + turtleID = os.getComputerID() + }) end end --- Command handling -local commands = { +-- ========== Legacy Command Handling ========== + +local legacyCommands = { forward = function() return smartForward(), "Moved forward" end, back = function() return smartBack(), "Moved back" end, up = function() return smartUp(), "Moved up" end, down = function() return smartDown(), "Moved down" end, turnLeft = function() return smartTurnLeft(), "Turned left" end, turnRight = function() return smartTurnRight(), "Turned right" end, - dig = function() - local dug, val, name = smartDig("forward") - return dug, dug and "Dug: " .. (name or "block") or "Nothing to dig" + dig = function() + local hasBlock, data = turtle.inspect() + if hasBlock then + turtle.dig() + return true, "Dug: " .. (data.name or "block") + end + return false, "Nothing to dig" end, digUp = function() - local dug, val, name = smartDig("up") - return dug, dug and "Dug up: " .. (name or "block") or "Nothing above" + local hasBlock, data = turtle.inspectUp() + if hasBlock then + turtle.digUp() + return true, "Dug up: " .. (data.name or "block") + end + return false, "Nothing above" end, digDown = function() - local dug, val, name = smartDig("down") - return dug, dug and "Dug down: " .. (name or "block") or "Nothing below" + local hasBlock, data = turtle.inspectDown() + if hasBlock then + turtle.digDown() + return true, "Dug down: " .. (data.name or "block") + end + return false, "Nothing below" end, place = function() return turtle.place(), "Placed" end, select = function(slot) return turtle.select(slot), "Selected slot " .. slot end, - refuel = function() + refuel = function() local success = tryRefuel() updateFuel() broadcastStatus() return success, success and ("Refueled! Now: " .. state.fuel) or "No fuel" end, - status = function() broadcastStatus() return true, "Status sent" end, - - -- Autonomous commands - setHome = function() return setHome(), "Home set" end, - + status = function() + broadcastStatus() + return true, "Status sent" + end, + setHome = function() + if not state.position then + if not updatePosition() then + return false, "No GPS" + end + end + state.homePosition = { + x = state.position.x, + y = state.position.y, + z = state.position.z + } + modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { + type = "set_home", + turtleID = os.getComputerID(), + position = state.homePosition + }) + return true, "Home set" + end, explore = function() - print("Explore command received") if not state.homePosition then - -- Auto-set home at current position if we have GPS if state.position then - print("Auto-setting home at current position") state.homePosition = { x = state.position.x, y = state.position.y, z = state.position.z } - -- Sync to server modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { type = "set_home", turtleID = os.getComputerID(), position = state.homePosition }) - print("✅ Home auto-set at:", textutils.serialize(state.homePosition)) else - return false, "Home not set and no GPS available! Use setHome first" + return false, "No home or GPS" end end - if isTooFarFromHome() then - return false, "Already at max distance from home" - end state.mode = "exploring" - state.stuckCounter = 0 - state.stuckInHoleCounter = 0 - state.visitedPositions = {} -- Clear visited positions for new exploration - state.lastPosition = nil - print("Starting fresh exploration with intelligent navigation") broadcastStatus() - return true, "Starting exploration" + return true, "Exploring" end, - mine = function() - print("Mine command received") if not state.homePosition then - -- Auto-set home at current position if we have GPS if state.position then - print("Auto-setting home at current position") state.homePosition = { x = state.position.x, y = state.position.y, z = state.position.z } - -- Sync to server modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { type = "set_home", turtleID = os.getComputerID(), position = state.homePosition }) - print("✅ Home auto-set at:", textutils.serialize(state.homePosition)) else - return false, "Home not set and no GPS available! Use setHome first" + return false, "No home or GPS" end end - if isTooFarFromHome() then - return false, "Already at max distance from home" - end - state.mode = "exploring" -- Use exploring mode for mining - state.stuckCounter = 0 - state.stuckInHoleCounter = 0 - state.visitedPositions = {} -- Clear visited positions for new mining - state.lastPosition = nil - print("Starting mining with intelligent navigation") + state.mode = "mining" broadcastStatus() - return true, "Starting mining" + return true, "Mining" end, - returnHome = function() - print("Return home command received") state.mode = "returning" - state.stuckCounter = 0 broadcastStatus() return true, "Returning home" end, - stop = function() - print("STOP command received") state.mode = "idle" - state.stuckCounter = 0 broadcastStatus() return true, "Stopped" end, - manual = function() state.mode = "manual" broadcastStatus() @@ -986,114 +423,206 @@ local commands = { end, } --- Process incoming messages -local function processMessage(message) - if type(message) ~= "table" then - return - end - - -- Check if message is targeted to this turtle +local function processLegacyCommand(message) + if type(message) ~= "table" then return end local myID = os.getComputerID() - if message.target and message.target ~= myID then - -- This command is for a different turtle, ignore it - return - end - - print("Received command message") - print(" Target: " .. tostring(message.target)) - print(" My ID: " .. myID) + if message.target and message.target ~= myID then return end if message.command then - print(" Command: " .. message.command) - -- Don't try to serialize the commands table - it contains functions! - -- Just list the command names - local availableCommands = {} - for cmdName, _ in pairs(commands) do - table.insert(availableCommands, cmdName) - end - print(" Available handlers: " .. table.concat(availableCommands, ", ")) - - local handler = commands[message.command] + local handler = legacyCommands[message.command] if handler then - print(" ✅ Executing command: " .. message.command) local success, result = handler(message.param) - print(" Result: success=" .. tostring(success) .. ", msg=" .. tostring(result)) - modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { status = success and "ok" or "error", message = result or "", turtleID = myID }) - broadcastStatus() - else - print(" ❌ No handler found for command: " .. message.command) end - else - print(" ❌ No command in message") end end --- Main loop -print("Initializing...") -print("Checking for wireless modem...") -if not modem then - print("ERROR: Wireless modem not found!") -else - print("Wireless modem: OK") +-- ========== Message Processing ========== + +local function processMessage(channel, message) + if type(message) ~= "table" then return end + local myID = os.getComputerID() + if message.target and message.target ~= myID then return end + + if message.type == "eval" and message.uuid and message.code then + print("Eval: " .. message.uuid:sub(1, 8) .. "...") + executeEval(message.uuid, message.code) + if message.stateUpdate then + state.mode = message.stateUpdate + end + + elseif message.type == "state_change" and message.state then + state.mode = message.state + broadcastStatus() + + elseif message.type == "home_position" and message.turtleID == myID then + if message.homePosition then + state.homePosition = message.homePosition + print("Home synced from server") + end + + elseif message.type == "home_set_confirm" and message.turtleID == myID then + if message.homePosition then + state.homePosition = message.homePosition + print("Home confirmed by server") + end + + elseif message.command then + processLegacyCommand(message) + end end -print("Attempting quick GPS check...") --- Single quick GPS attempt (5 second timeout instead of 10) +-- ========== Local Autonomous Behavior (fallback) ========== + +local localStuckCounter = 0 + +local function getDistance(pos1, pos2) + return math.abs(pos1.x - pos2.x) + math.abs(pos1.y - pos2.y) + math.abs(pos1.z - pos2.z) +end + +local function localExploreStep() + if state.position then + local blocks = scanAllDirections() + if #blocks > 0 then + reportDiscoveredBlocks(blocks) + end + end + + -- Mine valuable ores + for _, direction in ipairs({"forward", "up", "down"}) do + local inspectFn = direction == "forward" and turtle.inspect + or direction == "up" and turtle.inspectUp + or turtle.inspectDown + local digFn = direction == "forward" and turtle.dig + or direction == "up" and turtle.digUp + or turtle.digDown + + local hasBlock, data = inspectFn() + if hasBlock and config.valuableBlocks[data.name] then + digFn() + end + end + + local r = math.random(1, 100) + if r < 75 then + local hasBlock = turtle.inspect() + if hasBlock then turtle.dig() end + if not smartForward() then + smartTurnRight() + localStuckCounter = localStuckCounter + 1 + else + localStuckCounter = 0 + end + elseif r < 90 and state.position and state.position.y > 15 then + local hasBlock = turtle.inspectDown() + if hasBlock then turtle.digDown() end + smartDown() + else + local hasBlock = turtle.inspectUp() + if hasBlock then turtle.digUp() end + smartUp() + end + + if localStuckCounter > 5 then + turtle.digUp() + smartUp() + localStuckCounter = 0 + end +end + +local function localReturnHomeStep() + if not state.homePosition or not state.position then + state.mode = "idle" + return true + end + + if getDistance(state.position, state.homePosition) <= 1 then + state.mode = "idle" + broadcastStatus() + return true + end + + local dx = state.homePosition.x - state.position.x + local dy = state.homePosition.y - state.position.y + local dz = state.homePosition.z - state.position.z + + if math.abs(dy) > 3 then + if dy > 0 then + turtle.digUp(); smartUp() + else + turtle.digDown(); smartDown() + end + else + local targetFacing + if math.abs(dx) > math.abs(dz) then + targetFacing = dx > 0 and 1 or 3 + else + targetFacing = dz > 0 and 2 or 0 + end + while state.facing ~= targetFacing do + smartTurnRight() + end + turtle.dig() + if not smartForward() then + smartTurnRight() + end + end + + return false +end + +-- ========== Sync Home ========== + +local function syncHomeWithServer() + modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { + type = "request_home", + turtleID = os.getComputerID() + }) + return true +end + +-- ========== Initialization ========== + +print("Initializing...") local x, y, z = gps.locate(5) if x then state.position = {x = math.floor(x), y = math.floor(y), z = math.floor(z)} - print("GPS: OK - Position acquired!") + print("GPS: OK - " .. state.position.x .. "," .. state.position.y .. "," .. state.position.z) else - print("GPS: Not available - will retry in background") - print("Position tracking disabled for now.") - -- Initialize position to nil so we know we need GPS + print("GPS: Not available") state.position = nil - print("⚠️ WARNING: Without GPS, relative positioning will be inaccurate!") - print("⚠️ Please place GPS hosts or set home position manually.") -end - --- Sync state with server -print("Syncing with server...") -if syncHomeWithServer() then - print("✅ Server sync complete") -else - print("⚠️ Server sync failed, using local state") end +syncHomeWithServer() updateFuel() - -print("Ready! Waiting for commands...") -print("Broadcasting initial status...") +print("Ready! Turtle " .. os.getComputerID() .. " online (v4 eval protocol)") broadcastStatus() -print("Turtle " .. os.getComputerID() .. " is online!") --- Main execution loop +-- ========== Main Loop ========== + parallel.waitForAny( function() - -- GPS retry loop for turtles without initial position + -- GPS retry while true do if not state.position then - sleep(10) -- Try every 10 seconds - print("Retrying GPS...") + sleep(10) if updatePosition() then - print("✅ GPS acquired! Position:", textutils.serialize(state.position)) - broadcastStatus() -- Send updated position immediately + print("GPS acquired!") + broadcastStatus() end else - -- Once we have position, just sleep to keep this thread alive sleep(30) end end end, function() - -- Status broadcast loop + -- Status broadcast while true do sleep(config.statusUpdateInterval) broadcastStatus() @@ -1101,98 +630,48 @@ parallel.waitForAny( end, function() - -- Command processing loop - print("🎧 Command listener started - waiting for messages...") + -- Command processing while true do - local event, side, channel, replyChannel, message = os.pullEvent() - - -- Log ALL events for debugging - if event ~= "modem_message" then - -- Skip non-modem events to reduce spam - else - print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") - print("📻 MODEM MESSAGE DETECTED!") - print(" Event: " .. event) - print(" Side: " .. tostring(side)) - print(" Channel: " .. channel) - print(" Reply channel: " .. replyChannel) - print(" Message type: " .. type(message)) - print(" CHANNEL_RECEIVE value: " .. CHANNEL_RECEIVE) - - if type(message) == "table" then - print(" Message contents:") - for k, v in pairs(message) do - print(" " .. k .. " = " .. tostring(v)) - end - end - print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") - - if channel == CHANNEL_RECEIVE then - print(" ✅ Channel matches CHANNEL_RECEIVE!") - - -- Handle home position sync responses - if type(message) == "table" then - if message.type == "home_position" and message.turtleID == os.getComputerID() then - if message.homePosition then - state.homePosition = message.homePosition - print("📍 Synced home from server:", textutils.serialize(message.homePosition)) - else - print("No home position on server") - end - elseif message.type == "home_set_confirm" and message.turtleID == os.getComputerID() then - if message.homePosition then - state.homePosition = message.homePosition - print("✅ Server confirmed home:", textutils.serialize(message.homePosition)) - end - else - -- Regular command processing - print(" Passing to processMessage()") - processMessage(message) - end - else - print(" Message not a table, passing to processMessage()") - processMessage(message) - end - else - print(" ❌ Channel mismatch! Expected " .. CHANNEL_RECEIVE .. ", got " .. channel) - end + local event, side, channel, replyChannel, message = os.pullEvent("modem_message") + if channel == CHANNEL_RECEIVE then + processMessage(channel, message) end end end, function() - -- Autonomous execution loop + -- Local autonomous fallback while true do - if state.mode == "exploring" then + if state.mode == "exploring" or state.mode == "mining" then updateFuel() - -- Safety check: too far from home - if isTooFarFromHome() then - print("Too far from home! Returning...") - returnHome() -- Initialize return home - -- Check if we need to pause (chunk loading concern) - elseif shouldPauseOperations() then - print("Pausing operations (safety limit)") - state.mode = "idle" - broadcastStatus() - -- Check if we should stop - elseif needsRefuel() or inventoryFull() then - print("Need to return home") - returnHome() -- Initialize return home + if state.position and state.homePosition then + local dist = getDistance(state.position, state.homePosition) + if dist > 200 then + state.mode = "returning" + broadcastStatus() + elseif state.fuel ~= "unlimited" and state.fuel < 500 then + if not tryRefuel() then + state.mode = "returning" + broadcastStatus() + end + elseif inventoryFull() then + state.mode = "returning" + broadcastStatus() + else + localExploreStep() + sleep(0.2) + end else - exploreStep() + localExploreStep() sleep(0.2) end elseif state.mode == "returning" then - -- Non-blocking return home step - local done = returnHomeStep() - if not done then - sleep(0.1) - end + local done = localReturnHomeStep() + if not done then sleep(0.1) end else - -- Idle or manual mode sleep(0.5) end end