Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Hollow
- -- Fuel will be pulled from the internal
- -- slot first, chest if equipped and then
- -- from the drop slots. When the drop
- -- slots are full the turtle will either
- -- use the ender chest or return and drop
- -- behind the starting point.
- local drop_slots = { 1, 13 }
- local fuel_slot = 14
- local fuel_chest_slot = 15
- local drop_chest_slot = 16
- local verbose = 0
- local refuel_level = 0
- local mode = { dig = false, safe = false, drop = true }
- local marks = { start = 1, edge = 2, block = 3, empty = 4, entry = 5 }
- local lip_range = 5
- local log_file = nil
- local position = { 0, 0, 0, 3 }
- local drop_position = { 0, 0, 0, 1 }
- local drop_waypoint = nil
- local direction = { [0] = { 0, 0, 1, 0 },
- [1] = { -1, 0, 0, 0 },
- [2] = { 0, 0, -1, 0 },
- [3] = { 1, 0, 0, 0 } }
- local block = {}
- local bounds = { lower = { 0, 0, 0 }, upper = { 0, 0, 0 } }
- local dropping = false
- local log_handle = nil
- function deepcopy(orig)
- local orig_type = type(orig)
- local copy
- if orig_type == 'table' then
- copy = {}
- for orig_key, orig_value in next, orig, nil do
- copy[deepcopy(orig_key)] = deepcopy(orig_value)
- end
- setmetatable(copy, deepcopy(getmetatable(orig)))
- else
- copy = orig
- end
- return copy
- end
- function getopt(optstring, ...)
- local opts = { }
- local args = { ... }
- for optc, optv in optstring:gmatch"(%a)(:?)" do
- opts[optc] = { hasarg = optv == ":" }
- end
- return coroutine.wrap(function()
- local yield = coroutine.yield
- local i = 1
- while i <= #args do
- local arg = args[i]
- i = i + 1
- if arg == "--" then
- break
- elseif arg:sub(1, 1) == "-" then
- for j = 2, #arg do
- local opt = arg:sub(j, j)
- if opts[opt] then
- if opts[opt].hasarg then
- if j == #arg then
- if args[i] then
- yield(opt, args[i])
- i = i + 1
- elseif optstring:sub(1, 1) == ":" then
- yield(':', opt)
- else
- yield('?', opt)
- end
- else
- yield(opt, arg:sub(j + 1))
- end
- break
- else
- yield(opt, false)
- end
- else
- yield('?', opt)
- end
- end
- else
- yield(false, arg)
- end
- end
- for i = i, #args do
- yield(false, args[i])
- end
- end)
- end
- function writeLog(msg)
- if log_file ~= nil then
- writeLog = function (msg)
- log_handle = fs.open(log_file, 'a')
- log_handle.writeLine(msg)
- log_handle.close()
- end
- else
- writeLog = print
- end
- writeLog(msg)
- end
- function formatPosition(p)
- return p[1]..","..p[2]..","..p[3]..(p[4] == nil and "" or "," .. p[4])
- end
- function updatePosition(axis, value)
- if axis == 4 then
- position[axis] = (position[axis] + value) % 4
- else
- position[axis] = position[axis] + value
- end
- end
- function inPosition(p)
- return p ~= nil and position[1] == p[1] and position[2] == p[2] and position[3] == p[3]
- end
- function directionLessPosition(p)
- local p = deepcopy(p)
- p[4] = nil
- return p
- end
- function getDirection(s, e)
- return {
- s[1] - e[1] <= 0 and 1 or -1,
- s[2] - e[2] <= 0 and 1 or -1,
- s[3] - e[3] <= 0 and 1 or -1
- }
- end
- function dig()
- if mode.dig and turtle.dig() and mode.drop and getEmptySlots() == 0 then dropOff() end
- end
- function digUp()
- if mode.dig and turtle.digUp() and mode.drop and getEmptySlots() == 0 then dropOff() end
- end
- function digDown()
- if mode.dig and turtle.digDown() and mode.drop and getEmptySlots() == 0 then dropOff() end
- end
- function forward()
- turtle.attack()
- if mode.safe and isMarkForward(marks.edge) then return false end
- if turtle.detect() then dig() end
- if turtle.getFuelLevel() <= refuel_level then refuel() end
- if turtle.forward() then
- if (position[4] == 0) then updatePosition(3,1)
- elseif (position[4] == 1) then updatePosition(1,-1)
- elseif (position[4] == 2) then updatePosition(3,-1)
- elseif (position[4] == 3) then updatePosition(1,1)
- end
- return true
- else
- return false
- end
- end
- function back()
- if turtle.getFuelLevel() <= refuel_level then refuel() end
- if turtle.back() then
- if (position[4] == 0) then updatePosition(3,-1)
- elseif (position[4] == 1) then updatePosition(1,1)
- elseif (position[4] == 2) then updatePosition(3,1)
- elseif (position[4] == 3) then updatePosition(1,-1)
- end
- return true
- else
- return false
- end
- end
- function up()
- turtle.attackUp()
- if mode.safe and isMarkUp(marks.edge) then return false end
- if turtle.detectUp() then digUp() end
- if turtle.getFuelLevel() <= refuel_level then refuel() end
- local state = turtle.up()
- if (state) then updatePosition(2,1) end
- return state
- end
- function down()
- turtle.attackDown()
- if mode.safe and isMarkDown(marks.edge) then return false end
- if turtle.detectDown() then digDown() end
- if turtle.getFuelLevel() <= refuel_level then refuel() end
- local state = turtle.down()
- if (state) then updatePosition(2,-1) end
- return state
- end
- function right()
- updatePosition(4,1)
- turtle.turnRight()
- end
- function left()
- updatePosition(4,-1)
- turtle.turnLeft()
- end
- function turn(d)
- if (d == nil) then return end
- while position[4] ~= d do
- diff = position[4] - d
- if diff == 1 or diff == -3 then left() else right() end
- end
- end
- function moveX(x, wait)
- if (wait == nil) then wait = false end
- result = true
- if (position[1] == x) then return result end
- turn(position[1] < x and 3 or 1)
- while position[1] ~= x do
- result = forward()
- if (not result and not wait) then break end
- end
- return result
- end
- function moveY(y, wait)
- if (wait == nil) then wait = false end
- result = true
- if (position[2] == y) then return result end
- while position[2] ~= y do
- result = position[2] < y and up() or down()
- if (not result and not wait) then break end
- end
- return result
- end
- function moveZ(z, wait)
- if (wait == nil) then wait = false end
- result = true
- if (position[3] == z) then return result end
- turn(position[3] < z and 0 or 2)
- while position[3] ~= z do
- result = forward()
- if (not result and not wait) then break end
- end
- return result
- end
- function gotoMine(p, wait)
- moveX(p[1], wait)
- moveY(p[2], wait)
- moveZ(p[3], wait)
- if (p[4] ~= nil) then turn(p[4]) end
- end
- function goto(p, wait)
- if(p[3] == nil) then
- moveX(p[1], wait)
- moveZ(p[3], wait)
- elseif(position[2] > p[2]) then
- moveY(p[2], wait)
- moveZ(p[3], wait)
- moveX(p[1], wait)
- else
- moveX(p[1], wait)
- moveZ(p[3], wait)
- moveY(p[2], wait)
- end
- if (p[4] ~= nil) then turn(p[4]) end
- end
- function gotoSmart(p)
- if (p == nil) then return false end
- local last
- for tries = 0,8,1 do
- if position[2] <= p[2] then moveY(p[2]) end
- if (tries%2) == 0 then
- moveX(p[1])
- moveZ(p[3])
- else
- moveZ(p[3])
- moveX(p[1])
- end
- if position[2] > p[2] then moveY(p[2]) end
- if inPosition(p) then break end
- if inPosition(last) then
- pathFind(tries)
- end
- last = deepcopy(position)
- end
- if (p[4] ~= nil) then turn(p[4]) end
- return true
- end
- function gotoPath(path)
- for k,v in pairs(path) do
- goto(v, false)
- end
- end
- function pathFind(level)
- if level%5 == 0 then
- left()
- for cl = 0,level,1 do
- forward()
- cl=cl+1
- end
- elseif level%5 == 1 then
- left()
- for cl = 0,level,1 do
- forward()
- up()
- end
- elseif level%5 == 2 then
- left()
- for cl = 0,level,1 do
- up()
- end
- elseif level%5 == 3 then
- right()
- for cl = 0,level,1 do
- forward()
- up()
- end
- elseif level%5 == 4 then
- right()
- for cl = 0,level,1 do
- forward()
- end
- end
- end
- function getEmptySlots()
- numslots = 0
- for i = drop_slots[1], drop_slots[2] do
- if turtle.getItemCount(i) == 0 then
- numslots = numslots + 1
- end
- end
- return numslots
- end
- function getOpenSpace()
- local result = {}
- if not turtle.detect() then
- result.place = turtle.place
- result.detect = turtle.detect
- result.drop = turtle.drop
- result.suck = turtle.suck
- result.dig = turtle.dig
- elseif not turtle.detectUp() then
- result.place = turtle.placeUp
- result.detect = turtle.detectUp
- result.drop = turtle.dropUp
- result.suck = turtle.suckUp
- result.dig = turtle.digUp
- elseif not turtle.detectDown() then
- result.place = turtle.placeDown
- result.detect = turtle.detectDown
- result.drop = turtle.dropDown
- result.suck = turtle.suckDown
- result.dig = turtle.digDown
- else
- result = nil
- end
- return result
- end
- function useChest(slot, callback, param)
- local chest = getOpenSpace()
- local result = false
- if chest.place ~= nil then
- turtle.select(slot)
- if chest.place() then
- result = true
- if callback ~= nil then result = callback(chest) end
- turtle.select(slot)
- chest.dig()
- end
- end
- return result
- end
- function dropItemsInChest(chest)
- result = false
- if chest.detect() then
- for i = drop_slots[1], drop_slots[2] do
- turtle.select(i)
- chest.drop()
- end
- result = true
- end
- return result
- end
- function getItemFromChest(chest, param)
- turtle.select(param)
- return chest.detect() and chest.suck()
- end
- function dropOff()
- if dropping then return true end
- if verbose then writeLog("Drop Off") end
- dropping = true
- repeat
- if not useChest(drop_chest_slot, dropItems) then
- local resume = deepcopy(position)
- mode.dig = false
- gotoSmart(drop_waypoint)
- local old_mode = mode.safe
- mode.safe = false
- gotoSmart(drop_position)
- repeat
- if turtle.detect() then
- for i = drop_slots[1], drop_slots[2] do
- turtle.select(i)
- turtle.drop()
- end
- else
- sleep(1)
- end
- until getEmptySlots() ~= 0
- gotoSmart(drop_waypoint)
- mode.safe = old_mode
- gotoSmart(resume)
- mode.dig = true
- end
- until getEmptySlots() ~= 0
- dropping = false
- turtle.select(drop_slots[1])
- if verbose then writeLog("Resuming") end
- end
- function refuelFrom(slot)
- local result
- turtle.select(slot)
- while turtle.getItemCount(slot) ~= 0 and (turtle.getFuelLevel() < refuel_level or turtle.getFuelLevel() == 0) do
- if not turtle.refuel(1) then break end
- end
- end
- function refuel()
- if verbose then writeLog("Refueling") end
- while turtle.getFuelLevel() == 0 do
- if turtle.getItemCount(fuel_slot) ~= 0 or useChest(fuel_chest_slot, getFuelFromChest, fuel_slot) then
- if verbose > 1 then writeLog("Checking Fuel Slot") end
- refuelFrom(fuel_slot)
- else
- if verbose > 1 then writeLog("Searching Drop Slots") end
- for i = drop_slots[1], drop_slots[2] do
- refuelFrom(i)
- if turtle.getFuelLevel() >= refuel_level then break end
- end
- end
- if turtle.getFuelLevel() == 0 then sleep(1) end
- end
- turtle.select(drop_slots[1])
- if verbose then writeLog("Resuming") end
- end
- function addPoint(a, b)
- local c = {}
- for i = 1, math.min(#a, #b) do
- if a[i] == nil then
- c[i] = b[i]
- elseif b[i] == nil then
- c[i] = a[i]
- else
- c[i] = a[i] + b[i]
- end
- end
- return c
- end
- function strPoint(point)
- return point[1] .. ',' .. point[2] .. ',' .. point[3]
- end
- function adjustBounds(point)
- for i = 1,3 do
- if point[i] < bounds.lower[i] then bounds.lower[i] = point[i] end
- if point[i] > bounds.upper[i] then bounds.upper[i] = point[i] end
- end
- end
- function setMark(mark, adjust, point, map)
- if map == nil then map = block end
- if point == nil then point = position end
- if adjust ~= nil then point = addPoint(point, adjust) end
- adjustBounds(point)
- map[strPoint(point)] = mark
- end
- function setMarkForward(mark)
- setMark(mark, direction[position[4]])
- end
- function getMark(point, map)
- if map == nil then map = block end
- if point == nil then point = position end
- return map[strPoint(point)]
- end
- function isMark(mark, adjust, point, map)
- if map == nil then map = block end
- if point == nil then point = position end
- if adjust ~= nil then point = addPoint(point, adjust) end
- return getMark(point, map) == mark
- end
- function isMarkForward(mark)
- return isMark(mark, direction[position[4]])
- end
- function isMarkUp(mark)
- return isMark(mark, {0,1,0,0})
- end
- function isMarkDown(mark)
- return isMark(mark, {0,-1,0,0})
- end
- function isBetweenMark(mark)
- local point = deepcopy(position)
- local found = { upper = false, lower = false }
- for x = point[1], bounds.lower[1], -1 do
- if isMark(mark, { x, 0, 0 }, point) then
- found.lower = true
- break
- end
- end
- for x = point[1], bounds.upper[1], 1 do
- if isMark(mark, { x, 0, 0 }, point) then
- found.upper = true
- break
- end
- end
- return found.upper and found.lower
- end
- function floodRecursiveXZ(replace, with, point)
- if verbose > 1 then writeLog("Flood (Dig): " .. formatPosition(point) .. " Mark: " .. getMark(point)) end
- if not isMark(replace, nil, point) then return end
- if verbose > 1 then writeLog("Dig: " .. formatPosition(point)) end
- gotoMine(point)
- setMark(with, nil, point)
- floodRecursiveXZ(replace, with, addPoint(point, direction[2]))
- floodRecursiveXZ(replace, with, addPoint(point, direction[3]))
- floodRecursiveXZ(replace, with, addPoint(point, direction[0]))
- floodRecursiveXZ(replace, with, addPoint(point, direction[1]))
- goto(point)
- end
- function floodQueueXZ(replace, with, point)
- local queue = { point }
- local node = nil
- while #queue ~= 0 do
- node = table.remove(queue)
- if isMark(replace, nil, node) then
- setMark(with, nil, node)
- goto(node, false)
- for k,v in pairs(direction) do
- table.insert(queue, addPoint(node, v))
- end
- end
- end
- end
- function walkEdge()
- left()
- if turtle.detect() then
- setMarkForward(marks.edge)
- elseif forward() then
- right()
- if turtle.detect() then
- setMarkForward(marks.edge)
- elseif forward() then
- right()
- if turtle.detect() then
- setMarkForward(marks.edge)
- else
- if verbose > 0 then writeLog("Could not find way around") end
- return false
- end
- end
- end
- return true
- end
- function findLip(detect, move, message)
- local lip = 0
- while detect() do
- move()
- if lip == lip_range then
- if verbose > 0 then writeLog(message) end
- return false
- end
- lip = lip + 1
- end
- return true
- end
- function hollow(levels)
- local rest = deepcopy(position)
- local start = nil
- local lip = 0
- local ret = nil
- if verbose > 0 then writeLog("Mapping") end
- while not turtle.detect() do forward() end
- start = deepcopy(position)
- for y = 1, levels do
- if not findLip(function () return not turtle.detect() end, forward, "No edge after " .. lip_range) then return end
- setMark(marks.start)
- setMarkForward(marks.edge)
- while isMark(marks.start) do if not walkEdge() then return end end
- while not isMark(marks.start) do if not walkEdge() then return end end
- if y ~= levels then
- if not findLip(turtle.detectUp, back, "No way up after " .. lip_range) then return end
- up()
- end
- end
- gotoSmart(start)
- if verbose > 0 then writeLog("Clearing") end
- mode.dig = true
- turtle.select(drop_slots[1])
- forward()
- setMark(marks.entry)
- forward()
- mode.safe = true
- drop_waypoint = deepcopy(position)
- for y = 1, levels do
- -- floodRecursiveXZ(nil, marks.empty, point) - imagine this runs out of stack space on large areas
- ret = deepcopy(position)
- floodQueueXZ(nil, marks.empty, directionLessPosition(position))
- gotoSmart(ret)
- if y ~= levels then
- if verbose > 0 then writeLog("Next Level") end
- if isBetweenMark(marks.edge) then -- inside next level
- up()
- else
- while not isMarkUp(marks.edge) do forward() end -- outside next level
- forward()
- up()
- end
- end
- end
- gotoSmart(drop_waypoint)
- if mode.drop then dropOff() end
- mode.safe = false
- mode.dig = false
- goto(rest)
- if verbose then writeLog("Done") end
- end
- local tArgs = {}
- for opt, arg in getopt(":nl:v:", ...) do
- if opt == 'n' then mode.drop = false
- elseif opt == 'l' then log_file = arg
- elseif opt == 'v' then verbose = tonumber(arg)
- else table.insert(tArgs, arg)
- end
- end
- if #tArgs == 1 then
- if verbose > 0 then writeLog("Starting") end
- hollow(tonumber(tArgs[1]))
- else
- print("usage: hollow [options] -- <levels>")
- print()
- print(" -n No drop")
- print(" -l file log verbose oupput to file")
- print(" -v level enable verbose output")
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement