Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- ********************************************************************************** --
- -- ** ** --
- -- ** Minecraft Artificial Intelligence Turtle ** --
- -- ** ---------------------------------------------------- ** --
- -- ** ** --
- -- ** pastebin run u5wHiXH1 ** --
- -- ** ** --
- -- ** Big thanks AustinKK for OreQuarry Turtle ** --
- -- ********************************************************************************** --
- -- ********************************************************************************** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** Variables ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ********************************************************************************** --
- -- Enumeration to store names for the 6 directions
- beaconName = { THRASH=1, STORAGE=2, SMELTERY=3 }
- -- Enumeration of filling tempelates
- fillState = { plain=1, pattern=2 }
- local beacon = {}
- local globalStates = { IDL=1, STAIRS=2 }
- local currGlobalState = globalStates.IDL
- local doNotEnsureSpaceInInventory = false -- Sometime, when we carry something
- local startState = 4
- -- ID table
- idSlots = {
- coal = 1,
- chest = 2,
- cobblestone = 3,
- dirt = 4,
- gravel = 5,
- ironOre = 6,
- goldOre = 7,
- a = 8,
- b = 9,
- c = 10,
- d = 11,
- e = 12,
- f = 13,
- g = 14,
- h = 15,
- i = 16,
- furnace = 17
- }
- idSlotsMinCount = { 32, 32, 64} -- On start
- craftPattern = {
- [idSlots.furnace] =
- { 3, 3, 3, 0,
- 3, 0, 3, 0,
- 3, 3, 3, 0,
- 0, 0, 0, 0}
- }
- worldSide = {
- n=1, -- North
- e=2, -- East
- s=3, -- South
- w=4, -- West
- u=5, -- Up
- d=6 -- Down
- }
- fillPattern = {
- ['Plain'] =
- { {{1}} },
- ['O_hole'] =
- { {{1, 1, 1},
- {1, 0, 1},
- {1, 1, 1}}},
- ['AntonsGrid'] =
- { {{3, 2, 2, 2},
- {2, 1, 1, 1},
- {2, 1, 1, 1},
- {2, 1, 1, 1}},
- {{2, 1, 1, 1},
- {1, 0, 0, 0},
- {1, 0, 0, 0},
- {1, 0, 0, 0}},
- {{2, 1, 1, 1},
- {1, 0, 0, 0},
- {1, 0, 0, 0},
- {1, 0, 0, 0}},
- {{2, 1, 1, 1},
- {1, 0, 0, 0},
- {1, 0, 0, 0},
- {1, 0, 0, 0}}},
- ['Diagonal'] =
- { {{1, 0, 0},
- {0, 0, 0},
- {0, 0, 0}},
- {{0, 0, 0},
- {0, 1, 0},
- {0, 0, 0}},
- {{0, 0, 0},
- {0, 0, 0},
- {0, 0, 1}}},
- ['QuarterColumn'] =
- { {{ 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 1},
- { 0, 0, 0, 1, 0}},
- {{ 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 2},
- { 0, 0, 0, 2, 0}},
- {{ 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 1}},
- {{ 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 3}}},
- ['AdvRoad_Diag'] =
- { {{ 3, 2, 2, 2, 2, 2, 1},
- { 3, 2, 2, 2, 2, 2, 3},
- { 3, 2, 2, 2, 2, 2, 3},
- { 1, 2, 2, 2, 2, 2, 3}},
- {{ 0, 0, 0, 0, 0, 0, 4},
- { 0, 0, 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 0, 0, 0},
- { 4, 0, 0, 0, 0, 0, 0}},
- {{ 0, 0, 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 0, 0, 0},
- { 0, 0, 0, 0, 0, 0, 0}}}
- }
- local SLOT_FUEL = 1
- local SLOT_CHEST = 2
- -- ********************************************************************************** --
- -- Note: If you are in a world with flat bedrock, change the value below from 5 to 2.
- -- You don't need to change this, but the turtle is slightly faster if you do.
- -- ********************************************************************************** --
- local bottomLayer = 5 -- The y co-ords of the layer immediately above bedrock
- -- Enumeration to store the the different types of message that can be written
- messageLevel = { DEBUG=0, INFO=1, WARNING=2, ERROR=3, FATAL=4 }
- -- Enumeration to store names for the 6 directions
- way = { FORWARD=0, RIGHT=1, BACK=2, LEFT=3, UP=4, DOWN=5 }
- -- Enumeration of mining states
- miningState = { START=0, LAYER=1, EMPTYCHESTDOWN=2, EMPTYINVENTORY=3 }
- local messageOutputLevel = messageLevel.INFO
- local messageOutputFileName
- local fuelLevelToRefuelAt = 5
- local refuelItemsToUseWhenRefuelling = 63
- local maximumGravelStackSupported = 25 -- The number of stacked gravel or sand blocks supported
- local noiseBlocksCount
- local returningToStart = false
- local lookForChests = false -- Determines if chests should be located as part of the quarrying
- local miningOffset -- The offset to the mining layer. This is set depending on whether chests are being looked for or not
- local lastEmptySlot = 1 -- The last inventory slot that was empty when the program started (is either 15 if not looking for chests or 14 if we are)
- local turtleId
- local isWirelessTurtle
- local currentlySelectedSlot = 0 -- The slot that the last noise block was found in
- local lastMoveNeededDig = true -- Determines whether the last move needed a dig first
- local haveBeenAtZeroZeroOnLayer -- Determines whether the turtle has been at (0, 0) in this mining layer
- local orientationAtZeroZero -- The turtle's orientation when it was at (0, 0)
- local levelToReturnTo -- The level that the turtle should return to in order to head back to the start to unload
- -- Variables used to support a resume
- local startupParamsFile = "OreQuarryParams.txt"
- local oreQuarryLocation = "OreQuarryLocation.txt"
- local returnToStartFile = "OreQuarryReturn.txt"
- local startupBackup = "startup_bak"
- local supportResume = true -- Determines whether the turtle is being run in the mode that supports resume
- local resuming = false -- Determines whether the turtle is currently in the process of resuming
- local resumeX
- local resumeY
- local resumeZ
- local resumeOrient
- local resumeMiningState
- -- Variables to store the current location and orientation of the turtle. x is right, left, y is up, down and
- -- z is forward, back with relation to the starting orientation. Y is the actual turtle level, x and z are
- -- in relation to the starting point (i.e. the starting point is (0, 0))
- local currX=0
- local currY=0
- local currZ=0
- local currOrient=way.FORWARD
- local currMiningState = miningState.START
- -- Command line parameters
- local startHeight -- Represents the height (y co-ord) that the turtle started at
- local quarryWidth -- Represents the length of the mines that the turtle will dig
- -- ********************************************************************************** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** Utilities ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ********************************************************************************** --
- --[[---------------
- LuaBit v0.4
- -------------------
- a bitwise operation lib for lua.
- http://luaforge.net/projects/bit/
- How to use:
- -------------------
- bit.bnot(n) -- bitwise not (~n)
- bit.band(m, n) -- bitwise and (m & n)
- bit.bor(m, n) -- bitwise or (m | n)
- bit.bxor(m, n) -- bitwise xor (m ^ n)
- bit.brshift(n, bits) -- right shift (n >> bits)
- bit.blshift(n, bits) -- left shift (n << bits)
- bit.blogic_rshift(n, bits) -- logic right shift(zero fill >>>)
- Please note that bit.brshift and bit.blshift only support number within
- 32 bits.
- 2 utility functions are provided too:
- bit.tobits(n) -- convert n into a bit table(which is a 1/0 sequence)
- -- high bits first
- bit.tonumb(bit_tbl) -- convert a bit table into a number
- -------------------
- Under the MIT license.
- copyright(c) 2006~2007 hanzhao (abrash_han@hotmail.com)
- --]]---------------
- do
- ------------------------
- -- bit lib implementions
- local function check_int(n)
- -- checking not float
- if(n - math.floor(n) > 0) then
- error("trying to use bitwise operation on non-integer!")
- end
- end
- local function to_bits(n)
- check_int(n)
- if(n < 0) then
- -- negative
- return to_bits(bit.bnot(math.abs(n)) + 1)
- end
- -- to bits table
- local tbl = {}
- local cnt = 1
- while (n > 0) do
- local last = math.mod(n,2)
- if(last == 1) then
- tbl[cnt] = 1
- else
- tbl[cnt] = 0
- end
- n = (n-last)/2
- cnt = cnt + 1
- end
- return tbl
- end
- local function tbl_to_number(tbl)
- local n = table.getn(tbl)
- local rslt = 0
- local power = 1
- for i = 1, n do
- rslt = rslt + tbl[i]*power
- power = power*2
- end
- return rslt
- end
- local function expand(tbl_m, tbl_n)
- local big = {}
- local small = {}
- if(table.getn(tbl_m) > table.getn(tbl_n)) then
- big = tbl_m
- small = tbl_n
- else
- big = tbl_n
- small = tbl_m
- end
- -- expand small
- for i = table.getn(small) + 1, table.getn(big) do
- small[i] = 0
- end
- end
- local function bit_or(m, n)
- local tbl_m = to_bits(m)
- local tbl_n = to_bits(n)
- expand(tbl_m, tbl_n)
- local tbl = {}
- local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
- for i = 1, rslt do
- if(tbl_m[i]== 0 and tbl_n[i] == 0) then
- tbl[i] = 0
- else
- tbl[i] = 1
- end
- end
- return tbl_to_number(tbl)
- end
- local function bit_and(m, n)
- local tbl_m = to_bits(m)
- local tbl_n = to_bits(n)
- expand(tbl_m, tbl_n)
- local tbl = {}
- local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
- for i = 1, rslt do
- if(tbl_m[i]== 0 or tbl_n[i] == 0) then
- tbl[i] = 0
- else
- tbl[i] = 1
- end
- end
- return tbl_to_number(tbl)
- end
- local function bit_not(n)
- local tbl = to_bits(n)
- local size = math.max(table.getn(tbl), 32)
- for i = 1, size do
- if(tbl[i] == 1) then
- tbl[i] = 0
- else
- tbl[i] = 1
- end
- end
- return tbl_to_number(tbl)
- end
- local function bit_xor(m, n)
- local tbl_m = to_bits(m)
- local tbl_n = to_bits(n)
- expand(tbl_m, tbl_n)
- local tbl = {}
- local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
- for i = 1, rslt do
- if(tbl_m[i] ~= tbl_n[i]) then
- tbl[i] = 1
- else
- tbl[i] = 0
- end
- end
- --table.foreach(tbl, print)
- return tbl_to_number(tbl)
- end
- local function bit_rshift(n, bits)
- check_int(n)
- local high_bit = 0
- if(n < 0) then
- -- negative
- n = bit_not(math.abs(n)) + 1
- high_bit = 2147483648 -- 0x80000000
- end
- for i=1, bits do
- n = n/2
- n = bit_or(math.floor(n), high_bit)
- end
- return math.floor(n)
- end
- -- logic rightshift assures zero filling shift
- local function bit_logic_rshift(n, bits)
- check_int(n)
- if(n < 0) then
- -- negative
- n = bit_not(math.abs(n)) + 1
- end
- for i=1, bits do
- n = n/2
- end
- return math.floor(n)
- end
- local function bit_lshift(n, bits)
- check_int(n)
- if(n < 0) then
- -- negative
- n = bit_not(math.abs(n)) + 1
- end
- for i=1, bits do
- n = n*2
- end
- return bit_and(n, 4294967295) -- 0xFFFFFFFF
- end
- local function bit_xor2(m, n)
- local rhs = bit_or(bit_not(m), bit_not(n))
- local lhs = bit_or(m, n)
- local rslt = bit_and(lhs, rhs)
- return rslt
- end
- --------------------
- -- bit lib interface
- bit = {
- -- bit operations
- bnot = bit_not,
- band = bit_and,
- bor = bit_or,
- bxor = bit_xor,
- brshift = bit_rshift,
- blshift = bit_lshift,
- bxor2 = bit_xor2,
- blogic_rshift = bit_logic_rshift,
- -- utility func
- tobits = to_bits,
- tonumb = tbl_to_number,
- }
- end
- -- ********************************************************************************** --
- function makeSet (list)
- local set = {}
- for _, l in ipairs(list) do set[l] = true end
- return set
- end
- -- ********************************************************************************** --
- -- Clear screen and set cursor to start
- function clear()
- term.clear()
- term.setCursorPos (1,1)
- end
- -- ********************************************************************************** --
- function readNumberParametr(requestText, from, to)
- while true do
- clear()
- print (requestText)
- local event, param1 = os.pullEvent ("char") -- limit os.pullEvent to the char event
- local result = tonumber(param1)
- if result >= from and result <= to then
- return result
- else
- print ('\nerror')
- sleep (0.5) -- I don't like a whole 2 seconds but 1 second is too short
- end
- end
- end
- function readTable(requestText, canBeSkipped, separator)
- local resultStr = ''
- while true do
- clear()
- print(requestText)
- resultStr = read()
- if resultStr == '' and canBeSkipped == true then
- return nil
- elseif resultStr ~= '' then
- local result = {}
- local i=1
- for v in string.gmatch(resultStr, separator) do
- result[i] = v
- i = i+1
- end
- return result
- end
- end
- end
- function readTableOfNumbers(requestText, canBeSkipped, numbersCount, separator)
- local resultStr = ''
- local result = {}
- for i=1,numbersCount do result[i]=0 end
- while true do
- local returnedResult = readTable(requestText, canBeSkipped, separator)
- if not returnedResult and canBeSkipped == true then
- return result
- elseif(returnedResult)then
- local isAllNumbers = true
- for i=1,numbersCount do
- if(type(tonumber(returnedResult[i])) == "number") then
- result[i] = tonumber(returnedResult[i])
- else
- print(returnedResult[i]..' is not number!')
- sleep(1)
- isAllNumbers=false
- end
- end
- if isAllNumbers == true then
- return result
- end
- end
- end
- end
- -- ********************************************************************************** --
- function table.shallow_copy(t)
- local t2 = {}
- for k,v in pairs(t) do
- t2[k] = v
- end
- return t2
- end
- -- ********************************************************************************** --
- -- Writes an output message
- -- ********************************************************************************** --
- function writeMessage(message, msgLevel)
- if (msgLevel >= messageOutputLevel) then
- print(message)
- -- If this turtle has a modem, then write the message to red net
- if (isWirelessTurtle == true) then
- if (turtleId == nil) then
- rednet.broadcast(message)
- else
- -- Broadcast the message (prefixed with the turtle's id)
- rednet.broadcast("[".. turtleId.."] "..message)
- end
- end
- if (messageOutputFileName ~= nil) then
- -- Open file, write message and close file (flush doesn't seem to work!)
- local outputFile
- if (fs.exists(messageOutputFileName) == true) then
- outputFile = io.open(messageOutputFileName, "a")
- else
- outputFile = io.open(messageOutputFileName, "w")
- end
- outputFile:write(message)
- outputFile:write("\n")
- outputFile:close()
- end
- end
- end
- function p(message)
- print('# '..message)
- end
- function turtleDig(direction)
- if (direction == nil) or (direction == way.FORWARD) then
- return turtle.dig()
- elseif(direction == way.DOWN) then
- return turtle.digDown()
- elseif(direction == way.UP) then
- return turtle.digUp()
- else
- writeMessage('Wrong direction for turtleDig()', messageLevel.ERROR)
- return false
- end
- end
- function getAnyKey()
- local key
- repeat
- key=io.read()
- until key==nil
- end
- -- ********************************************************************************** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** Turtle wrappers ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ********************************************************************************** --
- -- ********************************************************************************** --
- -- Ensures that the turtle has fuel
- -- ********************************************************************************** --
- function ensureFuel()
- -- Determine whether a refuel is required
- local fuelLevel = turtle.getFuelLevel()
- if (fuelLevel ~= "unlimited") then
- if (fuelLevel < fuelLevelToRefuelAt) then
- -- Need to refuel
- turtle.select(SLOT_FUEL)
- currentlySelectedSlot = SLOT_FUEL
- local coalItemsCount = turtle.getItemCount(SLOT_FUEL)
- -- Do we need to impact the emergency fuel to continue? (always
- -- keep one fuel item in slot SLOT_FUEL)
- if (coalItemsCount == 0) then
- writeMessage("Completely out of fuel!", messageLevel.FATAL)
- elseif (coalItemsCount == 1) then
- writeMessage("Out of Fuel!", messageLevel.ERROR)
- turtle.refuel()
- else
- -- Refuel all coal and leave 1
- turtle.refuel(coalItemsCount - 1)
- end
- end
- end
- end
- -- ********************************************************************************** --
- -- Checks that the turtle has inventory space by checking for spare slots and returning
- -- to the starting point to empty out if it doesn't.
- --
- -- Takes the position required to move to in order to empty the turtle's inventory
- -- should it be full as arguments
- -- ********************************************************************************** --
- function ensureInventorySpace()
- -- TODO: Enable ensuring
- do return false end
- -- If already returning to start, then don't need to do anything
- if ((returningToStart == false) and (doNotEnsureSpaceInInventory == false)) then
- -- If the last inventory slot is full, then need to return to the start and empty
- if (turtle.getItemCount(lastEmptySlot) > 0) then
- -- Return to the starting point and empty the inventory, then go back to mining
- returnToStartAndUnload(true)
- end
- end
- end
- -- ********************************************************************************** --
- -- Function to move to the starting point, call a function that is passed in
- -- and return to the same location (if required)
- -- ********************************************************************************** --
- function returnToStartAndUnload(returnBackToMiningPoint)
- writeMessage("returnToStartAndUnload called", messageLevel.DEBUG)
- returningToStart = true
- local storedX, storedY, storedZ, storedOrient
- local prevMiningState = currMiningState
- if (resuming == true) then
- -- Get the stored parameters from the necessary file
- local resumeFile = fs.open(returnToStartFile, "r")
- if (resumeFile ~= nil) then
- -- Restore the parameters from the file
- local beenAtZero = resumeFile.readLine()
- if (beenAtZero == "y") then
- haveBeenAtZeroZeroOnLayer = true
- else
- haveBeenAtZeroZeroOnLayer = false
- end
- local miningPointFlag = resumeFile.readLine()
- if (miningPointFlag == "y") then
- returnBackToMiningPoint = true
- else
- returnBackToMiningPoint = false
- end
- currX = readNumber(resumeFile)
- currY = readNumber(resumeFile)
- currZ = readNumber(resumeFile)
- currOrient = readNumber(resumeFile)
- levelToReturnTo = readNumber(resumeFile)
- prevMiningState = readNumber(resumeFile)
- orientationAtZeroZero = readNumber(resumeFile)
- resumeFile.close()
- else
- writeMessage("Failed to read return to start file", messageLevel.ERROR)
- end
- elseif (supportResume == true) then
- local outputFile = io.open(returnToStartFile, "w")
- if (haveBeenAtZeroZeroOnLayer == true) then
- outputFile:write("y\n")
- else
- outputFile:write("n\n")
- end
- if (returnBackToMiningPoint == true) then
- outputFile:write("y\n")
- else
- outputFile:write("n\n")
- end
- outputFile:write(currX)
- outputFile:write("\n")
- outputFile:write(currY)
- outputFile:write("\n")
- outputFile:write(currZ)
- outputFile:write("\n")
- outputFile:write(currOrient)
- outputFile:write("\n")
- outputFile:write(levelToReturnTo)
- outputFile:write("\n")
- outputFile:write(prevMiningState)
- outputFile:write("\n")
- outputFile:write(orientationAtZeroZero)
- outputFile:write("\n")
- outputFile:close()
- end
- storedX = currX
- storedY = currY
- storedZ = currZ
- storedOrient = currOrient
- -- Store the current location and orientation so that it can be returned to
- currMiningState = miningState.EMPTYINVENTORY
- writeMessage("last item count = "..turtle.getItemCount(lastEmptySlot), messageLevel.DEBUG)
- if ((turtle.getItemCount(lastEmptySlot) > 0) or (returnBackToMiningPoint == false)) then
- writeMessage("Heading back to surface", messageLevel.DEBUG)
- -- Move down to the correct layer to return via
- if (currY > levelToReturnTo) then
- while (currY > levelToReturnTo) do
- turtleDown()
- end
- elseif (currY < levelToReturnTo) then
- while (currY < levelToReturnTo) do
- turtleUp()
- end
- end
- if ((haveBeenAtZeroZeroOnLayer == false) or (orientationAtZeroZero == way.FORWARD)) then
- -- Move back to the correct X position first
- if (currX > 0) then
- turtleSetOrientation(way.LEFT)
- while (currX > 0) do
- turtleForward()
- end
- elseif (currX < 0) then
- -- This should never happen
- writeMessage("Current x is less than 0 in returnToStartAndUnload", messageLevel.ERROR)
- end
- -- Then move back to the correct Z position
- if (currZ > 0) then
- turtleSetOrientation(way.BACK)
- while (currZ > 0) do
- turtleForward()
- end
- elseif (currZ < 0) then
- -- This should never happen
- writeMessage("Current z is less than 0 in returnToStartAndUnload", messageLevel.ERROR)
- end
- else
- -- Move back to the correct Z position first
- if (currZ > 0) then
- turtleSetOrientation(way.BACK)
- while (currZ > 0) do
- turtleForward()
- end
- elseif (currZ < 0) then
- -- This should never happen
- writeMessage("Current z is less than 0 in returnToStartAndUnload", messageLevel.ERROR)
- end
- -- Then move back to the correct X position
- if (currX > 0) then
- turtleSetOrientation(way.LEFT)
- while (currX > 0) do
- turtleForward()
- end
- elseif (currX < 0) then
- -- This should never happen
- writeMessage("Current x is less than 0 in returnToStartAndUnload", messageLevel.ERROR)
- end
- end
- -- Return to the starting layer
- if (currY < startHeight) then
- while (currY < startHeight) do
- turtleUp()
- end
- elseif (currY > startHeight) then
- -- This should never happen
- writeMessage("Current height is greater than start height in returnToStartAndUnload", messageLevel.ERROR)
- end
- -- Empty the inventory
- local slotLoop = 1
- -- Face the chest
- turtleSetOrientation(way.BACK)
- -- Loop over each of the slots (except the 16th one which stores fuel)
- while (slotLoop < 16) do
- -- If this is one of the slots that contains a noise block, empty all blocks except
- -- one
- turtle.select(slotLoop) -- Don't bother updating selected slot variable as it will set later in this function
- if ((slotLoop <= noiseBlocksCount) or ((slotLoop == 15) and (lastEmptySlot == 14))) then
- writeMessage("Dropping (n-1) from slot "..slotLoop.." ["..turtle.getItemCount(slotLoop).."]", messageLevel.DEBUG)
- if (turtle.getItemCount(slotLoop) > 0) then
- turtle.drop(turtle.getItemCount(slotLoop) - 1)
- end
- else
- -- Not a noise block, drop all of the items in this slot
- writeMessage("Dropping (all) from slot "..slotLoop.." ["..turtle.getItemCount(slotLoop).."]", messageLevel.DEBUG)
- if (turtle.getItemCount(slotLoop) > 0) then
- turtle.drop()
- end
- end
- slotLoop = slotLoop + 1
- end
- -- While we are here, refill the fuel items if there is capacity
- if (turtle.getItemCount(16) < 64) then
- turtleSetOrientation(way.LEFT)
- turtle.select(16) -- Don't bother updating selected slot variable as it will set later in this function
- local currFuelItems = turtle.getItemCount(16)
- turtle.suck()
- while ((currFuelItems ~= turtle.getItemCount(16)) and (turtle.getItemCount(16) < 64)) do
- currFuelItems = turtle.getItemCount(16)
- turtle.suck()
- end
- slotLoop = noiseBlocksCount + 1
- -- Have now picked up all the items that we can. If we have also picked up some
- -- additional fuel in some of the other slots, then drop it again
- while (slotLoop <= lastEmptySlot) do
- -- Drop any items found in this slot
- if (turtle.getItemCount(slotLoop) > 0) then
- turtle.select(slotLoop) -- Don't bother updating selected slot variable as it will set later in this function
- turtle.drop()
- end
- slotLoop = slotLoop + 1
- end
- end
- -- Select the 1st slot because sometimes when leaving the 15th or 16th slots selected it can result
- -- in that slot being immediately filled (resulting in the turtle returning to base again too soon)
- turtle.select(1)
- currentlySelectedSlot = 1
- end
- -- If required, move back to the point that we were mining at before returning to the start
- if (returnBackToMiningPoint == true) then
- -- If resuming, refresh the starting point to be the top of the return shaft
- if (resuming == true) then
- currX = 0
- currY = startHeight
- currZ = 0
- currOrient = resumeOrient
- end
- -- Return back to the required layer
- while (currY > levelToReturnTo) do
- turtleDown()
- end
- if ((haveBeenAtZeroZeroOnLayer == false) or (orientationAtZeroZero == way.FORWARD)) then
- -- Move back to the correct Z position first
- writeMessage("Stored Z: "..storedZ..", currZ: "..currZ, messageLevel.DEBUG)
- if (storedZ > currZ) then
- writeMessage("Orienting forward", messageLevel.DEBUG)
- writeMessage("Moving in z way", messageLevel.DEBUG)
- turtleSetOrientation(way.FORWARD)
- while (storedZ > currZ) do
- turtleForward()
- end
- elseif (storedZ < currZ) then
- -- This should never happen
- writeMessage("Stored z is less than current z in returnToStartAndUnload", messageLevel.ERROR)
- end
- -- Then move back to the correct X position
- if (storedX > currX) then
- writeMessage("Stored X: "..storedX..", currX: "..currX, messageLevel.DEBUG)
- writeMessage("Orienting right", messageLevel.DEBUG)
- writeMessage("Moving in x way", messageLevel.DEBUG)
- turtleSetOrientation(way.RIGHT)
- while (storedX > currX) do
- turtleForward()
- end
- elseif (storedX < currX) then
- -- This should never happen
- writeMessage("Stored x is less than current x in returnToStartAndUnload", messageLevel.ERROR)
- end
- else
- -- Move back to the correct X position first
- if (storedX > currX) then
- writeMessage("Stored X: "..storedX..", currX: "..currX, messageLevel.DEBUG)
- writeMessage("Orienting right", messageLevel.DEBUG)
- writeMessage("Moving in x way", messageLevel.DEBUG)
- turtleSetOrientation(way.RIGHT)
- while (storedX > currX) do
- turtleForward()
- end
- elseif (storedX < currX) then
- -- This should never happen
- writeMessage("Stored x is less than current x in returnToStartAndUnload", messageLevel.ERROR)
- end
- -- Then move back to the correct Z position
- writeMessage("Stored Z: "..storedZ..", currZ: "..currZ, messageLevel.DEBUG)
- if (storedZ > currZ) then
- writeMessage("Orienting forward", messageLevel.DEBUG)
- writeMessage("Moving in z way", messageLevel.DEBUG)
- turtleSetOrientation(way.FORWARD)
- while (storedZ > currZ) do
- turtleForward()
- end
- elseif (storedZ < currZ) then
- -- This should never happen
- writeMessage("Stored z is less than current z in returnToStartAndUnload", messageLevel.ERROR)
- end
- end
- -- Move back to the correct layer
- if (storedY < currY) then
- while (storedY < currY) do
- turtleDown()
- end
- elseif (storedY > currY) then
- while (storedY > currY) do
- turtleUp()
- end
- end
- -- Finally, set the correct orientation
- turtleSetOrientation(storedOrient)
- writeMessage("Have returned to the mining point", messageLevel.DEBUG)
- end
- -- Store the current location and orientation so that it can be returned to
- currMiningState = prevMiningState
- returningToStart = false
- end
- -- ********************************************************************************** --
- -- Empties a chest's contents
- -- ********************************************************************************** --
- function emptyChest(suckFn)
- local prevInventoryCount = {}
- local inventoryLoop
- local chestEmptied = false
- -- Record the number of items in each of the inventory slots
- for inventoryLoop = 1, 16 do
- prevInventoryCount[inventoryLoop] = turtle.getItemCount(inventoryLoop)
- end
- while (chestEmptied == false) do
- -- Pick up the next item
- suckFn()
- -- Determine the number of items in each of the inventory slots now
- local newInventoryCount = {}
- for inventoryLoop = 1, 16 do
- newInventoryCount[inventoryLoop] = turtle.getItemCount(inventoryLoop)
- end
- -- Now, determine whether there have been any items taken from the chest
- local foundDifferentItemCount = false
- inventoryLoop = 1
- while ((foundDifferentItemCount == false) and (inventoryLoop <= 16)) do
- if (prevInventoryCount[inventoryLoop] ~= newInventoryCount[inventoryLoop]) then
- foundDifferentItemCount = true
- else
- inventoryLoop = inventoryLoop + 1
- end
- end
- -- If no items have been found with a different item count, then the chest has been emptied
- chestEmptied = not foundDifferentItemCount
- if (chestEmptied == false) then
- prevInventoryCount = newInventoryCount
- -- Check that there is sufficient inventory space as may have picked up a block
- ensureInventorySpace()
- end
- end
- writeMessage("Finished emptying chest", messageLevel.DEBUG)
- end
- -- ********************************************************************************** --
- -- Write the current location to a file
- -- ********************************************************************************** --
- function saveLocation()
- -- Write the x, y, z and orientation to the file
- if ((supportResume == true) and (resuming == false)) then
- local outputFile = io.open(oreQuarryLocation, "w")
- outputFile:write(currMiningState)
- outputFile:write("\n")
- outputFile:write(currX)
- outputFile:write("\n")
- outputFile:write(currY)
- outputFile:write("\n")
- outputFile:write(currZ)
- outputFile:write("\n")
- outputFile:write(currOrient)
- outputFile:write("\n")
- outputFile:close()
- end
- end
- -- ********************************************************************************** --
- -- If the turtle is resuming and the current co-ordinates, orientation and
- -- mining state have been matched, then no longer resuming
- -- ********************************************************************************** --
- function updateResumingFlag()
- if (resuming == true) then
- if ((resumeMiningState == currMiningState) and (resumeX == currX) and (resumeY == currY) and (resumeZ == currZ) and (resumeOrient == currOrient)) then
- resuming = false
- end
- end
- end
- -- ********************************************************************************** --
- -- Generic function to move the Turtle (pushing through any gravel or other
- -- things such as mobs that might get in the way).
- --
- -- The only thing that should stop the turtle moving is bedrock. Where this is
- -- found, the function will return after 15 seconds returning false
- -- ********************************************************************************** --
- function moveTurtle(moveFn, detectFn, digFn, attackFn, compareFn, suckFn, maxDigCount, newX, newY, newZ)
- local moveSuccess = false
- -- If we are resuming, then don't do anything in this function other than updating the
- -- co-ordinates as if the turtle had moved
- if (resuming == true) then
- -- Set the move success to true (but don't move) - unless this is below bedrock level
- -- in which case return false
- if (currY <= 0) then
- moveSuccess = false
- else
- moveSuccess = true
- end
- -- Update the co-ordinates to reflect the movement
- currX = newX
- currY = newY
- currZ = newZ
- else
- local prevX, prevY, prevZ
- prevX = currX
- prevY = currY
- prevZ = currZ
- -- TODO: Ensure fuel in another place
- --ensureFuel()
- -- Flag to determine whether digging has been tried yet. If it has
- -- then pause briefly before digging again to allow sand or gravel to
- -- drop
- local digCount = 0
- if (lastMoveNeededDig == false) then
- -- Didn't need to dig last time the turtle moved, so try moving first
- currX = newX
- currY = newY
- currZ = newZ
- saveLocation()
- moveSuccess = moveFn()
- -- If move failed, update the co-ords back to the previous co-ords
- if (moveSuccess == false) then
- currX = prevX
- currY = prevY
- currZ = prevZ
- saveLocation()
- end
- -- Don't need to set the last move needed dig. It is already false, if
- -- move success is now true, then it won't be changed
- else
- -- If we are looking for chests, then check that this isn't a chest before trying to dig it
- if (lookForChests == true) then
- if (isNoiseBlock(compareFn) == false) then
- if (detectFn() == true) then
- -- Determine if it is a chest before digging it
- if (isChestBlock(compareFn) == true) then
- -- Have found a chest, empty it before continuing
- emptyChest (suckFn)
- end
- end
- end
- end
- -- Try to dig (without doing a detect as it is quicker)
- local digSuccess = digFn()
- if (digSuccess == true) then
- digCount = 1
- end
- currX = newX
- currY = newY
- currZ = newZ
- saveLocation()
- moveSuccess = moveFn()
- if (moveSuccess == true) then
- lastMoveNeededDig = digSuccess
- else
- currX = prevX
- currY = prevY
- currZ = prevZ
- saveLocation()
- end
- end
- -- Loop until we've successfully moved
- if (moveSuccess == false) then
- while ((moveSuccess == false) and (digCount < maxDigCount)) do
- -- If there is a block in front, dig it
- if (detectFn() == true) then
- -- If we've already tried digging, then pause before digging again to let
- -- any sand or gravel drop, otherwise check for a chest before digging
- if(digCount == 0) then
- -- Am about to dig a block - check that it is not a chest if necessary
- -- If we are looking for chests, then check that this isn't a chest before moving
- if (lookForChests == true) then
- if (isNoiseBlock(compareFn) == false) then
- if (detectFn() == true) then
- -- Determine if it is a chest before digging it
- if (isChestBlock(compareFn) == true) then
- -- Have found a chest, empty it before continuing
- emptyChest (suckFn)
- end
- end
- end
- end
- else
- sleep(0.1)
- end
- digFn()
- digCount = digCount + 1
- else
- -- Am being stopped from moving by a mob, attack it
- attackFn()
- end
- currX = newX
- currY = newY
- currZ = newZ
- saveLocation()
- -- Try the move again
- moveSuccess = moveFn()
- if (moveSuccess == false) then
- currX = prevX
- currY = prevY
- currZ = prevZ
- saveLocation()
- end
- end
- if (digCount == 0) then
- lastMoveNeededDig = false
- else
- lastMoveNeededDig = true
- end
- end
- end
- -- If we are resuming and the current co-ordinates and orientation are the resume point
- -- then are no longer resuming
- if (moveSuccess == true) then
- updateResumingFlag()
- end
- -- Return the move success
- return moveSuccess
- end
- -- ********************************************************************************** --
- -- Move the turtle forward one block (updating the turtle's position)
- -- ********************************************************************************** --
- function turtleForward()
- -- Determine the new co-ordinate that the turtle will be moving to
- local newX, newZ
- -- Update the current co-ordinates
- if (currOrient == way.FORWARD) then
- newZ = currZ + 1
- newX = currX
- elseif (currOrient == way.LEFT) then
- newX = currX - 1
- newZ = currZ
- elseif (currOrient == way.BACK) then
- newZ = currZ - 1
- newX = currX
- elseif (currOrient == way.RIGHT) then
- newX = currX + 1
- newZ = currZ
- else
- writeMessage ("Invalid currOrient in turtleForward function", messageLevel.ERROR)
- end
- local returnVal = moveTurtle(turtle.forward, turtle.detect, turtle.dig, turtle.attack, turtle.compare, turtle.suck, maximumGravelStackSupported, newX, currY, newZ)
- if (returnVal == true) then
- -- Check that there is sufficient inventory space as may have picked up a block
- ensureInventorySpace()
- end
- return returnVal
- end
- -- ********************************************************************************** --
- -- Move the turtle up one block (updating the turtle's position)
- -- ********************************************************************************** --
- function turtleUp()
- local returnVal = moveTurtle(turtle.up, turtle.detectUp, turtle.digUp, turtle.attackUp, turtle.compareUp, turtle.suckUp, maximumGravelStackSupported, currX, currY + 1, currZ)
- if (returnVal == true) then
- -- Check that there is sufficient inventory space as may have picked up a block
- ensureInventorySpace()
- end
- return returnVal
- end
- -- ********************************************************************************** --
- -- Move the turtle down one block (updating the turtle's position)
- -- ********************************************************************************** --
- function turtleDown()
- local returnVal = moveTurtle(turtle.down, turtle.detectDown, turtle.digDown, turtle.attackDown, turtle.compareDown, turtle.suckDown, 1, currX, currY - 1, currZ)
- if (returnVal == true) then
- -- Check that there is sufficient inventory space as may have picked up a block
- ensureInventorySpace()
- end
- return returnVal
- end
- -- ********************************************************************************** --
- -- Move the turtle back one block (updating the turtle's position)
- -- ********************************************************************************** --
- function turtleBack()
- -- Assume that the turtle will move, and switch the co-ords back if it doesn't
- -- (do this so that we can write the co-ords to a file before moving)
- local newX, newZ
- local prevX, prevZ
- prevX = currX
- prevZ = currZ
- -- Update the current co-ordinates
- if (currOrient == way.FORWARD) then
- newZ = currZ - 1
- newX = currX
- elseif (currOrient == way.LEFT) then
- newX = currX + 1
- newZ = currZ
- elseif (currOrient == way.BACK) then
- newZ = currZ + 1
- newX = currX
- elseif (currOrient == way.RIGHT) then
- newX = currX - 1
- newZ = currZ
- else
- writeMessage ("Invalid currOrient in turtleBack function", messageLevel.ERROR)
- end
- -- First try to move back using the standard function
- currX = newX
- currZ = newZ
- saveLocation()
- local returnVal = turtle.back()
- if (returnVal == false) then
- -- Didn't move. Reset the co-ordinates to the previous value
- currX = prevX
- currZ = prevZ
- -- Reset the location back to the previous location (because the turn takes 0.8 of a second
- -- so could be stopped before getting to the forward function)
- saveLocation()
- turtle.turnRight()
- turtle.turnRight()
- -- Try to move by using the forward function (note, the orientation will be set as
- -- the same way as this function started because if the function stops, that is the
- -- way that we want to consider the turtle to be pointing)
- returnVal = moveTurtle(turtle.forward, turtle.detect, turtle.dig, turtle.attack, turtle.compare, turtle.suck, maximumGravelStackSupported, newX, currY, newZ)
- turtle.turnRight()
- turtle.turnRight()
- end
- if (returnVal == true) then
- -- Check that there is sufficient inventory space as may have picked up a block
- ensureInventorySpace()
- end
- return returnVal
- end
- -- ********************************************************************************** --
- -- Turns the turtle (updating the current orientation at the same time)
- -- ********************************************************************************** --
- function turtleTurn(turnDir)
- if (turnDir == way.LEFT) then
- if (currOrient == way.FORWARD) then
- currOrient = way.LEFT
- elseif (currOrient == way.LEFT) then
- currOrient = way.BACK
- elseif (currOrient == way.BACK) then
- currOrient = way.RIGHT
- elseif (currOrient == way.RIGHT) then
- currOrient = way.FORWARD
- else
- writeMessage ("Invalid currOrient in turtleTurn function", messageLevel.ERROR)
- end
- -- If we are resuming, just check to see whether have reached the resume point, otherwise
- -- turn
- if (resuming == true) then
- updateResumingFlag()
- else
- -- Write the new orientation and turn
- saveLocation()
- turtle.turnLeft()
- end
- elseif (turnDir == way.RIGHT) then
- if (currOrient == way.FORWARD) then
- currOrient = way.RIGHT
- elseif (currOrient == way.LEFT) then
- currOrient = way.FORWARD
- elseif (currOrient == way.BACK) then
- currOrient = way.LEFT
- elseif (currOrient == way.RIGHT) then
- currOrient = way.BACK
- else
- writeMessage ("Invalid currOrient in turtleTurn function", messageLevel.ERROR)
- end
- -- If we are resuming, just check to see whether have reached the resume point, otherwise
- -- turn
- if (resuming == true) then
- updateResumingFlag()
- writeMessage("["..currMiningState..", "..currX..", "..currY..", "..currZ..", "..currOrient.."]", messageLevel.DEBUG)
- else
- -- Write the new orientation and turn
- saveLocation()
- turtle.turnRight()
- end
- else
- writeMessage ("Invalid turnDir in turtleTurn function", messageLevel.ERROR)
- end
- end
- -- ********************************************************************************** --
- -- Sets the turtle to a specific orientation, irrespective of its current orientation
- -- ********************************************************************************** --
- function turtleSetOrientation(newOrient)
- if (currOrient ~= newOrient) then
- if (currOrient == way.FORWARD) then
- if (newOrient == way.RIGHT) then
- currOrient = newOrient
- -- If resuming, check whether the resume point has been reached, otherwise turn
- if (resuming == true) then
- updateResumingFlag()
- else
- -- Write the new orientation and turn
- saveLocation()
- turtle.turnRight()
- end
- elseif (newOrient == way.BACK) then
- currOrient = newOrient
- -- If resuming, check whether the resume point has been reached, otherwise turn
- if (resuming == true) then
- updateResumingFlag()
- else
- -- Write the new orientation and turn
- saveLocation()
- turtle.turnRight()
- turtle.turnRight()
- end
- elseif (newOrient == way.LEFT) then
- currOrient = newOrient
- -- If resuming, check whether the resume point has been reached, otherwise turn
- if (resuming == true) then
- updateResumingFlag()
- else
- -- Write the new orientation and turn
- saveLocation()
- turtle.turnLeft()
- end
- else
- writeMessage ("Invalid newOrient in turtleSetOrientation function", messageLevel.ERROR)
- end
- elseif (currOrient == way.RIGHT) then
- if (newOrient == way.BACK) then
- currOrient = newOrient
- -- If resuming, check whether the resume point has been reached, otherwise turn
- if (resuming == true) then
- updateResumingFlag()
- else
- -- Write the new orientation and turn
- saveLocation()
- turtle.turnRight()
- end
- elseif (newOrient == way.LEFT) then
- currOrient = newOrient
- -- If resuming, check whether the resume point has been reached, otherwise turn
- if (resuming == true) then
- updateResumingFlag()
- else
- -- Write the new orientation and turn
- saveLocation()
- turtle.turnRight()
- turtle.turnRight()
- end
- elseif (newOrient == way.FORWARD) then
- currOrient = newOrient
- -- If resuming, check whether the resume point has been reached, otherwise turn
- if (resuming == true) then
- updateResumingFlag()
- else
- -- Write the new orientation and turn
- saveLocation()
- turtle.turnLeft()
- end
- else
- writeMessage ("Invalid newOrient in turtleSetOrientation function", messageLevel.ERROR)
- end
- elseif (currOrient == way.BACK) then
- if (newOrient == way.LEFT) then
- currOrient = newOrient
- -- If resuming, check whether the resume point has been reached, otherwise turn
- if (resuming == true) then
- updateResumingFlag()
- else
- -- Write the new orientation and turn
- saveLocation()
- turtle.turnRight()
- end
- elseif (newOrient == way.FORWARD) then
- currOrient = newOrient
- -- If resuming, check whether the resume point has been reached, otherwise turn
- if (resuming == true) then
- updateResumingFlag()
- else
- -- Write the new orientation and turn
- saveLocation()
- turtle.turnRight()
- turtle.turnRight()
- end
- elseif (newOrient == way.RIGHT) then
- currOrient = newOrient
- -- If resuming, check whether the resume point has been reached, otherwise turn
- if (resuming == true) then
- updateResumingFlag()
- else
- -- Write the new orientation and turn
- saveLocation()
- turtle.turnLeft()
- end
- else
- writeMessage ("Invalid newOrient in turtleSetOrientation function", messageLevel.ERROR)
- end
- elseif (currOrient == way.LEFT) then
- if (newOrient == way.FORWARD) then
- currOrient = newOrient
- -- If resuming, check whether the resume point has been reached, otherwise turn
- if (resuming == true) then
- updateResumingFlag()
- else
- -- Write the new orientation and turn
- saveLocation()
- turtle.turnRight()
- end
- elseif (newOrient == way.RIGHT) then
- currOrient = newOrient
- -- If resuming, check whether the resume point has been reached, otherwise turn
- if (resuming == true) then
- updateResumingFlag()
- else
- -- Write the new orientation and turn
- saveLocation()
- turtle.turnRight()
- turtle.turnRight()
- end
- elseif (newOrient == way.BACK) then
- currOrient = newOrient
- -- If resuming, check whether the resume point has been reached, otherwise turn
- if (resuming == true) then
- updateResumingFlag()
- else
- -- Write the new orientation and turn
- saveLocation()
- turtle.turnLeft()
- end
- else
- writeMessage ("Invalid newOrient in turtleSetOrientation function", messageLevel.ERROR)
- end
- else
- writeMessage ("Invalid currOrient in turtleTurn function", messageLevel.ERROR)
- end
- end
- end
- -- ********************************************************************************** --
- -- Determines if a particular block is considered a noise block or not. A noise
- -- block is one that is a standard block in the game (stone, dirt, gravel etc.) and
- -- is one to ignore as not being an ore. Function works by comparing the block
- -- in question against a set of blocks in the turtle's inventory which are known not to
- -- be noise blocks. Param is the function to use to compare the block for a noise block
- -- ********************************************************************************** --
- function isNoiseBlock(compareFn)
- -- Consider air to be a noise block
- local returnVal = false
- if (resuming == true) then
- returnVal = true
- else
- local seamLoop = 1
- local prevSelectedSlot
- -- If the currently selected slot is a noise block, then compare against this first
- -- so that the slot doesn't need to be selected again (there is a 0.05s cost to do
- -- this even if it is the currently selected slot)
- if (currentlySelectedSlot <= noiseBlocksCount) then
- returnVal = compareFn()
- end
- if (returnVal == false) then
- prevSelectedSlot = currentlySelectedSlot
- while((returnVal == false) and (seamLoop <= noiseBlocksCount)) do
- if (seamLoop ~= prevSelectedSlot) then
- turtle.select(seamLoop)
- currentlySelectedSlot = seamLoop
- returnVal = compareFn()
- end
- seamLoop = seamLoop + 1
- end
- end
- end
- -- Return the calculated value
- return returnVal
- end
- -- ********************************************************************************** --
- -- Determines if a particular block is a chest. Returns false if it is not a chest
- -- or chests are not being detected
- -- ********************************************************************************** --
- function isChestBlock(compareFn)
- -- Check the block in the appropriate way to see whether it is a chest. Only
- -- do this if we are looking for chests
- local returnVal = false
- if (lookForChests == true) then
- turtle.select(15)
- currentlySelectedSlot = 15
- returnVal = compareFn()
- end
- -- Return the calculated value
- return returnVal
- end
- -- ********************************************************************************** --
- -- Function to calculate the number of non seam blocks in the turtle's inventory. This
- -- is all of the blocks at the start of the inventory (before the first empty slot is
- -- found
- -- ********************************************************************************** --
- function determineNoiseBlocksCountCount()
- -- Determine the location of the first empty inventory slot. All items before this represent
- -- noise items.
- local foundFirstBlankInventorySlot = false
- noiseBlocksCount = 1
- while ((noiseBlocksCount < 16) and (foundFirstBlankInventorySlot == false)) do
- if (turtle.getItemCount(noiseBlocksCount) > 0) then
- noiseBlocksCount = noiseBlocksCount + 1
- else
- foundFirstBlankInventorySlot = true
- end
- end
- noiseBlocksCount = noiseBlocksCount - 1
- -- Determine whether a chest was provided, and hence whether we should support
- -- looking for chests
- if (turtle.getItemCount(15) > 0) then
- lookForChests = true
- lastEmptySlot = 14
- miningOffset = 0
- writeMessage("Looking for chests...", messageLevel.DEBUG)
- else
- lastEmptySlot = 15
- miningOffset = 1
- writeMessage("Ignoring chests...", messageLevel.DEBUG)
- end
- end
- -- ********************************************************************************** --
- -- Creates a quarry mining out only ores and leaving behind any noise blocks
- -- ********************************************************************************** --
- function createQuarry()
- -- Determine the top mining layer layer. The turtle mines in layers of 3, and the bottom layer
- -- is the layer directly above bedrock.
- --
- -- The actual layer that the turtle operates in is the middle of these three layers,
- -- so determine the top layer
- local topMiningLayer = startHeight + ((bottomLayer - startHeight - 2) % 3) - 1 + miningOffset
- -- If the top layer is up, then ignore it and move to the next layer
- if (topMiningLayer > currY) then
- topMiningLayer = topMiningLayer - 3
- end
- local startedLayerToRight = true -- Only used where the quarry is of an odd width
- -- Loop over each mining row
- local miningLevel
- for miningLevel = (bottomLayer + miningOffset), topMiningLayer, 3 do
- writeMessage("Mining Layer: "..miningLevel, messageLevel.INFO)
- haveBeenAtZeroZeroOnLayer = false
- -- While the initial shaft is being dug out, set the level to return to in order to unload
- -- to the just take the turtle straight back up
- if (miningLevel == (bottomLayer + miningOffset)) then
- levelToReturnTo = startHeight
- end
- -- Move to the correct level to start mining
- if (currY > miningLevel) then
- while (currY > miningLevel) do
- turtleDown()
- end
- elseif (currY < miningLevel) then
- while (currY < miningLevel) do
- turtleUp()
- end
- end
- -- Am now mining the levels (update the mining state to reflect that fact)
- currMiningState = miningState.LAYER
- -- Set the layer to return via when returning to the surface as the one below the currently
- -- mined one
- if (miningLevel == (bottomLayer + miningOffset)) then
- levelToReturnTo = (bottomLayer + miningOffset)
- else
- levelToReturnTo = miningLevel - 3
- end
- -- Move turtle into the correct orientation to start mining (if this is the
- -- first row to be mined, then don't need to turn, otherwise turn towards the next
- -- mining section)
- writeMessage("Mining Level: "..miningLevel..", Bottom Layer: "..bottomLayer..", Mining Offset: "..miningOffset, messageLevel.DEBUG)
- if (miningLevel > (bottomLayer + miningOffset)) then
- -- Turn towards the next mining layer
- if (quarryWidth % 2 == 0) then
- -- An even width quarry, always turn right
- turtleTurn(way.RIGHT)
- else
- -- Turn the opposite way to that which we turned before
- if (startedLayerToRight == true) then
- turtleTurn(way.LEFT)
- startedLayerToRight = false
- else
- turtleTurn(way.RIGHT)
- startedLayerToRight = true
- end
- end
- end
- local mineRows
- local onNearSideOfQuarry = true
- local diggingAway = true
- for mineRows = 1, quarryWidth do
- -- If this is not the first row, then get into position to mine the next row
- if ((mineRows == 1) and (lookForChests == false)) then
- -- Not looking for chests, check the block below for being an ore. Only do this
- -- if we're not looking for chests since the program doesn't support chests in
- -- bedrock
- if (isNoiseBlock(turtle.compareDown) == false) then
- turtle.digDown()
- ensureInventorySpace()
- end
- elseif (mineRows > 1) then
- -- Move into position for mining the next row
- if (onNearSideOfQuarry == diggingAway) then
- if (startedLayerToRight == true) then
- turtleTurn(way.LEFT)
- else
- turtleTurn(way.RIGHT)
- end
- else
- if (startedLayerToRight == true) then
- turtleTurn(way.RIGHT)
- else
- turtleTurn(way.LEFT)
- end
- end
- turtleForward()
- -- Before making the final turn, check the block below. Do this
- -- now because if it is a chest, then we want to back up and
- -- approach it from the side (so that we don't lose items if we
- -- have to return to the start through it).
- --
- -- This is the point at which it is safe to back up without moving
- -- out of the quarry area (unless at bedrock in which case don't bother
- -- as we'll be digging down anyway)
- if (miningLevel ~= bottomLayer) then
- if (isNoiseBlock(turtle.compareDown) == false) then
- -- If we are not looking for chests, then just dig it (it takes
- -- less time to try to dig and fail as it does to do detect and
- -- only dig if there is a block there)
- if (lookForChests == false) then
- turtle.digDown()
- ensureInventorySpace()
- elseif (turtle.detectDown() == true) then
- if (isChestBlock(turtle.compareDown) == true) then
- -- There is a chest block below. Move back and approach
- -- from the side to ensure that we don't need to return to
- -- start through the chest itself (potentially losing items)
- turtleBack()
- turtleDown()
- currMiningState = miningState.EMPTYCHESTDOWN
- emptyChest(turtle.suck)
- currMiningState = miningState.LAYER
- turtleUp()
- turtleForward()
- turtle.digDown()
- ensureInventorySpace()
- else
- turtle.digDown()
- ensureInventorySpace()
- end
- end
- end
- end
- -- Move into final position for mining the next row
- if (onNearSideOfQuarry == diggingAway) then
- if (startedLayerToRight == true) then
- turtleTurn(way.LEFT)
- else
- turtleTurn(way.RIGHT)
- end
- else
- if (startedLayerToRight == true) then
- turtleTurn(way.RIGHT)
- else
- turtleTurn(way.LEFT)
- end
- end
- end
- -- Dig to the other side of the quarry
- local blocksMined
- for blocksMined = 0, (quarryWidth - 1) do
- if (blocksMined > 0) then
- -- Only move forward if this is not the first space
- turtleForward()
- end
- -- If the current block is (0,0), then record the fact that the
- -- turtle has been through this block and what it's orientation was and update the layer
- -- that it should return via to get back to the surface (it no longer needs to go down
- -- a level to prevent losing ores).
- if ((currX == 0) and (currZ == 0)) then
- -- Am at (0, 0). Remember this, and what way I was facing so that the quickest route
- -- to the surface can be taken
- levelToReturnTo = miningLevel
- haveBeenAtZeroZeroOnLayer = true
- orientationAtZeroZero = currOrient
- end
- -- If currently at bedrock, just move down until the turtle can't go any
- -- further. This allows the blocks within the bedrock to be mined
- if (miningLevel == bottomLayer) then
- -- Temporarily turn off looking for chests to increase bedrock mining speed (this
- -- means that the program doesn't support chests below level 5 - but I think
- -- they they don't exist anyway)
- local lookForChestsPrev = lookForChests
- lookForChests = false
- -- Manually set the flag to determine whether the turtle should try to move first or
- -- dig first. At bedrock, is very rarely any space
- -- Just above bedrock layer, dig down until can't dig any lower, and then
- -- come back up. This replicates how the quarry functions
- lastMoveNeededDig = true
- local moveDownSuccess = turtleDown()
- while (moveDownSuccess == true) do
- moveDownSuccess = turtleDown()
- end
- -- Know that we are moving back up through air, therefore set the flag to force the
- -- turtle to try moving first
- lastMoveNeededDig = false
- -- Have now hit bedrock, move back to the mining layer
- while (currY < bottomLayer) do
- turtleUp()
- end
- -- Now back at the level above bedrock, again reset the flag to tell the turtle to
- -- try digging again (because it is rare to find air at bedrock level)
- lastMoveNeededDig = false
- -- Reset the look for chests value
- lookForChests = lookForChestsPrev
- elseif ((blocksMined > 0) and ((currX ~= 0) or (currZ ~= 0))) then
- -- This isn't the first block of the row, nor are we at (0, 0) so we need to check the
- -- block below
- -- Check the block down for being a noise block (don't need to check the first
- -- block as it has already been checked in the outer loop)
- if (isNoiseBlock(turtle.compareDown) == false) then
- -- If we are not looking for chests, then just dig it (it takes
- -- less time to try to dig and fail as it does to do detect and
- -- only dig if there is a block there)
- if (lookForChests == false) then
- turtle.digDown()
- ensureInventorySpace()
- elseif (turtle.detectDown() == true) then
- if (isChestBlock(turtle.compareDown) == true) then
- -- There is a chest block below. Move back and approach
- -- from the side to ensure that we don't need to return to
- -- start through the chest itself (potentially losing items)
- turtleBack()
- currMiningState = miningState.EMPTYCHESTDOWN
- turtleDown()
- emptyChest(turtle.suck)
- currMiningState = miningState.LAYER
- turtleUp()
- turtleForward()
- turtle.digDown()
- ensureInventorySpace()
- else
- turtle.digDown()
- ensureInventorySpace()
- end
- end
- end
- end
- -- Check the block above for ores (if we're not a (0, 0) in which case
- -- we know it's air)
- if ((currX ~= 0) or (currZ ~= 0)) then
- if (isNoiseBlock(turtle.compareUp) == false) then
- -- If we are not looking for chests, then just dig it (it takes
- -- less time to try to dig and fail as it does to do detect and
- -- only dig if there is a block there)
- if (lookForChests == false) then
- turtle.digUp()
- ensureInventorySpace()
- elseif (turtle.detectUp() == true) then
- -- Determine if it is a chest before digging it
- if (isChestBlock(turtle.compareUp) == true) then
- -- There is a chest block above. Empty it before digging it
- emptyChest(turtle.suckUp)
- turtle.digUp()
- ensureInventorySpace()
- else
- turtle.digUp()
- ensureInventorySpace()
- end
- end
- end
- end
- end
- -- Am now at the other side of the quarry
- onNearSideOfQuarry = not onNearSideOfQuarry
- end
- -- If we were digging away from the starting point, will be digging
- -- back towards it on the next layer
- diggingAway = not diggingAway
- end
- -- Return to the start
- returnToStartAndUnload(false)
- -- Face forward
- turtleSetOrientation(way.FORWARD)
- end
- -- ********************************************************************************** --
- -- Reads the next number from a given file
- -- ********************************************************************************** --
- function readNumber(inputFile)
- local returnVal
- local nextLine = inputFile.readLine()
- if (nextLine ~= nil) then
- returnVal = tonumber(nextLine)
- end
- return returnVal
- end
- -- ********************************************************************************** --
- -- Startup function to support resuming mining turtle
- -- ********************************************************************************** --
- function isResume()
- local returnVal = false
- -- Try to open the resume file
- local resumeFile = fs.open(startupParamsFile, "r")
- if (resumeFile == nil) then
- -- No resume file (presume that we are not supporting it)
- supportResume = false
- else
- writeMessage("Found startup params file", messageLevel.DEBUG)
- -- Read in the startup params
- quarryWidth = readNumber(resumeFile)
- startHeight = readNumber(resumeFile)
- noiseBlocksCount = readNumber(resumeFile)
- lastEmptySlot = readNumber(resumeFile)
- resumeFile.close()
- -- If the parameters were successfully read, then set the resuming flag to true
- if ((quarryWidth ~= nil) and (startHeight ~= nil) and (noiseBlocksCount ~= nil) and (lastEmptySlot ~= nil)) then
- resuming = true
- writeMessage("Read params", messageLevel.DEBUG)
- -- Determine the look for chest and mining offset
- if (lastEmptySlot == 14) then
- lookForChests = true
- miningOffset = 0
- else
- lookForChests = false
- miningOffset = 1
- end
- -- Get the turtle resume location
- resumeFile = fs.open(oreQuarryLocation, "r")
- if (resumeFile ~= nil) then
- resumeMiningState = readNumber(resumeFile)
- resumeX = readNumber(resumeFile)
- resumeY = readNumber(resumeFile)
- resumeZ = readNumber(resumeFile)
- resumeOrient = readNumber(resumeFile)
- resumeFile.close()
- -- Ensure that the resume location has been found
- if ((resumeMiningState ~= nil) and (resumeX ~= nil) and (resumeY ~= nil) and (resumeZ ~= nil) and (resumeOrient ~= nil)) then
- returnVal = true
- local emptiedInventory = false
- -- Perform any mining state specific startup
- if (resumeMiningState == miningState.EMPTYINVENTORY) then
- -- Am mid way through an empty inventory cycle. Complete it before
- -- starting the main Quarry function
- returnToStartAndUnload(true)
- resuming = true
- -- Continue from the current position
- resumeX = currX
- resumeY = currY
- levelToReturnTo = resumeY
- resumeZ = currZ
- resumeOrient = currOrient
- writeMessage("Resuming with state of "..currMiningState, messageLevel.DEBUG)
- resumeMiningState = currMiningState
- emptiedInventory = true
- end
- -- If was emptying a chest when the program stopped, then move back
- -- to a point which the Quarry
- if (resumeMiningState == miningState.EMPTYCHESTDOWN) then
- -- Set the current X, Y, Z and orientation to the true position that
- -- the turtle is at
- if (emptiedInventory == false) then
- currX = resumeX
- currY = resumeY
- currZ = resumeZ
- currOrient = resumeOrient
- end
- -- Set the mining state as layer, assume haven't been through zero
- -- zero and set the level to return to as the one below the current one
- currMiningState = miningState.LAYER
- levelToReturnTo = currY - 2
- haveBeenAtZeroZeroOnLayer = false
- -- Temporarily disable resuming (so that the new location is written to the file
- -- in case the program stops again)
- resuming = false
- turtleUp()
- resuming = true
- resumeY = currY
- resumeMiningState = miningState.LAYER
- end
- end
- end
- end
- if (returnVal == false) then
- writeMessage("Failed to resume", messageLevel.ERROR)
- end
- end
- return returnVal
- end
- -- ********************************************************************************** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** Starting functions ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ********************************************************************************** --
- -- Process the input arguments - storing them to global variables
- local paramsOK = true
- function startOreQuarry()
- -- Detect whether this is a wireless turtle, and if so, open the modem
- local peripheralConnected = peripheral.getType("right")
- if (peripheralConnected == "modem") then
- isWirelessTurtle = true
- end
- -- If a wireless turtle, open the modem
- if (isWirelessTurtle == true) then
- turtleId = os.getComputerLabel()
- rednet.open("right")
- end
- if (#args == 0) then
- -- Is this a resume?
- if (isResume() == false) then
- paramsOK = false
- end
- elseif (#args == 1) then
- quarryWidth = tonumber(args[1])
- local x, y, z = gps.locate(5)
- startHeight = y
- if (startHeight == nil) then
- writeMessage("Can't locate GPS", messageLevel.FATAL)
- paramsOK = false
- end
- elseif (#args == 2) then
- if (args[2] == "/r") then
- quarryWidth = tonumber(args[1])
- supportResume = false
- else
- quarryWidth = tonumber(args[1])
- startHeight = tonumber(args[2])
- end
- elseif (#args == 3) then
- quarryWidth = tonumber(args[1])
- startHeight = tonumber(args[2])
- if (args[3] == "/r") then
- supportResume = false
- else
- paramsOK = false
- end
- end
- if ((paramsOK == false) and (resuming == false)) then
- writeMessage("Usage: "..shell.getRunningProgram().." <diameter> [turtleY] [/r]", messageLevel.FATAL)
- paramsOK = false
- end
- if (paramsOK == true) then
- if ((startHeight < 6) or (startHeight > 128)) then
- writeMessage("turtleY must be between 6 and 128", messageLevel.FATAL)
- paramsOK = false
- end
- if ((quarryWidth < 2) or (quarryWidth > 64)) then
- writeMessage("diameter must be between 2 and 64", messageLevel.FATAL)
- paramsOK = false
- end
- end
- if (paramsOK == true) then
- if (resuming == true) then
- writeMessage("Resuming Ore Quarry...", messageLevel.INFO)
- else
- writeMessage("----------------------------------", messageLevel.INFO)
- writeMessage("** Ore Quarry v0.71 by AustinKK **", messageLevel.INFO)
- writeMessage("----------------------------------", messageLevel.INFO)
- end
- -- Set the turtle's starting position
- currX = 0
- currY = startHeight
- currZ = 0
- currOrient = way.FORWARD
- -- Calculate which blocks in the inventory signify noise blocks
- if (resuming == false) then
- determineNoiseBlocksCountCount()
- end
- if ((noiseBlocksCount == 0) or (noiseBlocksCount > 13)) then
- writeMessage("No noise blocks have been been added. Please place blocks that the turtle should not mine (e.g. Stone, Dirt, Gravel etc.) in the first few slots of the turtle\'s inventory. The first empty slot signifies the end of the noise blocks.", messageLevel.FATAL)
- else
- -- If we are supporting resume (and are not currently in the process of resuming)
- -- then store startup parameters in appropriate files
- if ((supportResume == true) and (resuming == false)) then
- -- Write the startup parameters to file
- local outputFile = io.open(startupParamsFile, "w")
- outputFile:write(quarryWidth)
- outputFile:write("\n")
- outputFile:write(startHeight)
- outputFile:write("\n")
- outputFile:write(noiseBlocksCount)
- outputFile:write("\n")
- outputFile:write(lastEmptySlot)
- outputFile:write("\n")
- outputFile:close()
- -- Setup the startup file
- -- Take a backup of the current startup file
- if (fs.exists("startup") == true) then
- fs.copy("startup", startupBackup)
- outputFile = io.open("startup", "a")
- else
- outputFile = io.open("startup", "w")
- end
- -- Write an info message so that people know how to get out of auto-resume
- outputFile:write("\nprint(\"Running auto-restart...\")\n")
- outputFile:write("print(\"If you want to stop auto-resume and restore original state:\")\n")
- outputFile:write("print(\"1) Hold Ctrl-T until the program terminates\")\n")
- outputFile:write("print(\"2) Type \\\"rm startup\\\" (without quotes) and hit Enter\")\n")
- outputFile:write("print(\"\")\n\n")
- -- Write the code required to restart the turtle
- outputFile:write("shell.run(\"")
- outputFile:write(shell.getRunningProgram())
- outputFile:write("\")\n")
- outputFile:close()
- end
- -- Create a Quarry
- turtle.select(1)
- currentlySelectedSlot = 1
- createQuarry()
- -- Restore the file system to its original configuration
- if (supportResume == true) then
- fs.delete("startup")
- if (fs.exists(startupBackup) == true) then
- fs.move(startupBackup, "startup")
- end
- if (fs.exists(startupParamsFile) == true) then
- fs.delete(startupParamsFile)
- end
- if (fs.exists(oreQuarryLocation) == true) then
- fs.delete(oreQuarryLocation)
- end
- if (fs.exists(returnToStartFile) == true) then
- fs.delete(returnToStartFile)
- end
- end
- end
- end
- end
- -- ********************************************************************************** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** PROGRAMS ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ** ** --
- -- ********************************************************************************** --
- -- ********************************************************************************** --
- -- PROGRAM: STAIRS BUILDER
- -- Slots map:
- -- F B S S
- -- S S S S
- -- S S S S
- -- S S S S
- -- F - Fuel, B - Corner Blocks, S - stairs
- -- ********************************************************************************** --
- local strsW = 7
- local strsH = 7
- local strsDirect = true;
- local steps=2
- local isRunning = true
- -- Start loop for building
- function startStairsBuilder()
- currGlobalState = globalStates.STAIRS
- -- Set stairs size
- if (#args == 2) then
- strsW = tonumber(args[1])
- strsH = tonumber(args[1])
- elseif (#args == 3) then
- strsW = tonumber(args[1])
- strsH = tonumber(args[2])
- end
- -- Starting movements
- turtle.select(1)
- turtle.refuel()
- turtle.select(3)
- turtle.place()
- turtle.up()
- turtle.forward()
- turtle.forward()
- turtle.down()
- -- main loop
- while (isHaveStairs() and isRunning) do
- if (((steps < strsW) and strsDirect) or ((steps < strsH) and (strsDirect==false)) ) then
- turtle.placeUp()
- turtle.forward()
- if (((steps ~= (strsW-1)) and strsDirect) or ((steps ~= (strsH-1)) and (strsDirect==false)) ) then
- turtle.up()
- end
- steps = steps + 1
- else
- if(strsDirect)then
- strsDirect = false
- else
- strsDirect = true
- end
- steps = 2
- turtle.turnRight()
- turtle.select(2)
- turtle.placeUp()
- turtle.forward()
- turtle.up()
- turtle.select(currSelectedSlot)
- end
- end
- return true
- end
- -- Check inventory
- function isHaveStairsAndCorners()
- if(turtle.getItemCount(2) == 0) then
- return false
- end
- while ((turtle.getItemCount(currSelectedSlot) == 0) and (currSelectedSlot < 16)) do
- currSelectedSlot = currSelectedSlot + 1
- turtle.select(currSelectedSlot)
- end
- if (turtle.getItemCount(currSelectedSlot)==0) then
- return false
- else
- return true
- end
- end
- -- ********************************************************************************** --
- -- PROGRAM: DOCK BUILDER
- -- Slots map:
- -- F C B _
- -- _ _ _ _
- -- _ _ _ _
- -- _ _ _ _
- -- F - Fuel, C - Chest, B - Cobblestone, _ - Thrash
- --
- -- Additional: Must have chest from back at start
- -- ********************************************************************************** --
- function startBuildFirstStorage()
- p('startBuildFirstStorage()')
- -- Check is enough chests
- while (turtle.getItemCount(idSlots.chest) < idSlotsMinCount[idSlots.chest]) do
- writeMessage('Put at least 32 chests in slot'..idSlots.chest..'and press any key', messageLevel.ERROR)
- getAnyKey()
- end
- -- Build Thrash collector
- -- TMP
- currX = 0
- currY = 0
- currZ = 0
- -- Place 3 chests
- turtleTurn(way.LEFT)
- placeBlock(idSlots.chest)
- turtleTurn(way.RIGHT)
- placeBlock(idSlots.chest)
- turtleTurn(way.RIGHT)
- placeBlock(idSlots.chest)
- -- Move to storage
- turtleUp()
- turtleGoTo(beacon[beaconName.STORAGE].x, beacon[beaconName.STORAGE].y + 1, beacon[beaconName.STORAGE].z)
- turtleDown()
- -- Place bunch of chests
- local chestPlaced = 0
- while (chestPlaced < 16) do
- orientToChestInStorage()
- placeBlock(idSlots.chest)
- turtleSetOrientation(way.RIGHT)
- turtleForward()
- chestPlaced = chestPlaced+1
- end
- return true
- end
- -- ********************************************************************************** --
- -- PROGRAM: SortUnload
- -- Slots map:
- -- _ _ _ _
- -- _ _ _ _
- -- _ _ _ _
- -- _ _ _ _
- -- Emptyes all to different chests by type
- --
- --
- -- ********************************************************************************** --
- function program_unloadBySorting()
- p('program_unloadBySorting()')
- -- Go back to first
- turtleGoToBeacon(beaconName.STORAGE)
- -- Unload each slot in different chest
- -- TODO: Working with more then 16 items
- local slotLoop = 1
- while (slotLoop < 16) and (turtle.getItemCount(slotLoop) > 0) do
- orientToChestInStorage()
- turtle.select(slotLoop)
- if( turtle.drop() ) then
- turtleSetOrientation(way.RIGHT)
- turtleForward()
- else
- writeMessage('Cant drop items!', messageLevel.FATAL)
- -- TODO: Action if inventory is full or cant drop items
- return false
- end
- slotLoop = slotLoop+1
- end
- return true
- end
- -- ********************************************************************************** --
- -- PROGRAM: Fill
- -- Slots map:
- -- _ _ _ _
- -- _ _ _ _
- -- _ _ _ _
- -- _ _ _ _
- -- Build!!
- --
- --
- -- ********************************************************************************** --
- fillingFlags = {
- 'whole', -- All blocks will filling up
- 'sides', -- Making boxes
- 'mirror', -- Pattern texture will mirrored
- 'mirror -1', -- Each mirrored pattern indexes will shif by one block
- 'mirror -1x',
- 'mirror -1y',
- 'mirror -1z',
- 'y->', 'x->', -- Build first x-z blocks, then go to next y layer
- 'x++', 'x--', -- Shift next coord. Userful for stairs
- 'y++', 'y--',
- 'z++', 'z--',
- 'clear', -- Replaces all pattern indexes to -1
- 'clearAllSkipped',-- Replaces all 0 to -1
- 'skipClearing', -- Replaces all -1 to 0
- }
- function fill(_sizeX, _sizeY, _sizeZ, patternId, x, y, z, direction, isGoBackAfterFill, fillFlags)
- -- Default direction: DOWN
- if(direction==nil) then direction=way.DOWN end
- local correction = {x=0,y=0,z=0}
- if(direction==way.DOWN) then correction.y = 1 end
- if(direction==way.UP) then correction.y = -1 end
- -- TODO: Make support for all directions
- -- Default position: zero
- if(x==nil) then x=0 end
- if(y==nil) then y=0 end
- if(z==nil) then z=0 end
- -- Default pattern index - plain fill
- if(patternId == nil) then patternId = 'plain' end
- -- Default go home - yes, go back
- if(isGoBackAfterFill == nil) then isGoBackAfterFill = true end
- -- Compute slots pattern.
- -- Pattern shows what block index in what slot
- local slotsPattern = getSlotsPattern()
- -- =====================================================================
- -- =====================================================================
- local startPos = {x=currX, y=currY, z=currZ}
- -- Pattern sizes per align
- local ptSzY = #fillPattern[patternId]
- local ptSzZ = #fillPattern[patternId][1]
- local ptSzX = #fillPattern[patternId][1][1]
- -- There we will storage what slots was deplited, out of building blocks
- -- 0: slot in use, have blocks.
- -- 1: slot deplited, haven't blocks or have thrash
- local blacklistPattern = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
- -- Real sizes of dimensions
- local sizeX, sizeY, sizeZ
- local fabled_ptSzX,fabled_ptSzY,fabled_ptSzZ
- if (fillFlags['y->']) then
- sizeX, sizeY, sizeZ = _sizeX, _sizeZ, _sizeY
- fabled_ptSzX,fabled_ptSzY,fabled_ptSzZ = ptSzX,ptSzZ,ptSzY
- elseif(fillFlags['x->']) then
- sizeX, sizeY, sizeZ = _sizeZ, _sizeX, _sizeY
- fabled_ptSzX,fabled_ptSzY,fabled_ptSzZ = ptSzZ,ptSzX,ptSzY
- else
- sizeX, sizeY, sizeZ = _sizeX, _sizeY, _sizeZ
- fabled_ptSzX,fabled_ptSzY,fabled_ptSzZ = ptSzX,ptSzY,ptSzZ
- end
- local totalVolume = sizeX*sizeY*sizeZ
- for O=0, totalVolume-1 do
- local _u, _v, _w -- Imaginationed positions on 3d grid
- local u, v, w -- Real position on 3d grid
- _u = math.floor(O/(sizeX*sizeZ))
- _v = math.floor(O/sizeX) % sizeZ
- _w = math.floor(O%sizeX)
- -- Ping-pong
- if (not fillFlags['y->']) and (not fillFlags['x->']) then
- if(_u%2==1) then _v = sizeZ - _v - 1 end
- else
- end
- if(_v%2==1) then _w = sizeX - _w - 1 end
- -- Change filling directions
- if (fillFlags['y->']) then
- u,v,w = _v, _u, _w
- elseif(fillFlags['x->']) then
- u,v,w = _w, _u, _v
- else
- u,v,w = _u, _v, _w
- end
- -- Pattern piker must think we are on this positions
- local fabled_u, fabled_v, fabled_w = u,v,w
- if(fillFlags['mirror -1'] or fillFlags['mirror -1z']) then
- fabled_u = u + math.floor( (u+fabled_ptSzY-1) / (fabled_ptSzY*2-1) )
- end
- if(fillFlags['mirror -1'] or fillFlags['mirror -1y']) then
- fabled_v = v + math.floor( (v+fabled_ptSzZ-1) / (fabled_ptSzZ*2-1) )
- end
- if(fillFlags['mirror -1'] or fillFlags['mirror -1x']) then
- fabled_w = w + math.floor( (w+fabled_ptSzX-1) / (fabled_ptSzX*2-1) )
- end
- -- Compute pattern array indexes. Place on pattern that we want to take
- local ptX, ptY, ptZ = (fabled_w)%ptSzX+1, (fabled_u)%ptSzY+1, (fabled_v)%ptSzZ+1
- -- Flag "mirror" must mirrored all coordinates on even step
- -- Don't forget add here all mirrors flags!
- if(fillFlags['mirror'] or fillFlags['mirror -1'] or fillFlags['mirror -1x'] or
- fillFlags['mirror -1y'] or fillFlags['mirror -1z']) then
- if (math.floor(fabled_w/ptSzX) % 2) == 1 then ptX = 1+ptSzX-ptX end
- if (math.floor(fabled_u/ptSzY) % 2) == 1 then ptY = 1+ptSzY-ptY end
- if (math.floor(fabled_v/ptSzZ) % 2) == 1 then ptZ = 1+ptSzZ-ptZ end
- end
- -- Shift next coord
- local shift = {}
- shift.x = v*(fillFlags['x++'] and 1 or (fillFlags['x--'] and -1 or 0))
- shift.y = w*(fillFlags['y++'] and 1 or (fillFlags['y--'] and -1 or 0))
- shift.z = v*(fillFlags['z++'] and 1 or (fillFlags['z--'] and -1 or 0))
- -- Standart type of fill rule, take all pattern indexes
- -- Get block index from pattern, demands on position of turtle
- local blockIndexToPlace = fillPattern[patternId][ptY][ptZ][ptX]
- if fillFlags['sides'] then
- if(u>0 and u<sizeY-1) and (v>0 and v<sizeZ-1) and (w>0 and w<sizeX-1) then
- blockIndexToPlace = 0
- end
- end
- if fillFlags['clear'] then blockIndexToPlace = -1 end
- if fillFlags['clearAllSkipped'] then if blockIndexToPlace== 0 then blockIndexToPlace = -1 end end
- if fillFlags['skipClearing'] then if blockIndexToPlace==-1 then blockIndexToPlace = 0 end end
- if( blockIndexToPlace ~= 0) then
- turtleGoTo(x+w + correction.x +shift.x,
- y+u + correction.y +shift.z,
- z+v + correction.z +shift.y + 1) -- Z-Shift means that turtle 0 0 0 point is forward
- if blockIndexToPlace > 0 then
- local slotWithNeededItem = findInSlotsArrayByPattern(slotsPattern, blockIndexToPlace, blacklistPattern)
- if(slotWithNeededItem ~= 0) then
- -- We have block and can put it on place
- placeBlock(slotWithNeededItem, direction)
- -- If slot was emptyed, we must note, that now there will be thrash
- -- Each next time when turtle dig, empty slot will filling
- if(turtle.getItemCount(slotWithNeededItem) == 0) then
- blacklistPattern[slotWithNeededItem] = 1
- -- Check again if we have another slot with item
- if( findInSlotsArrayByPattern(slotsPattern, blockIndexToPlace, blacklistPattern) == 0)then
- -- No avaliable blocks to build!
- -- Save coords, take more blocks and return
- local buildProgressStopPos = {x=currX, y=currY, z=currZ}
- turtleGoTo(currX, _sizeY+1, currZ)
- turtleGoTo(currX, _sizeY+1, 0)
- local reloadedPattern = reloadForFilling(slotsPattern, blacklistPattern)
- -- If some slots was not reloaded, leave them in blacklist
- for i=1, 16 do
- if(reloadedPattern[i] > 0) then blacklistPattern[i] = 0 end
- end
- -- TODO: Path finding for faster go back
- turtleGoTo(currX, _sizeY+1, currZ)
- turtleGoTo(buildProgressStopPos.x, buildProgressStopPos.y, buildProgressStopPos.z)
- end
- end
- else
- -- No blocks to place
- p('No blocks to plate here')
- return false
- end
- else
- -- Pattern says here is index < 0. Remove block here and do nothing
- turtleDig(direction)
- end
- else
- -- blockIndexToPlace == 0
- end
- end
- -- Now we finished filling territory. Just go home
- turtleGoTo(startPos.x, startPos.y, startPos.z)
- return true
- end
- -- ********************************************************************************** --
- -- PROGRAM: BuildSmeltery
- -- Slots map:
- -- _ _ _ _
- -- _ _ _ _
- -- _ _ _ _
- -- _ _ _ _
- -- Build small bunch of furnaces
- --
- --
- -- ********************************************************************************** --
- function program_buildSmeltery()
- p('program_buildSmeltery()')
- local furnaceCount = 7 -- TODO: Correct value after tests
- craft(idSlots.furnace, furnaceCount)
- -- Go to smeltery
- fill(3, 1, 3, 'O_hole', beacon[beaconName.SMELTERY].x, beacon[beaconName.SMELTERY].y, beacon[beaconName.SMELTERY].z)
- return true
- end
- -- ********************************************************************************** --
- -- Place item in front of turtle. Check if item already placed.
- -- We must take +1 block: chest must have at least 1 block to store
- -- for ID compares
- -- ********************************************************************************** --
- function takeBlocksFromStorage(itemSlotId, count)
- -- All slots must be empty
- for i=1,#idSlots do
- if( turtle.getItemCount(i) > 0 ) then
- -- TODO: Action when inventory has items
- writeMessage('Inventory have items, must clear all', messageLevel.ERROR)
- return false
- end
- end
- local x,y,z = getStoragePosByItemSlotID(itemSlotId)
- turtleGoTo(x,y,z)
- orientToChestInStorage()
- local itemsSucked = 0
- local slotLoop = 1
- while (itemsSucked < (count + 1)) do
- turtle.select(slotLoop)
- if( turtle.suck() ) then
- itemsSucked = itemsSucked + turtle.getItemCount(slotLoop)
- else
- p('Not enought items')
- -- Drop taken items back
- for i=slotLoop, 1, -1 do
- turtle.select(i)
- turtle.drop()
- end
- return false
- end
- slotLoop = slotLoop + 1
- end
- -- Place excess items back
- -- Including one additional item for filter
- if( itemsSucked > count ) then
- local excessCount = itemsSucked - count
- turtle.drop(excessCount)
- end
- return true
- end
- -- ********************************************************************************** --
- -- Go to the storage and suck needed blocks. Storage must be defined
- -- ********************************************************************************** --
- function reloadForFilling(slotsPattern, blacklistPattern)
- local reloadedPattern = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
- -- Default blacklist. No thrash to drop
- if(blacklistPattern==nil) then blacklistPattern = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} end
- -- First, unload blacklisted slotls
- -- This is blocks that we dont using for building
- turtleGoTo(0,0,0)
- turtleSetOrientation(way.LEFT)
- for i=1, 16 do
- if( blacklistPattern[i] > 0) then
- turtle.select(i)
- turtle.drop()
- end
- end
- -- Then move to storages and take blocks
- for i=1, 16 do
- local itemSpace = turtle.getItemSpace(i)
- if( itemSpace > 0 and (slotsPattern[i] > 0)) then
- -- TODO: Working with different kind of storages, not only chest array
- turtleGoTo(0+(slotsPattern[i]-1)*2,0,0)
- turtleSetOrientation(way.BACK)
- turtle.select(i)
- if( turtle.suck(itemSpace) ) then
- -- Yes, we sucked something. Lets write it in pattern
- reloadedPattern[i] = 1
- end
- end
- end
- return reloadedPattern
- end
- -- ********************************************************************************** --
- -- Place item in front of turtle. Check if item already placed.
- -- ********************************************************************************** --
- function placeBlock(itemSlot, direction)
- local detectFnc, compareFnc, placeFnc, attackFnc =
- turtle.detect, turtle.compare, turtle.place,turtle.attack
- if( direction == way.UP ) then
- detectFnc, compareFnc, placeFnc, attackFnc =
- turtle.detectUp, turtle.compareUp, turtle.placeUp, turtle.attackUp
- elseif( direction == way.DOWN )then
- detectFnc, compareFnc, placeFnc, attackFnc =
- turtle.detectDown, turtle.compareDown, turtle.placeDown, turtle.attackDown
- end
- -- slotsPattern is array of 16 nubbers that represent
- -- what kind of blocks lying in what kind of
- if(itemSlot == nil) then
- selectNonEmptySlot()
- else
- turtle.select(itemSlot)
- end
- local placeSucces = false
- local digCount = 0
- local maxDigCount = 20
- -- Check if there is already item then try to place
- placeSucces = placeFnc()
- if((not placeSucces) and detectFnc()) then
- if(compareFnc()) then
- -- Item that we must set already here
- return true
- else
- -- There is something else. Dig/Attack and place item
- turtleDig(direction)
- digCount = digCount + 1
- end
- end
- -- Now try to place item until item will placed
- while ((placeSucces == false) and (digCount < maxDigCount)) do
- if (detectFnc()) then
- if(digCount > 0) then
- sleep(0.1)
- end
- turtleDig(direction)
- digCount = digCount + 1
- else
- -- Am being stopped from moving by a mob, attack it
- attackFnc()
- end
- -- Try the place again
- placeSucces = placeFnc()
- end
- return placeSucces
- end
- -- ********************************************************************************** --
- -- Select non-empty slot
- -- ********************************************************************************** --
- function selectNonEmptySlot()
- for i=1, 16 do
- if( turtle.getItemCount(i) > 0) then
- turtle.select(i)
- return true
- end
- end
- return false
- end
- -- ********************************************************************************** --
- -- Get slot pattern.
- -- Returning array(16) that represent indexes in slots
- -- ********************************************************************************** --
- function getSlotsPattern()
- local ptattern = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} -- Standart pattern. All slots empty
- local lastUnemptySlot = 0
- local lastEnum = 1
- for i=1, 16 do
- turtle.select(i)
- if( turtle.getItemCount(i) > 0) then
- if (lastUnemptySlot == 0) then
- lastUnemptySlot = i
- elseif (i>1) and (turtle.compareTo(lastUnemptySlot) == false) then
- lastEnum = lastEnum + 1
- lastUnemptySlot = i
- end
- ptattern[i] = lastEnum
- end
- end
- return ptattern
- end
- -- ********************************************************************************** --
- -- Craft things.
- -- ********************************************************************************** --
- function craft(itemSlotId, count)
- print('craft('..itemSlotId..','..count..')')
- local itemsForPattern = 0
- -- Count how much we need
- for i=1, 16 do
- if( craftPattern[itemSlotId][i] > 0 ) then
- itemsForPattern = itemsForPattern + 1
- end
- end
- -- TODO: Change code to work with different kind of crafting blocs
- takeBlocksFromStorage(craftPattern[itemSlotId][1], count * itemsForPattern)
- -- Move items like in craftPattern
- -- TODO: Working with large numbers of items
- turtle.select(1)
- local lastSelectedSlot = 1
- for i=2, 16 do
- if( craftPattern[itemSlotId][i] > 0 ) then
- turtle.transferTo(i, turtle.getItemCount( lastSelectedSlot ) - count)
- turtle.select(i)
- lastSelectedSlot = i
- end
- end
- turtle.select(1)
- turtle.craft()
- return true
- end
- -- ********************************************************************************** --
- -- ********************************************************************************** --
- -- ********************************************************************************** --
- function addBeaconCoords(x,y,z,beaconId)
- p("addBeaconCoords:"..x.." "..y.." "..z..' '..beaconId)
- beacon[beaconId] = {x=0, y=0, z=0}
- beacon[beaconId].x = x
- beacon[beaconId].y = y
- beacon[beaconId].z = z
- end
- function saveBeacon(b)
- addBeaconCoords(currX,currY,currZ,b)
- end
- function turtleGoToBeacon(b)
- turtleGoTo(beacon[b].x,beacon[b].y,beacon[b].z)
- end
- function getStoragePosByItemSlotID(itemSlotId)
- return beacon[beaconName.STORAGE].x + itemSlotId-1,
- beacon[beaconName.STORAGE].y,
- beacon[beaconName.STORAGE].z
- end
- function turtleGoTo(x,y,z)
- -- TODO: Check highway and path finding
- while (x<currX) do
- turtleSetOrientation(way.LEFT)
- turtleForward()
- end
- while (x>currX) do
- turtleSetOrientation(way.RIGHT)
- turtleForward()
- end
- while (z<currZ) do
- turtleSetOrientation(way.BACK)
- turtleForward()
- end
- while (z>currZ) do
- turtleSetOrientation(way.FORWARD)
- turtleForward()
- end
- while (y<currY) do
- turtleDown()
- end
- while (y>currY) do
- turtleUp()
- end
- end
- function orientToChestInStorage()
- if( ((beacon[beaconName.STORAGE].x - currX) % 2) == 0 ) then
- turtleSetOrientation(way.FORWARD)
- else
- turtleSetOrientation(way.BACK)
- end
- end
- function selectPatternByBlacklist(pattern, blacklist)
- local resultPattern = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
- for i=1, 16 do
- if(blacklist[i] == 0) then resultPattern[i] = pattern[i] end
- end
- return resultPattern
- end
- function findInSlotsArrayByPattern(arr, n, blacklist)
- blacklist = blacklist or {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
- -- Set indexes to 0 if blacklist here is 1
- local filteredArr = selectPatternByBlacklist(arr, blacklist)
- for i=1, 16 do
- if(filteredArr[i] == n) and (turtle.getItemCount(i) > 0) then
- return i
- end
- end
- return 0
- end
- -- ********************************************************************************** --
- -- Main Function
- -- ********************************************************************************** --
- local args = { ... }
- startupMode = {
- 'Refuel all slots','Fill'
- }
- -- Load wrappers, if we lunch from other place
- if(turtle == nil) then
- dofile('src/ComputerCraft.lua')
- end
- function startup()
- term.clear()
- term.setCursorPos(1,1)
- local welcomingText = [[
- =======================================
- == Krutoy Turtle - universal builder ==
- =======================================
- ]]
- sleep(1)
- while true do
- local paramsLine = welcomingText
- local separator = '----------------------\n'
- local result = 0
- local currLine = 'Select mode:\n 1 - Refuel all slots\n 2 - Fill'
- result = readNumberParametr(paramsLine..currLine, 1, #startupMode)
- local mode = startupMode[result]
- if(mode == 'Refuel all slots') then
- clear()
- for i=1, 16 do
- turtle.select(i)
- turtle.refuel()
- end
- end
- if(mode == 'Fill') then
- paramsLine = paramsLine..'MODE: Fill\n'
- currLine = 'Select fill pattern:\n'
- local pattern = nil
- local n = 1
- for k,v in pairs(fillPattern) do
- currLine = currLine..' '..n..' - '..k..'\n'
- n = n+1
- end
- result = readNumberParametr(paramsLine..separator..currLine, 1, n)
- n = 1
- for k,v in pairs(fillPattern) do
- if(n == result) then
- pattern = k
- paramsLine = paramsLine..'PATTERN: '..pattern..'\n'
- break
- end
- n = n+1
- end
- currLine = 'Specify size by x y z, separate with spaces, and press ENTER:\n'
- result = readTableOfNumbers(paramsLine..separator..currLine, false, 3, "%S+")
- local sizeX,sizeY,sizeZ = result[1],result[2],result[3]
- paramsLine = paramsLine..'SIZE: {'..sizeX..', '..sizeY..', '..sizeZ..'}\n'
- currLine = 'Set position by x y z, separate with spaces, and press ENTER\n(or press ENTER to skip)\n'
- result = readTableOfNumbers(paramsLine..separator..currLine, true, 3, "%S+")
- local x,y,z = result[1],result[2],result[3]
- paramsLine = paramsLine..'POS: {'..x..', '..y..', '..z..'}\n'
- currLine = 'Add flags if need, separate with commas, and press ENTER\n'
- result = readTable(paramsLine..separator..currLine, true, "%s*([^,]+)")
- local fillFlags = {}
- if(result) then fillFlags = makeSet(result) end
- clear()
- print(separator..paramsLine..separator)
- sleep(1)
- ---------------------------------------
- fill(sizeX,sizeZ,sizeY, pattern, x,y,z, way.DOWN, true, fillFlags)
- end
- end
- end
- --[[
- -- Code to connect other file to project
- local response=http.get("http://pastebin...")
- local filetext=response.readAll()
- local func,err=loadstring(filetext,"имя чанка")
- if not err then
- setfenv(func,getfenv(1))
- func()
- else
- error(err)
- end
- ]]--
- startup()
Add Comment
Please, Sign In to add comment