local robotApi = require('robot') local turtleUtils = require('turtle-utils') local config = require('config-miner') local LOWER_SIZE_LIMIT = 2; local HIGHER_SIZE_LIMIT = 128; local INITIAL_TARGET_Y = config.targetY - config.startY local TARGET_Z = config.size - 1 local TARGET_X = config.size - 1 local MAX_X = TARGET_X local MAX_Y = INITIAL_TARGET_Y + (config.height - 1) local MAX_Z = TARGET_Z local MIN_PERCENTAGE_NEEDED = 10 local miner = { robot = robotApi.create(), mission = 'unload', -- "unload" | "refuel" | "mine" | "return-home" | "return-mine" | nil started = false, finished = false, lastPositionState = nil, -- { x, y, z, dir } targetY = INITIAL_TARGET_Y } local function saveMinerState() local minerState = { robotState = miner.robot.getState(), mission = miner.mission, started = miner.started, finished = miner.finished, lastPositionState = miner.lastPositionState and textutils.serializeJSON(miner.lastPositionState), targetY = miner.targetY } local file = fs.open('.minerstate', 'w') if not file then error('saveMinerState: cannot open .minerstate file!') end file.write(textutils.serializeJSON(minerState)) file.close() end local function loadMinerState() local file = fs.open('.minerstate', 'r') if not file then return end local serializedMinerState = file.readAll() file.close() local minerState = textutils.unserializeJSON(serializedMinerState) local lastPositionState = nil if minerState.lastPositionState then lastPositionState = minerState.lastPositionState end miner = { robot = robotApi.create(minerState.robotState), mission = minerState.mission, started = minerState.started, finished = minerState.finished, lastPositionState = lastPositionState, targetY = minerState.targetY } end local function cleanupMinerState() fs.delete('.minerstate') end local function isOdd(x) return (x % 2) == 1 end local function robotForceForward() local ok = miner.robot.forward() if ok then return ok end turtleUtils.digAll() return miner.robot.forward() end local function robotForceDown() local ok = miner.robot.down() if ok then return ok end turtle.digDown() return miner.robot.down() end local function robotForceUp() local ok = miner.robot.up() if ok then return ok end turtle.digUp() return miner.robot.up() end local function checkConfig() if config.targetY >= config.startY then error('targetY should be lower than startY') end if config.height < 1 then error('height cannot be lower than 1') end if config.size < LOWER_SIZE_LIMIT then error('size cannot be lower than ' .. LOWER_SIZE_LIMIT) end if config.size > HIGHER_SIZE_LIMIT then error('size is too large (higher limit is ' .. HIGHER_SIZE_LIMIT .. ')') end if isOdd(config.size) then error('configured size should be even') end end local function minerStarted() turtle.select(1) miner.started = true print("> Miner program started, minimum percentgae fuel needed: " .. MIN_PERCENTAGE_NEEDED) end local function minerFinished() cleanupMinerState() print("> Miner program finished!") end local function isProgramFinished() if miner.mission ~= nil then return false end local robotState = miner.robot.getState() return robotState.x == 0 and robotState.y == 0 and robotState.z == 0 end local function unloadProcedure() print('> Starting unload procedure...') turtleUtils.waitForInventory('front') for i=1, 16, 1 do turtle.select(i) turtle.refuel() turtleUtils.tryDrop() end miner.mission = 'refuel' end local function refuelProcedure() print('> Starting refuel procedure (' .. turtleUtils.getFuelPercentage() .. '%)') turtleUtils.waitForInventory('top') turtle.select(1) while turtleUtils.getFuelPercentage() < MIN_PERCENTAGE_NEEDED do turtleUtils.trySuckUp() local ok = turtle.refuel() if not ok then turtleUtils.tryDrop() end end print('> Refuel done (' .. turtleUtils.getFuelPercentage() .. '%)') miner.mission = 'return-mine' end local function returnMineProcedure() if miner.finished then miner.mission = nil -- it will stop the loop return end if miner.robot.lastPositionState then local robotState = miner.robot.getState() if robotState.y > miner.lastPositionState.y then robotForceDown() elseif robotState.z < miner.lastPositionState.z then robotForceForward() else miner.robot.turnRight() for i=1, miner.lastPositionState.x, 1 do robotForceForward() saveMinerState() end while miner.robot.getState().dir ~= miner.lastPositionState.dir do miner.robot.turnRight() saveMinerState() end miner.mission = 'mine' print('> Starting mining procedure...') end else miner.mission = 'mine' print('> Starting mining procedure...') end end local function mineProcedure() local robotState = miner.robot.getState() local targetY = miner.targetY local targetZ = TARGET_Z -- 1. Move if robotState.y > targetY then turtle.digDown() robotForceDown() elseif robotState.dir == 'FORWARD' and robotState.z < targetZ then turtleUtils.digAll() robotForceForward() elseif robotState.dir == 'FORWARD' and robotState.z == targetZ then miner.robot.turnRight() turtleUtils.digAll() robotForceForward() miner.robot.turnRight() elseif robotState.dir == 'BACKWARD' and robotState.z > 0 then turtleUtils.digAll() robotForceForward() elseif robotState.dir == 'BACKWARD' and robotState.z == 0 then miner.robot.turnLeft() turtleUtils.digAll() robotForceForward() miner.robot.turnLeft() end -- 2. Check if robotState.x == MAX_X and robotState.y == MAX_Y and robotState.z == 0 then miner.finished = true miner.mission = 'return-home' miner.lastPositionState = miner.robot.getState() print('> Starting return home procedure because mining session is finished...') elseif robotState.x == MAX_X and robotState.z == 0 then -- this part assume that the size of the mine is even miner.robot.turnRight() for i=1, config.size - 1, 1 do robotForceForward() saveMinerState() end miner.robot.turnRight() -- mine the next stage robotForceUp() miner.targetY = miner.targetY + 1 end if turtleUtils.countFreeSlots() < 1 then print('> Starting return home procedure because inventory is almost full...') miner.mission = 'return-home' miner.lastPositionState = miner.robot.getState() elseif turtleUtils.getFuelPercentage() <= 1 then print('> Starting return home procedure because there is less than 1% of fuel...') miner.mission = 'return-home' miner.lastPositionState = miner.robot.getState() end end local function returnHomeProcedure() local robotState = miner.robot.getState() if robotState.x == 0 and robotState.y == 0 and robotState.z == 0 then miner.mission = 'unload' elseif robotState.x == 0 and robotState.z == 0 then while miner.robot.getState().dir ~= 'FORWARD' do miner.robot.turnRight() end robotForceUp() elseif robotState.x > 0 then if robotState.dir == 'FORWARD' then miner.robot.turnLeft() elseif robotState.dir == 'BACKWARD' then miner.robot.turnRight() end for i=1, robotState.x, 1 do robotForceForward() saveMinerState() end miner.robot.turnLeft() elseif robotState.z > 0 then for i=1, robotState.z, 1 do robotForceForward() end miner.robot.turnLeft() miner.robot.turnLeft() end end local function waitForUserConfirmation() print('> startY: ' .. config.startY) print('> targetY: ' .. config.targetY) print('> height: ' .. config.height) print('> size: ' .. config.size) print('> type "yes" to start the mining process.') local val = read() if val == 'yes' then return true end return false end checkConfig() loadMinerState() if not miner.started then while not waitForUserConfirmation() do end end minerStarted() saveMinerState() while not isProgramFinished() do if miner.mission == 'unload' then unloadProcedure() elseif miner.mission == 'refuel' then refuelProcedure() elseif miner.mission == 'mine' then mineProcedure() saveMinerState() elseif miner.mission == 'return-home' then returnHomeProcedure() saveMinerState() elseif miner.mission == 'return-mine' then returnMineProcedure() saveMinerState() end end minerFinished()