Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- *********************************
- -- Room Miner Turtle Program
- -- By Jharakn
- -- *********************************
- -- *********************** --
- -- Create Global Variables --
- -- *********************** --
- -- using computercraft GPS nomenclature so x is north/south y is east/west and z is up/down
- current = {x=0, y=0, z=0, d=0}
- home = {x=0, y=0, z=0}
- --dig point that the turtle was last at, might be a little out of date due to the writescheduler
- dig = {x=0, y=0, z=0, d=0}
- -- defined at startup these boundaries mark the edge of the room
- boundary = {xMax=0, xMin=0, yMax=0, yMin=0, zMax=0, zMin=0}
- -- these directions will be updated with the gps function, numbers set to absolute
- -- values based on north/easet/south/west values
- direction = {forward=0, right=0, back=0, left =0}
- -- various operation flags for the turtle
- flag = {xReverse=false, yReverse=false, atHome=false, digging=true, digComplete=false}
- -- misc variables for the turtle
- filterSlots = 1
- -- misc variables that are not written to the save file
- writeSchedulerCounter = 0
- -- ********************************************************************** --
- -- close the program dispaying error message the function was called with --
- -- ********************************************************************** --
- function errorStop(displayMessage)
- broadcast("Error - "..displayMessage)
- print("")
- print("turtle encountered an error and stopped")
- print("message recieved was:")
- print(displayMessage)
- print("")
- print("press r to restart")
- while true do
- local event, character = os.pullEvent()
- if event == "char" and character == "r" then break end
- end
- os.reboot()
- end
- -- ****************************************************** --
- -- prints a debug message of most of the global variables --
- -- call with true to end the program too --
- -- ****************************************************** --
- function debugger(endProgram)
- term.clear()
- term.setCursorPos(1, 1)
- print("x coords")
- print("xMin:", boundary.xMin, " current.x:", current.x, " xMax:", boundary.xMax)
- print("y coords")
- print("yMin:", boundary.yMin, " current.y:", current.y, " yMax:", boundary.yMax)
- print("zMin:", boundary.zMin, " current.z:", current.z, " zMax:", boundary.zMax)
- print("flag.xReverse=", flag.xReverse, " flag.yReverse=", flag.yReverse)
- print("current.d:", current.d)
- if endProgram == true then
- errorStop("debugger wanted to close program")
- end
- end
- -- ************************************ --
- -- halts the program until c is pressed --
- -- ************************************ --
- function halt()
- while true do
- local event, character = os.pullEvent()
- if event == "char" and character == "c" then break end
- end
- end
- -- **************************************************************** --
- -- Broadcasts a message the function was called with to the rednet --
- -- **************************************************************** --
- function broadcast(message)
- print(message)
- rednet.open("right")
- if (os.getComputerLabel() == nil) then
- rednet.broadcast(message)
- else
- -- Broadcast the message (prefixed with the turtle's id)
- rednet.broadcast("[".. os.getComputerLabel().."] "..message)
- end
- rednet.close("right")
- end
- -- ************************************************************ --
- -- a variaty of tests to ensure the turtle hasn't wandered off --
- -- ************************************************************ --
- function positionCheck()
- --5 is added to the boundarys just in case the turtle is picked up and dropped outside the boundarys of the room by a player
- if current.x < (boundary.xMin -5) then
- errorStop("turtle Moved outside its boundaries")
- end
- if current.x < (boundary.xMax +5) then
- errorStop("turtle Moved outside its boundaries")
- end
- if current.y < (boundary.yMin -5) then
- errorStop("turtle Moved outside its boundaries")
- end
- if current.y < (boundary.xMax +5) then
- errorStop("turtle Moved outside its boundaries")
- end
- end
- -- **************************** --
- -- Move Up and update current.z --
- -- **************************** --
- function moveUp()
- local counter = 0
- while turtle.up() == false do
- turtle.digUp()
- turtle.attackUp()
- counter = counter + 1
- if counter == 30 then break end
- end
- if counter == 100 then
- errorStop("timeout on dig up")
- else
- current.z = current.z + 1
- end
- end
- -- ************************************************* --
- -- Move Down and update current.z, call with true to --
- -- make the function terminate early and return --
- -- false if it can't move for detecting bedrock --
- -- ************************************************* --
- function moveDown(bedrockDetect)
- local counter = 0
- local returnVar = true
- while turtle.down() == false do
- turtle.digDown()
- turtle.attackDown()
- counter = counter + 1
- if bedrockDetect == true and counter == 2 then
- returnVar = false
- break
- end
- if counter == 30 then break end
- end
- if counter == 100 then
- errorStop("timeout on dig down")
- else
- if returnVar == true then
- current.z = current.z - 1
- end
- return returnVar
- end
- end
- -- ********************************************** --
- -- Move Forward and update current.x or current.y --
- -- ********************************************** --
- function moveForward()
- local counter = 0
- while turtle.forward() == false do
- turtle.dig()
- turtle.attack()
- counter = counter + 1
- if counter == 30 then break end
- end
- if counter == 100 then
- errorStop("timeout on dig forward")
- else
- if current.d == 1 then current.y = current.y + 1 end
- if current.d == 2 then current.x = current.x + 1 end
- if current.d == 3 then current.y = current.y - 1 end
- if current.d == 4 then current.x = current.x - 1 end
- end
- end
- -- ****************************************************************** --
- -- Turn the turtle based on input and current.d and update current.d --
- -- ****************************************************************** --
- function turn(turnToDirection)
- while current.d ~= turnToDirection do
- if current.d < turnToDirection then
- if current.d == 1 and turnToDirection == 4 then
- turtle.turnLeft()
- current.d = 4
- else
- turtle.turnRight()
- current.d = current.d + 1
- end
- else
- if current.d == 4 and turnToDirection == 1 then
- turtle.turnRight()
- current.d = 1
- else
- turtle.turnLeft()
- current.d = current.d - 1
- end
- end
- end
- end
- -- ************************************************************** --
- -- Goto as specific location and turn to face the right direction --
- -- if reverse is true the turtle matches x then y then z rather --
- -- than z then y then x --
- -- ************************************************************** --
- function goto(x, y, z, d, reverse)
- if reverse == false then
- while current.z ~= z do -- match z location
- if current.z < z then
- moveUp()
- writeScheduler()
- fuelCheck()
- elseif current.z > z then
- moveDown(false)
- writeScheduler()
- fuelCheck()
- end
- end
- elseif reverse == true then
- while current.x ~= x do -- match x location
- if current.x < x then
- turn(2)
- moveForward()
- writeScheduler()
- fuelCheck()
- elseif current.x > x then
- turn(4)
- moveForward()
- writeScheduler()
- fuelCheck()
- end
- end
- else
- errorStop("goto function incorrectly called")
- end
- while current.y ~= y do -- match y location
- if current.y < y then
- turn(1)
- moveForward()
- writeScheduler()
- fuelCheck()
- elseif current.y > y then
- turn(3)
- moveForward()
- writeScheduler()
- fuelCheck()
- end
- end
- if reverse == false then
- while current.x ~= x do -- match x location
- if current.x < x then
- turn(2)
- moveForward()
- writeScheduler()
- fuelCheck()
- elseif current.x > x then
- turn(4)
- moveForward()
- writeScheduler()
- fuelCheck()
- end
- end
- elseif reverse == true then
- while current.z ~= z do -- match z location
- if current.z < z then
- moveUp()
- writeScheduler()
- fuelCheck()
- elseif current.z > z then
- moveDown(false)
- writeScheduler()
- fuelCheck()
- end
- end
- else
- errorStop("goto function incorrectly called")
- end
- turn(d) -- turn to face the right direction
- end
- -- *************************************************************************************************** --
- -- Use the GPS function to detect turtle position and move forward and back to detect facing direction --
- -- if called with true will update direction table --
- -- *************************************************************************************************** --
- function GPS(updateDirection)
- local forward = 0
- local right = 0
- local back = 0
- local left = 0
- rednet.open("right")
- local x1, y1, z1 = gps.locate(5,false)
- if x1 == nil then
- errorStop("no GPS signal")
- end
- --If the turtle finds itself below layer 5 move back up to layer 5, then run the gps again
- --otherwise it might get blocked by bedrock
- if z1 < 5 then
- local counter = z1
- while counter ~= 5 do
- moveUp()
- counter = counter + 1
- end
- x1, y1, z1 = gps.locate(5,false)
- if x1 == nil then
- errorStop("no GPS signal")
- end
- end
- moveForward()
- local x2, y2, z2 = gps.locate(5,false)
- if x2 == nil then
- errorStop("no GPS signal")
- end
- turtle.back()
- rednet.close("right")
- if y1 - y2 == -1 then --have taken two measurments can use the change to work out which direction the turtle moved in
- current.d = 1; forward = 1; right = 2; back = 3; left = 4
- elseif x1 - x2 == -1 then
- current.d = 2; forward = 2; right = 3; back = 4; left = 1
- elseif y1 - y2 == 1 then
- current.d = 3; forward = 3; right = 4; back = 1; left = 2
- elseif x1 - x2 == 1 then
- current.d = 4; forward = 4; right = 1; back = 2; left = 3
- else
- errorStop("both GPS samples are identical, what?!?!?")
- end
- current.x = x1; current.y = y1; current.z = z1
- if updateDirection == true then
- direction.forward = forward; direction.right = right; direction.back = back; direction.left = left
- end
- end
- -- ************************************************************** --
- -- write the gloabal variables to a file named MinerInstructions --
- -- NOTE: this function should always be called with pcall --
- -- or a server lag spike might crash the program --
- -- ************************************************************** --
- function writeVariables()
- local writeTable = {} --build a table to serialise into the new file with all the global variables
- writeTable["current_x"] = current.x; writeTable["current_y"] = current.y; writeTable["current_z"] = current.z; writeTable["current_d"] = current.d
- writeTable["home_x"] = home.x; writeTable["home_y"] = home.y; writeTable["home_z"] = home.z
- writeTable["dig_x"] = dig.x; writeTable["dig_y"] = dig.y; writeTable["dig_z"] = dig.z; writeTable["dig_d"] = dig.d
- writeTable["boundary_xMax"] = boundary.xMax; writeTable["boundary_xMin"] = boundary.xMin; writeTable["boundary_yMax"] = boundary.yMax; writeTable["boundary_yMin"] = boundary.yMin; writeTable["boundary_zMax"] = boundary.zMax; writeTable["boundary_zMin"] = boundary.zMin
- writeTable["direction_forward"] = direction.forward; writeTable["direction_right"] = direction.right; writeTable["direction_back"] = direction.back; writeTable["direction_left"] = direction.left
- writeTable["flag_xReverse"] = flag.xReverse; writeTable["flag_yReverse"] = flag.yReverse; writeTable["flag_atHome"] = flag.atHome; writeTable["flag_digging"] = flag.digging; writeTable["flag_digComplete"] = flag.digComplete
- writeTable["filterSlots"] = filterSlots
- local file = fs.open("MinerInstructions","w") --serialise and write the writeTable variable
- file.write(textutils.serialize(writeTable))
- file.close()
- end
- -- ******************************************************************** --
- -- calls the write function ever nth time, designed to prevent constant --
- -- write calls generating server lag --
- -- ******************************************************************** --
- function writeScheduler()
- --adjust this variable for delay to instruction saving, higher value = less server lag but a greater error when the turtle is restarting
- local saveDelay = 10
- writeSchedulerCounter = writeSchedulerCounter + 1
- if writeSchedulerCounter == saveDelay then
- if pcall(writeVariables) == false then
- broadcast("Warning - Write failure")
- end
- writeSchedulerCounter = 0
- end
- end
- -- *********************************************************************************************************************************** --
- -- Start the program, checking for an existing MinerInstructions file, if it exists it will detect its location with gps and continue --
- -- otherwise it will ask the user for a new instruction set, check its gps location and begin --
- -- *********************************************************************************************************************************** --
- function startup()
- if fs.exists("MinerInstructions") == true then --existing instructions startup
- --Read the dig instructions file and update global variables
- local file = fs.open("MinerInstructions","r")
- local data = file.readAll()
- file.close()
- local readTable = {}
- readTable = textutils.unserialize(data)
- current.x = readTable["current_x"]; current.y = readTable["current_y"]; current.z = readTable["current_z"]; current.d = readTable["current_d"]
- home.x = readTable["home_x"]; home.y = readTable["home_y"]; home.z = readTable["home_z"]
- dig.x = readTable["dig_x"]; dig.y = readTable["dig_y"]; dig.z = readTable["dig_z"]; dig.d = readTable["dig_d"]
- boundary.xMax = readTable["boundary_xMax"]; boundary.xMin = readTable["boundary_xMin"]; boundary.yMax = readTable["boundary_yMax"]; boundary.yMin = readTable["boundary_yMin"]; boundary.zMax = readTable["boundary_zMax"]; boundary.zMin = readTable["boundary_zMin"]
- direction.forward = readTable["direction_forward"]; direction.right = readTable["direction_right"]; direction.back = readTable["direction_back"]; direction.left = readTable["direction_left"]
- flag.xReverse = readTable["flag_xReverse"]; flag.yReverse = readTable["flag_yReverse"]; flag.atHome = readTable["flag_atHome"]; flag.digging = readTable["flag_digging"]; flag.digComplete = readTable["flag_digComplete"]
- filterSlots = readTable["filterSlots"]
- --examine the atHome flag, if true a gps cycle might cause the turtle to destroy a chest so we move up a space and run the gps instead
- if flag.atHome == true then
- moveUp()
- GPS()
- moveDown(false)
- elseif flag.atHome == false then
- GPS()
- else
- errorStop("corrupted GPS flag at Startup")
- end
- --examine the digging flag, if its true return to the last known dig point and continue
- --if its false we can assume it was doing an unload cycle, so do that which will automatically
- --return to the dig point on completion
- if flag.digging == true then
- goto(dig.x, dig.y, dig.z, dig.d, false)
- elseif flag.digging == false then
- unloadCycle()
- else
- errorStop("corrupted dig flag at startup")
- end
- broadcast("Resuming existing dig")
- else -- new room startup
- term.clear() --ask the user to enter a room size
- term.setCursorPos(1, 1)
- print("***********************************")
- print("** Room Miner Program by Jharakn **")
- print("***********************************")
- print("")
- print("No previous instruction set found, starting new room:")
- print("Please enter your room width")
- local digWidth = (tonumber(read()) - 1) --need a data validation step here
- print("Thank you, please enter your room depth")
- local digDepth = (tonumber(read()) - 1) --need a data validation step here
- print("Thank you, finally please enter a room Height")
- local digHeight = (tonumber(read()) - 1) --need a data validation step here
- print("Thank you, Starting Program")
- sleep(3)
- term.clear()
- term.setCursorPos(1, 1)
- GPS(true) --grab the turtle location and update its direction table
- home.x = current.x; home.y = current.y; home.z = current.z --make the home locations
- dig.x = current.x; dig.y = current.y; dig.z = current.z; dig.d = current.d --make the dig locations here too
- if current.d == 1 then --update the boundaries and flags based on the turtles current direction
- boundary.xMax = current.x + digWidth
- boundary.xMin = current.x
- boundary.yMax = current.y + digDepth
- boundary.yMin = current.y
- boundary.zMax = current.z + digHeight
- boundary.zMin = current.z
- flag.xReverse = false
- flag.yReverse = false
- flag.atHome = false; flag.digging = true; flag.digComplete = false
- elseif current.d == 2 then
- boundary.xMax = current.x + digDepth
- boundary.xMin = current.x
- boundary.yMax = current.y
- boundary.yMin = current.y - digWidth
- boundary.zMax = current.z + digHeight
- boundary.zMin = current.z
- flag.xReverse = false
- flag.yReverse = true
- flag.atHome = false; flag.digging = true; flag.digComplete = false
- elseif current.d == 3 then
- boundary.xMax = current.x
- boundary.xMin = current.x - digWidth
- boundary.yMax = current.y
- boundary.yMin = current.y - digDepth
- boundary.zMax = current.z + digHeight
- boundary.zMin = current.z
- flag.xReverse = true
- flag.yReverse = true
- flag.atHome = false; flag.digging = true; flag.digComplete = false
- elseif current.d == 4 then
- boundary.xMax = current.x
- boundary.xMin = current.x - digDepth
- boundary.yMax = current.y + digWidth
- boundary.yMin = current.y
- boundary.zMax = current.z + digHeight
- boundary.zMin = current.z
- flag.xReverse = true
- flag.yReverse = false
- flag.atHome = false; flag.digging = true; flag.digComplete = false
- else
- errorStop("corrupted current.d value in startup")
- end
- moveUp()
- broadcast("Digging New Room")
- end
- end
- -- ************************************************************************ --
- -- will check the layers above and below the turtle, if they are within the --
- -- boundaries they will be dug out --
- -- ************************************************************************ --
- function digger()
- --detect below the turtle and dig
- local counter = 0
- while turtle.detectDown() == true do
- turtle.digDown()
- turtle.attackDown()
- counter = counter + 1
- if counter == 100 then
- errorStop("digger timed out on dig down")
- end
- end
- --detect above the turtle and dig only if the layer above is within the boundarys
- if current.z + 1 <= boundary.zMax then
- local counter = 0
- while turtle.detectUp() == true do
- turtle.digUp()
- turtle.attackUp()
- counter = counter + 1
- if counter == 100 then
- errorStop("digger timed out on dig up")
- end
- end
- end
- end
- -- *************************************************************************************** --
- -- Refuels the turtle if low on fuel from slot 16, returns false if slot 16 is running low --
- -- *************************************************************************************** --
- function fuelCheck()
- if turtle.getFuelLevel() < 10 then
- turtle.select(16)
- turtle.refuel(1)
- turtle.select(1)
- end
- if turtle.getItemCount(16) < 10 then
- broadcast("Fuel Low - returning home")
- return false
- else
- return true
- end
- end
- -- ********************************************************************************************** --
- -- checks slot 15 in the inventory, if it has an item the turtle needs unloading so returns false --
- -- ********************************************************************************************** --
- function invCheck()
- if turtle.getItemCount(15) == 0 then
- return true
- else
- broadcast("Inventory full - returning home")
- return false
- end
- end
- -- ************************************************************************************************* --
- -- Returns home to unload its inventory and top up its fuel supplies then return to the mining point --
- -- ************************************************************************************************* --
- function unloadCycle()
- --update the turtle objective just in case the chunk unloads whilst its traveling home
- --and commit these changes to memory this will also update the dig locations so its
- --got the freshest locations
- flag.digging = false
- pcall(writeVariables)
- --return home
- goto(home.x, home.y, home.z, direction.back, false)
- --update flag.atHome so if it loads again at this point it won't eat its output chest
- flag.atHome = true
- pcall(writeVariables)
- --output all of the items except fuel in slot 16
- for i = 1, 15 do
- turtle.select(i)
- if turtle.getItemCount(i) ~= 0 then
- if turtle.drop() == false then --if it can't drop in the chest drop on the floor instead
- broadcast("Warning - Output Chest Full")
- turtle.dropDown()
- end
- end
- end
- -- turn to face the fuel chest and top off slot 16
- turn(direction.left)
- turtle.select(15)
- if turtle.suck() == false then
- broadcast("Warning - Fuel chest empty")
- end
- turtle.transferTo(16, turtle.getItemSpace(16))
- turtle.drop()
- turtle.select(1)
- -- update the turtle objective and return to the dig point
- flag.digging = true
- flag.atHome = false
- pcall(writeVariables)
- goto(dig.x, dig.y, dig.z, dig.d, true)
- end
- -- **************************** --
- -- MAIN PROGRAM --
- -- **************************** --
- ----------------------
- -- Phase 1: Startup --
- ----------------------
- broadcast("Starting Program")
- if fuelCheck() == false then --preflight checks, needs at least 2 fuel for the gps function called in startup()
- errorStop("turtle has no fuel in slot 16")
- end
- startup()
- pcall(writeVariables)
- --debugger(true)
- ---------------------------
- -- Phase 2: Build Room --
- ---------------------------
- local yMoveSkip = true --yMoveSkip used to prevent a move in the y direction when starting a new z layer
- if flag.digComplete == false then digger() end --Dig the very first square the turtle is sat at
- while flag.digComplete == false do -- loop for z
- if flag.yReverse == false then
- while current.y ~= boundary.yMax do --loop for yReverse == false
- if yMoveSkip == false then
- turn(1)
- moveForward()
- dig.x = current.x; dig.y = current.y; dig.z = current.z; dig.d = current.d
- writeScheduler()
- digger()
- if fuelCheck() == false or invCheck() == false then unloadCycle() end
- end
- if flag.xReverse == false then
- turn(2)
- while current.x ~= boundary.xMax do --loop for xReverse == false (in yReverse == false)
- moveForward()
- dig.x = current.x; dig.y = current.y; dig.z = current.z; dig.d = current.d
- writeScheduler()
- digger()
- if fuelCheck() == false or invCheck() == false then unloadCycle() end
- end
- elseif flag.xReverse == true then
- turn(4)
- while current.x ~= boundary.xMin do --loop for xReverse == true (in yReverse == false)
- moveForward()
- dig.x = current.x; dig.y = current.y; dig.z = current.z; dig.d = current.d
- writeScheduler()
- digger()
- if fuelCheck() == false or invCheck() == false then unloadCycle() end
- end
- else
- errorStop("xReverse flag corrupted")
- end
- if flag.xReverse == true then -- flip the xReverse flag
- flag.xReverse = false
- else
- flag.xReverse = true
- end
- if yMoveSkip == true then yMoveSkip = false end -- remove the yMoveSkip
- end
- elseif flag.yReverse == true then
- while current.y ~= boundary.yMin do --loop for yReverse == true
- if yMoveSkip == false then
- turn(3)
- moveForward()
- dig.x = current.x; dig.y = current.y; dig.z = current.z; dig.d = current.d
- writeScheduler()
- digger()
- if fuelCheck() == false or invCheck() == false then unloadCycle() end
- end
- if flag.xReverse == false then
- turn(2)
- while current.x ~= boundary.xMax do --loop for xReverse == false (in yReverse == true)
- moveForward()
- dig.x = current.x; dig.y = current.y; dig.z = current.z; dig.d = current.d
- writeScheduler()
- digger()
- if fuelCheck() == false or invCheck() == false then unloadCycle() end
- end
- elseif flag.xReverse == true then
- turn(4)
- while current.x ~=boundary.xMin do --loop for xReverse == true (in yReverse == true)
- moveForward()
- dig.x = current.x; dig.y = current.y; dig.z = current.z; dig.d = current.d
- writeScheduler()
- digger()
- if fuelCheck() == false or invCheck() == false then unloadCycle() end
- end
- else
- errorStop("xReverse flag corrupted")
- end
- if flag.xReverse == true then -- flip the xReverse flag
- flag.xReverse = false
- else
- flag.xReverse = true
- end
- if yMoveSkip == true then yMoveSkip = false end -- remove the yMoveSkip
- end
- else
- errorStop("yReverse flag corrupted")
- end
- if flag.yReverse == false then -- flip the yReverse flag
- flag.yReverse = true
- else
- flag.yReverse = false
- end
- --If the loop compleates at boundary.zMax + 1 (because of digger) then were all done
- if current.z + 1 >= boundary.zMax then flag.digComplete = true end
- -- keep moving the turtle until unless it hits the zMax
- if current.z + 1 <= boundary.zMax then moveUp() end
- if current.z + 1 <= boundary.zMax then moveUp() end
- if current.z + 1 <= boundary.zMax then moveUp() end
- yMoveSkip = true
- dig.x = current.x; dig.y = current.y; dig.z = current.z; dig.d = current.d
- writeScheduler()
- digger()
- pcall(writeVariables)
- broadcast("Starting digging on level "..current.z)
- end
- -----------------------
- -- Phase 3: shutdown --
- -----------------------
- broadcast("Room finished - returning home")
- goto(home.x, home.y, home.z, direction.back, false)
- --final unload of inventory items
- for i = 1, 15 do
- turtle.select(i)
- if turtle.getItemCount(i) ~= 0 then
- if turtle.drop() == false then --if it can't drop in the chest drop on the floor instead
- broadcast("Warning - Output Chest Full")
- turtle.dropDown()
- end
- end
- end
- --remove the Miner Instructions file as its no longer needed
- fs.delete("MinerInstructions")
- broadcast("Turtle Finished")
Advertisement
Add Comment
Please, Sign In to add comment