--configurables local CHANNEL = 55555 local TOKEN = "LxdIjZPDLL2oZoayb7uR2Lbz7bnnbkxz" -- Constant local HOST = "HOST" local UNIT = "LEAD" local LUA_PREFIX = "?" -- Constant actions local MOVEBY = "moveby" local MOVETO = "moveto" local STATUS = "status" local POSITION = "position" local HEADING = "heading" local QUARRY = "quarry" local TASK = "task" local FAILED = "failed" -- Constant messaging local LOG = "LOG" local REQUEST = "REQ" -- Constant requests local BLOCKED = "blocked" local ITEM = "item" -- avoid breaking pattern local notPattern = "^allthemodium" -- INIT SEQUENCE print("Initiating leader mining turtle.") running = true local task = nil local taskArgs = {} local modem = nil local heading = nil local position = nil local rightAxis = nil local relHeading = vector.new(1,0,0) local relativePos = vector.new(0,0,0) local relRightAxis = vector.new(0,0,1) do periphs = peripheral.find("modem") if not periphs then error("Modem peripheral not found.") else print("Modem found.") modem = periphs end end print("Opening channel: " .. CHANNEL) modem.open(CHANNEL) -- helpers local function getMessage(data) if string.find(data, TOKEN .. ":") == 1 then print("Validated data token.") local validated = string.sub(data, string.len(TOKEN) + 1 + 1) print("Validated sequence: " .. validated) if string.find(validated, UNIT .. ":") == 1 then print("Checking unit...") return string.sub(validated, string.len(UNIT) + 1 + 1) end end print("Data was unable to be parsed.") return nil end local function SendRequest(request, ...) print("Sending request: " .. request) reqData = { token=TOKEN, recipient=HOST, sender=UNIT, type=REQUEST, request=request, content=arg } modem.transmit(CHANNEL, CHANNEL, textutils.serialise(reqData)) end local function sendLog(log, ...) print("Sending message: " .. log) logData = { token=TOKEN, recipient=HOST, sender=UNIT, type=LOG, log=log, content=arg } modem.transmit(CHANNEL, CHANNEL, textutils.serialise(logData)) end local function captureString(str, seq) if seq == nil then seq = "([^%s]+)" end res = {} for i in string.gmatch(str, seq) do table.insert(res, i) end return res end local function findItem(str) for s=1,16 do if string.sub(str, -string.len(str)) == str then return s end end return 0 end local function refuel() local slot = findItem("coal") if slot == 0 then --TODO: request more coal end turtle.select(slot) turtle.refuel() end local function checkFuel() if turtle.getFuelLevel() > 0 then return false end refuel() return true; end local function vectorEqual(a, b) return a.x == b.x and a.y == b.y and a.z == b.z end local function attemptMove(movef) if not movef() then refuel() return movef(); end return true end local function attemptBreak(breakf, inspectf, notPattern) local exists, data = inspectf() if not exists then return false end if string.match(data.name, pattern) then return false end breakf() end local function locatePosition() local x, y, z = gps.locate() if x == nil then return false end position = vector.new(x, y, z) return true end local function attemptMoveUp() if attemptMove(turtle.up) then relativePos.y = relativePos.y + 1 if position ~= nil then position.y = position.y + 1 end return true end return false end local function attemptMoveDown() if attemptMove(turtle.down) then relativePos.y = relativePos.y - 1 if position ~= nil then position.y = position.y - 1 end return true end return false end local function attemptMoveForward() if attemptMove(turtle.forward) then relativePos = relativePos + relHeading if (position ~= nil) then if ( heading ~= nil ) then position = position + heading else locatePosition() end end return true end return false end local function attemptMoveBackward() if attemptMove(turtle.back) then relativePos = relativePos - relHeading if (position ~= nil) then if (heading ~= nil) then position = position - heading else locatePosition() end end return true end return false end local function attemptMoveLeft() if attemptMove(turtle.turnLeft) then local previousHeading = relHeading relHeading = -relRightAxis relRightAxis = previousHeading if heading ~= nil then local previousHeading = heading heading = -rightAxis rightAxis = previousHeading end return true end return false end local function attemptMoveRight() if attemptMove(turtle.turnRight) then local previousHeading = relHeading relHeading = relRightAxis relRightAxis = -previousHeading if heading ~= nil then local previousHeading = heading heading = rightAxis rightAxis = -previousHeading end return true end return true end local function attemptBreakUp(notPattern) return attemptBreak(turtle.digUp, turtle.inspectUp, notPattern); end local function attemptBreakFront(notPattern) return attemptBreak(turtle.dig, turtle.inspect, notPattern) end local function attemptBreakDown(notPattern) return attemptBreak(turtle.digDown, turtle.inspectDown, notPattern) end local function orient(v, forward, right) print("Attempting to orient such that " .. tostring(v) .. " == " .. tostring(forward) .. ".") if vectorEqual(v, forward) then return true elseif vectorEqual(v, right) then return attemptMoveRight() elseif vectorEqual(v, -right) then return attemptMoveLeft() else return attemptMoveRight() and attemptMoveRight() end return false end local function attemptStartTask(name, f, ...) sendLog(TASK, "Attempting to start task.") if task ~= nil then sendLog(FAILED, "Unable to start task due to another task running.") return end task = {name=name, func=f, args=arg} taskArgs = arg end -- Actions local function determinePosition() if not locatePosition() then sendLog(FAILED, "Unable to obtain GPS based position.") return end sendLog(POSITION, "Position obtained: " .. tostring(position)) end local function determineHeading() sendLog(HEADING, "Calculating heading.") if position == nil and locatePosition() then sendLog(FAILED, "Unable to get current position.") return end local oldPos = vector.new(position.x, position.y, position.z) if not attemptMoveForward() then sendLog(FAILED, "Could not move to get delta.") return end local fDelta = position - oldPos if not attemptMoveBackward() then sendLog(FAILED, "Could not return to original position after first axis.") return end if not attemptMoveRight() then sendLog(FAILED, "Unable to rotate right.") return end if not attemptMoveForward() then sendLog(FAILED, "Unable to move forward after rotation.") return end local rDelta = position - oldPos if not attemptMoveBackward() then sendLog(FAILED, "Could not return to original position after second axis.") return end if not attemptMoveLeft() then sendLog(FAILED, "Unable to rotate left.") return end heading = fDelta rightAxis = rDelta sendLog(HEADING, "Heading obtained. \nForward: " .. textutils.serialiseJSON(heading) .. "\nRight direction: " .. textutils.serialiseJSON(rightAxis)) end local function moveBy(delta, forward, right) sendLog(MOVEBY, "Attempting to move by (" .. delta.x .. "," .. delta.y .. "," .. delta.z .. ") (x,y,z).") -- x first if delta.x ~= 0 then sendLog(MOVEBY, "Moving on x axis by: " .. delta.x) orient(vector.new(delta.x, 0, 0):normalize(), forward, right) for x=1,math.abs(delta.x) do if not attemptMoveForward() then sendLog(FAILED, "Unable to move forward.") return end end end if delta.z ~= 0 then sendLog(MOVEBY, "Moving on z axis by: " .. delta.z) orient(vector.new(0, 0, delta.z):normalize(), forward, right) for z=1,math.abs(delta.z) do if not attemptMoveForward() then sendLog(FAILED, "Unable to move forward.") return end end end if delta.y > 0 then sendLog(MOVEBY, "Moving on y axis by: " .. delta.y) for y=1,delta.y do if not attemptMoveUp() then sendLog(FAILED, "Unable to move up.") return end end elseif delta.y < 0 then sendLog(MOVEBY, "Moving on y axis by: " .. delta.y) for y=1,math.abs(delta.y) do if not attemptMoveDown() then sendLog(FAILED, "Unable to move down.") return end end end end local function moveTo(dest) dest = vector.new(math.floor(dest.x), math.floor(dest.y), math.floor(dest.z)) sendLog(MOVETO, "Attempting to move by (" .. dest.x .. "," .. dest.y .. "," .. dest.z .. ") (x,y,z).") if position == nil or heading == nil then sendLog(FAILED, "Position or heading is invalid.") return end local xBlocked, yBlocked, zBlocked = false, false, false local oldPos = nil while not vectorEqual(dest, position) do if oldPos ~= nil and vectorEqual(oldPos, position) then sendLog(FAILED, "Cannot proceed to due obstacle. XYZ being blocked: " .. xBlocked .. "," .. yBlocked .. "," .. zBlocked) break end oldPos = vector.new(position.x, position.y, position.z); if position.x ~= dest.x then if not orient(vector.new(dest.x - position.x, 0, 0):normalize(), heading, rightAxis) then sendLog(FAILED, "Unable to orient for X axis.") return end xblocked = attemptMoveForward() end if position.y ~= dest.y then if dest.y - position.y > 0 then yBlocked = attemptMoveUp() else yBlocked = attemptMoveDown() end end if position.z ~= dest.z then if not orient(vector.new(0, 0, dest.z - position.z):normalize(), heading, rightAxis) then sendLog(FAILED, "Unable to orient for Z axis.") return end zBlocked = attemptMoveForward() end end end local function quarry(x, y, z, forward, right, pattern) sendLog(QUARRY, "Starting quarry task from positions " .. tostring(a) .. " to " .. tostring(b) ".") for yDelta=1,math.abs(y) do for xDelta=1,math.abs(x) do for zDelta=1,math.abs(z) do orient(vector.new(0, 0, z), forward, right) attemptBreakUp(pattern) attemptBreakDown(pattern) attemptBreakFront(pattern) attemptMoveForward() end orient(vector.new(x, 0, 0), forward, right) attemptMoveForward() end if (y > 0) then attemptMoveUp(); else if not attemptMoveDown() then sendLog(FAILED, "Unable to move further down for quarrying.") end end end end local function executeTasks() while running do if task ~= nil then sendLog(TASK, "Executing task: " .. task["name"]) task["func"](unpack(task["args"])) task = nil end sleep(5) end end local function listen() while running do print("Listening for directives...") local event, side, sendChannel, replyChannel, serialized, distance = os.pullEvent("modem_message") print("Recieved data...") local data = nil if not pcall(function() data = textutils.unserialise(serialized) end) then print("Message already table, not deserializing.") data = serialized end if data["token"] == TOKEN and data["recipient"] == UNIT and data["type"] == REQUEST then print("Got request: " .. data["request"]) if data["request"] == LUA_PREFIX then print("Executing: " .. data["content"][0]) local f = loadstring(luaScript) if f ~= nil then local freturn = f() if freturn ~= nil then sendLog(LUA_PREFIX, textutils.serialise(freturn)) end else printError("Unable to load lua string.") end elseif data["request"] == MOVETO then local x = data["content"][1] local y = data["content"][2] local z = data["content"][3] if heading ~= nil then print("Using actual coordinates.") sendLog(MOVETO, "Using actual coordinates to move.") attemptStartTask(MOVETO, moveTo, vector.new(x,y,z), heading, rightAxis) else print("Using relative coordinates.") sendLog(MOVETO, "Using relative coordinates to move.") attemptStartTask(MOVETO, moveTo, vector.new(x,y,z), relHeading, relRightAxis) end elseif data["request"] == MOVEBY then local x = data["content"][1] local y = data["content"][2] local z = data["content"][3] if heading ~= nil then print("Using actual coordinates.") sendLog(MOVEBY, "Using actual coordinates to move.") attemptStartTask(MOVEBY, moveBy, vector.new(x,y,z), heading, rightAxis) else print("Using relative coordinates.") sendLog(MOVEBY, "Using relative coordinates to move.") attemptStartTask(MOVEBY, moveBy, vector.new(x,y,z), relHeading, relRightAxis) end elseif data["request"] == STATUS then status = { relative_position = { coordinates=relativePos, forward=relHeading, right=relRightAxis }, position={ coordinates=position, forward=heading, right=rightAxis }, fuel=turtle.getFuelLevel(), task=(task and task["name"] or "nothing") } sendLog(STATUS, "Relative position: " .. textutils.serializeJSON(status["relative_position"]) .. "\nPosition: " .. (status["position"]["coordinates"] and textutils.serialiseJSON(status["position"]) or "not positioned.") .. "\nfuel: " .. tostring(status["fuel"]) .. "\ncurrent task: " .. status["task"], status) elseif data["request"] == POSITION then determinePosition() elseif data["request"] == HEADING then determineHeading() elseif data["request"] == QUARRY then if heading ~= nil then quarry(data["content"][1], data["content"][2], data["content"][3], heading, rightAxis) sendLog(QUARRY, "Using actual position for quarrying.") else sendLog(QUARRY, "Using relative position for quarrying.") quarry(data["content"][1], data["content"][2], data["content"][3], relHeading, relRightAxis) end end end end end parallel.waitForAll(listen, executeTasks)