Skip to content

Instantly share code, notes, and snippets.

@DavidGoldman
Last active February 14, 2021 01:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DavidGoldman/f73492ec45a27358dcfcc3aea1b08964 to your computer and use it in GitHub Desktop.
Save DavidGoldman/f73492ec45a27358dcfcc3aea1b08964 to your computer and use it in GitHub Desktop.
Minecraft Strip Miner (Minecraft 1.12 requires Plethora Peripherals, Minecraft 1.7.10 requires Open Peripherals)
--[[ Strip Miner by Davidee inspired by https://pastebin.com/6qmBBLpz ]]--
--[[ History at https://gist.github.com/DavidGoldman/f73492ec45a27358dcfcc3aea1b08964 ]]
--[[ Do what you will with the script. ]]--
--[[ This turtle script is smart enough to mine veins of a hardcoded set of ores. ]]--
--[[ Cheers! ]]--
local version = "1.0.6"
-- Logging
local ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF = 1, 2, 3, 4, 5, 6, 7
local LOG_PREFIXES = { "", "[Debug] ", "[Info] ", "[Warn] ", "[Error] ", "[Fatal] ", "" }
local log_f_handle = nil
local logging_level = ALL
local function log(level, msg)
if level < logging_level then
return
end
if log_f_handle == nil then
log_f_handle = fs.open("miner.log", "w")
end
log_f_handle.writeLine(LOG_PREFIXES[level] .. msg)
log_f_handle.flush()
end
local function closeLog()
if log_f_handle ~= nil then
log_f_handle.close()
log_f_handle = nil
end
end
-- Sorted pairs function from https://www.lua.org/pil/19.3.html
local function pairsByKeys(t, f)
local a = {}
for key in pairs(t) do
table.insert(a, key)
end
table.sort(a, f)
local i = 0 -- Iterator variable
local iter = function () -- Iterator function
i = i + 1
if a[i] == nil then
return nil
else
return a[i], t[a[i]]
end
end
return iter
end
-- http://stackoverflow.com/questions/640642/how-do-you-copy-a-lua-table-by-value
local function deepcopy(o, seen)
seen = seen or {}
if o == nil then return nil end
if seen[o] then return seen[o] end
local no
if type(o) == 'table' then
no = {}
seen[o] = no
for k, v in next, o, nil do
no[deepcopy(k, seen)] = deepcopy(v, seen)
end
setmetatable(no, deepcopy(getmetatable(o), seen))
else -- number, string, boolean, etc
no = o
end
return no
end
-- Stack data structure
local function stack_new()
return { _size = 0, _storage = {} }
end
local function stack_getSize(this)
return this._size
end
local function stack_apply(this, fn, context)
local size = this._size
local storage = this._storage
for i=size,1,-1 do
fn(context, storage[i])
end
end
local function stack_reverse_apply(this, fn, context)
local size = this._size
local storage = this._storage
for i=1,size do
fn(context, storage[i])
end
end
local function stack_push(this, val)
this._size = this._size + 1
table.insert(this._storage, val)
end
local function stack_peek(this)
local size = this._size
if size == 0 then
return nil
else
return this._storage[size]
end
end
local function stack_pop(this)
local size = this._size
if size == 0 then
return nil
else
this._size = size - 1
return table.remove(this._storage)
end
end
local function stack_pop_until(this, idx, fn)
local size = this._size
local storage = this._storage
for i=size,idx+1,-1 do
fn(i, table.remove(storage))
end
this._size = idx
return stack_peek(this)
end
local function stack_copy(this)
local size = this._size
local my_storage = this._storage
local copy_storage = {}
for i=1,size do
table.insert(copy_storage, my_storage[i])
end
return { _size = size, _storage = copy_storage }
end
local function stack_reverseCopy(this)
local size = this._size
local my_storage = this._storage
local copy_storage = {}
for i=size,1,-1 do
table.insert(copy_storage, my_storage[i])
end
return { _size = size, _storage = copy_storage }
end
-- End stack
-- DIR = 0 (forward, neg z), 1 (right, pos x), 2 (backward, pos z), 3 (left, neg x)
local FORWARD, RIGHT, BACKWARD, LEFT = 0, 1, 2, 3
local function dir_left(dir)
return (dir - 1) % 4
end
local function dir_right(dir)
return (dir + 1) % 4
end
local function dir_offset(dir, offset_dir)
return (dir + offset_dir) % 4
end
local function dir_forward(dir, x, z)
if dir == FORWARD then
return x, z - 1
elseif dir == RIGHT then
return x + 1, z
elseif dir == BACKWARD then
return x, z + 1
else
return x - 1, z
end
end
local function dir_backward(dir, x, z)
if dir == FORWARD then
return x, z + 1
elseif dir == RIGHT then
return x - 1, z
elseif dir == BACKWARD then
return x, z - 1
else
return x + 1, z
end
end
-- End DIR helpers
-- Coord helpers
local function dir_coords_toString(x, y, z, dir)
return string.format("%d-%d-%d:%d", x, y, z, dir)
end
local function coords_toString(x, y, z)
return string.format("%d-%d-%d", x, y, z)
end
local function coords_getMovementStringsFromPos(x, y, z, dir)
local up = dir_coords_toString(x, y + 1, z, dir)
local down = dir_coords_toString(x, y - 1, z, dir)
local xf, zf = dir_forward(dir, x, z)
local forward = dir_coords_toString(xf, y, zf, dir)
local xb, zb = dir_backward(dir, x, z)
local backward = dir_coords_toString(xb, y, zb, dir)
return forward, backward, up, down
end
local function coords_getMovementStrings(state)
return coords_getMovementStringsFromPos(state.x, state.y, state.z, state.dir)
end
-- End Coord helpers
-- Tracker data structure used to keep track of all movement to go home via a moon walk (backwards).
-- All coordinates are relative to the turtle's starting position (the origin).
local function tracker_new(track_all_states)
local stack = stack_new()
stack_push(stack, { x = 0, y = 0, z = 0, dir = 0, cost = 0, action = "START"})
return { _stack = stack, _movementTable = {}, _track_states = track_all_states, _sx = 0, _sy = 0, _sz = 0, _sdir = 0 }
end
local function tracker_newStartingFromOld(old_tracker)
local stack = stack_new()
local state = stack_peek(old_tracker._stack)
stack_push(stack, { x = state.x, y = state.y, z = state.z, dir = state.dir, cost = 0, action = "START" })
return { _stack = stack, _movementTable = {}, _track_states = true, _sx = state.x, _sy = state.y, _sz = state.z, _sdir = state.dir }
end
local function _addToMovementTable(movementTable, key, value)
movementTable[key] = movementTable[key] or value
end
local function _removeFromMovementTable(movementTable, key, value)
if movementTable[key] == value then
movementTable[key] = nil
end
end
local function tracker_updateForState(this, state)
local stack = this._stack
local movementTable = this._movementTable
-- If we don't track all states, only keep track of the current position (not even the starting position).
if not this._track_states then
stack_pop(stack)
stack_push(stack, state)
return
end
-- Check if this movement can be traced back.
local coord_str = dir_coords_toString(state.x, state.y, state.z, state.dir)
local idx = movementTable[coord_str]
if idx ~= nil and idx ~= stack_getSize(stack) then
local prev_state = stack_pop_until(stack, idx, function(cur_index, cur_state)
-- Remove potential movements
local f, b, u, d = coords_getMovementStrings(cur_state)
_removeFromMovementTable(movementTable, f, cur_index)
_removeFromMovementTable(movementTable, b, cur_index)
_removeFromMovementTable(movementTable, u, cur_index)
_removeFromMovementTable(movementTable, d, cur_index)
end
)
-- Update proper action and cost for this state
state.cost = prev_state.cost + 1
local f, b, u, d = coords_getMovementStrings(prev_state)
if coord_str == f then
state.action = "FORWARD"
elseif coord_str == b then
state.action = "BACKWARD"
elseif coord_str == u then
state.action = "UP"
else
state.action = "DOWN"
end
end
-- Push new state on to stack now that any previous ones have been removed
stack_push(stack, state)
-- Add potential movements from this state to table
local idx = stack_getSize(stack)
f, b, u, d = coords_getMovementStrings(state)
_addToMovementTable(movementTable, f, idx)
_addToMovementTable(movementTable, b, idx)
_addToMovementTable(movementTable, u, idx)
_addToMovementTable(movementTable, d, idx)
end
local function tracker_left(this)
local stack = this._stack
local last_state = stack_peek(stack)
local new_state = {
x = last_state.x,
y = last_state.y,
z = last_state.z,
dir = dir_left(last_state.dir),
cost = last_state.cost,
action = "LEFT"
}
tracker_updateForState(this, new_state)
end
local function tracker_right(this)
local stack = this._stack
local last_state = stack_peek(stack)
local new_state = {
x = last_state.x,
y = last_state.y,
z = last_state.z,
dir = dir_right(last_state.dir),
cost = last_state.cost,
action = "RIGHT"
}
tracker_updateForState(this, new_state)
end
local function tracker_up(this)
local stack = this._stack
local last_state = stack_peek(stack)
local new_state = {
x = last_state.x,
y = last_state.y + 1,
z = last_state.z,
dir = last_state.dir,
cost = last_state.cost + 1,
action = "UP"
}
tracker_updateForState(this, new_state)
end
local function tracker_down(this)
local stack = this._stack
local last_state = stack_peek(stack)
local new_state = {
x = last_state.x,
y = last_state.y - 1,
z = last_state.z,
dir = last_state.dir,
cost = last_state.cost + 1,
action = "DOWN"
}
tracker_updateForState(this, new_state)
end
local function tracker_forward(this)
local stack = this._stack
local last_state = stack_peek(stack)
local new_x, new_z = dir_forward(last_state.dir, last_state.x, last_state.z)
local new_state = {
x = new_x,
y = last_state.y,
z = new_z,
dir = last_state.dir,
cost = last_state.cost + 1,
action = "FORWARD"
}
tracker_updateForState(this, new_state)
end
local function tracker_backward(this)
local stack = this._stack
local last_state = stack_peek(stack)
local new_x, new_z = dir_backward(last_state.dir, last_state.x, last_state.z)
local new_state = {
x = new_x,
y = last_state.y,
z = new_z,
dir = last_state.dir,
cost = last_state.cost + 1,
action = "BACKWARD"
}
tracker_updateForState(this, new_state)
end
-- Rewind by iterating through the stack.
-- WARNING: You can't rewind a tracker while it is active!
local function tracker_rewind(this, fn, context)
log(DEBUG, "Rewinding")
stack_apply(this._stack, fn, context)
log(DEBUG, "Done Rewinding")
end
local function tracker_getState(this)
return stack_peek(this._stack)
end
local function tracker_getStartingPosAndDir(this)
return this._sx, this._sy, this._sz, this._sdir
end
local function tracker_getDir(this)
local state = stack_peek(this._stack)
if state ~= nil then
return state.dir
else
return 0
end
end
local function tracker_getPosAndDir(this)
local state = stack_peek(this._stack)
if state ~= nil then
return state.x, state.y, state.z, state.dir
else
return 0, 0, 0, 0
end
end
-- End tracker
-- Terminal helpers
local function writeString(msg, x, y)
term.setCursorPos(x, y)
write(msg)
end
local function writeCenteredString(msg, y)
local width = term.getSize()
writeString(msg, math.ceil((width - string.len(msg) + 1) / 2), y)
end
local function writePaddedString(msg, x, y, padded_length)
term.setCursorPos(x, y)
if padded_length <= string.len(msg) then
write(msg)
else
write(msg .. string.rep(" ", padded_length - string.len(msg)))
end
end
local function resetScreen()
term.clear()
writeCenteredString("Strip Miner", 1)
writeCenteredString("For the automation extraordinaire", 2)
writeString("By Davidee, version " .. version, 1, 13)
term.setCursorPos(1, 4)
end
local function checkedInputNumber(x, y, min, max)
local input = min
repeat
writePaddedString(" ", x, y, (term.getSize() - x))
term.setCursorPos(x, y)
input = tonumber(read())
until (input ~= nil and input >= min and input <= max)
return input
end
local function checkedInputTwoEnum(x, y, value_a, value_b)
local short_a = string.lower(string.sub(value_a, 1, 1))
local short_b = string.lower(string.sub(value_b, 1, 1))
local suggestion = value_a .. " (" .. short_a .. ") / " .. value_b .. " (" .. short_b .. "): "
local input = short_a
repeat
writePaddedString(suggestion, x, y, term.getSize())
term.setCursorPos(string.len(suggestion) + 1, y) -- TODO: Verify
input = read()
if input == "" then
input = "NO" -- sadly there is no 'continue' in Lua, so we do this instead
else
input = string.lower(string.sub(input, 1, 1))
end
until (input == short_a or input == short_b)
return input == short_a
end
local function clearLines(y_min, y_max)
for l=y_min, y_max do
writePaddedString("", 1, l, term.getSize())
end
end
local function pause(pause_handler, msg)
if pause_handler then
pause_handler(msg)
else
print(msg)
os.pullEvent("key")
end
end
-- Prefer an over-estimation here.
local function estimateFuelCostToMineBlocks(num_blocks)
return 2 * num_blocks
end
-- We can have 3 different trackers at once:
-- * Primary tracker (we're mining in the strip mine)
-- * Return to position tracker (only possible when to_origin = false)
-- * Offroad tracker (e.g. we got caught up mining some coal)
local function estimateReturnFuelCost(state, to_origin)
local offroad_estimate = 0
if state.offroad_tracker ~= nil then
local offroad_state = tracker_getState(state.offroad_tracker)
if offroad_state ~= nil then
offroad_estimate = offroad_state.cost
end
end
-- Going from a place to the origin. Ignore return to position tracker.
if to_origin then
local px, py, pz = tracker_getPosAndDir(state.primary_tracker)
local primary_estimate = math.abs(px) + math.abs(py) + math.abs(pz) + 20 -- "Estimate"
return primary_estimate + offroad_estimate
end
-- Going back to a previous position. Use the cur_tracker (return to position).
local sx, sy, sz = tracker_getStartingPosAndDir(state.cur_tracker)
local x, y, z = tracker_getPosAndDir(state.cur_tracker)
local return_estimate = math.abs(x - sx) + math.abs(y - sy) + math.abs(z - sz) + 20 -- "Estimate"
return return_estimate + offroad_estimate
end
-- Forward declared
local decrementFuel
local MOVEMENT_TRIES = 20
-- Functions below use a different type of 'state' than above. Stateful props.
local function turnLeft(state)
if turtle.turnLeft() then
tracker_left(state.cur_tracker)
end
end
local function turnRight(state)
if turtle.turnRight() then
tracker_right(state.cur_tracker)
end
end
local function turn(state, right)
if right then
turnRight(state)
else
turnLeft(state)
end
end
local function safeDigUp()
local dug = false
while turtle.detectUp() do
if not turtle.digUp() then
break -- Digging up failed for some reason. What gives? Did a player break the block?
else
dug = true
end
sleep(0.45) -- Wait for gravel to fall
end
return dug
end
local function moveUp(state)
for i=1, MOVEMENT_TRIES do
if turtle.up() then
tracker_up(state.cur_tracker)
decrementFuel(state)
break
end
if turtle.detectUp() then
safeDigUp()
else -- Must be a mob.
turtle.attackUp()
sleep(0.1) -- Mobs are invincible for a short time.
end
end
end
local function moveDown(state)
for i=1, MOVEMENT_TRIES do
if turtle.down() then
tracker_down(state.cur_tracker)
decrementFuel(state)
break
end
if turtle.detectDown() then
turtle.digDown()
else -- Must be a mob.
turtle.attackDown()
sleep(0.1) -- Mobs are invincible for a short time.
end
end
end
local function moveForward(state)
for i=1, MOVEMENT_TRIES do
if turtle.forward() then
tracker_forward(state.cur_tracker)
decrementFuel(state)
break
end
if turtle.detect() then
turtle.dig()
else -- Must be a mob.
turtle.attack()
sleep(0.1) -- Mobs are invincible for a short time.
end
end
end
local function moveBackward(state)
if turtle.back() then -- Moon walk!
tracker_backward(state.cur_tracker)
decrementFuel(state)
else -- There might be something behind us. Rotate to fix it.
turnRight(state)
turnRight(state)
moveForward(state) -- This will try to break the block.
turnRight(state)
turnRight(state)
end
end
local function rotate(state, cur_dir, desired_dir)
if cur_dir == desired_dir then
return
end
if dir_right(cur_dir) == desired_dir then
turnRight(state)
elseif dir_left(cur_dir) == desired_dir then
turnLeft(state)
else
turnRight(state)
turnRight(state)
end
end
local function moveY(state, from, to, dir)
local diff = to - from
if diff >= 0 then
for i = 1, diff do
moveUp(state)
end
else
for i = 1, math.abs(diff) do
moveDown(state)
end
end
return dir
end
local function moveX(state, from, to, cur_dir, opt_dig_up_and_down)
local diff = to - from
local new_dir = cur_dir
if diff > 0 then -- dir = 1 (right, pos x)
new_dir = RIGHT
elseif diff < 0 then -- dir = 3 (left, neg x)
new_dir = LEFT
end
diff = math.abs(diff)
rotate(state, cur_dir, new_dir)
for i = 1, diff do
moveForward(state)
if opt_dig_up_and_down then
safeDigUp()
turtle.digDown()
end
end
return new_dir
end
local function moveZ(state, from, to, cur_dir, opt_dig_up_and_down)
local diff = to - from
local new_dir = cur_dir
if diff > 0 then -- dir = 2 (backward, pos z)
new_dir = BACKWARD
elseif diff < 0 then -- dir = 0 (forward, neg z)
new_dir = FORWARD
end
diff = math.abs(diff)
rotate(state, cur_dir, new_dir)
for i = 1, diff do
moveForward(state)
if opt_dig_up_and_down then
safeDigUp()
turtle.digDown()
end
end
return new_dir
end
local function makeSet(t)
local s = {}
for _, v in pairs(t) do
s[v] = true
end
return s
end
local ORE_ID_SET = makeSet({
-- Vanilla ores
"minecraft:gold_ore", "minecraft:iron_ore", "minecraft:coal_ore", "minecraft:lapis_ore",
"minecraft:diamond_ore", "minecraft:redstone_ore", "minecraft:emerald_ore",
-- IC2 ores
"IC2:blockOreCopper", "IC2:blockOreTin", "IC2:blockOreUran", "IC2:blockOreLead",
"ic2:resource",
-- Applied Energistics 2 ores
"appliedenergistics2:tile.OreQuartz", "appliedenergistics2:tile.OreQuartzCharged",
"appliedenergistics2:quartz_ore", "appliedenergistics2:charged_quartz_ore",
-- Forestry
"Forestry:resources",
"forestry:resources",
-- GrowthCraft
"growthcraft:salt_ore",
-- BigReactors
"BigReactors:YelloriteOre",
"bigreactors:oreyellorite",
-- Railcraft
"Railcraft:ore",
"railcraft:ore",
-- ThermalFoundation
"ThermalFoundation:Ore",
"thermalfoundation:ore",
-- BluePower
"bluepower:amethyst_ore",
-- ProjectRed Exploration
"ProjRed|Exploration:projectred.exploration.ore",
"projectred-exploration:ore",
-- Biomes O' Plenty
"biomesoplenty:gem_ore"
})
local ACTION_TABLE = { LEFT = turnLeft, RIGHT = turnRight, UP = moveUp, DOWN = moveDown, FORWARD = moveForward, BACKWARD = moveBackward }
local REV_ACTION_TABLE = { LEFT = turnRight, RIGHT = turnLeft, UP = moveDown, DOWN = moveUp, FORWARD = moveBackward, BACKWARD = moveForward }
local function doAction(state, action)
if ACTION_TABLE[action] then
ACTION_TABLE[action](state)
return true
end
return false
end
local function doReverseAction(state, action)
if REV_ACTION_TABLE[action] then
REV_ACTION_TABLE[action](state)
return true
end
return false
end
local function doStateAction(state, tracker_state)
local times = tracker_state.distance or 1
for i=1,times do
doAction(state, tracker_state.action)
end
end
local function doReverseStateAction(state, tracker_state)
local times = tracker_state.distance or 1
for i=1,times do
doReverseAction(state, tracker_state.action)
end
end
decrementFuel = function(state)
if state.fuel ~= "unlimited" then
state.fuel = state.fuel - 1
end
end
local function isChestPresent(side)
local peri = peripheral.wrap(side)
if peri == nil then
return false
end
-- Support for Plethora Peripherals, verify it's a chest from the size.
if peri.list ~= nil and peri.size ~= nil then
return peri.size() > 10
end
-- Legacy support for Open Peripherals.
if peri.listSources ~= nil then
local sources = peri.listSources()
return not not sources.inventory and not not sources["inventory-world"]
end
end
local function moveItemIntoChest(chest, dir_from_chest, slot, opt_amount)
-- Support for Plethora Peripherals.
if chest.pullItems ~= nil then
return chest.pullItems(dir_from_chest, slot, opt_amount)
end
-- Legacy support for Open Peripherals.
return chest.pullItem(dir_from_chest, slot, opt_amount)
end
-- Legacy support for Open Peripherals: check if we can actually move into the chest.
local function testChestMove(chest, dir_from_chest)
local status = pcall(moveItemIntoChest, chest, dir_from_chest, 0)
return status
end
local function detectChestDirection(side, optional_dir_from_chest)
if not isChestPresent(side) then
return nil
end
local chest = peripheral.wrap(side)
-- Support for Plethora Peripherals, check the transfer locations.
if chest.getTransferLocations ~= nil then
local locations = chest.getTransferLocations()
local found_loc = nil
for _, dir in pairs(locations) do
if optional_dir_from_chest == dir then
return dir
end
-- Ignore "self" see it refers to the chest itself, not the turtle.
if found_loc == nil and dir ~= "self" then
found_loc = dir
end
end
return found_loc
end
-- Legacy support for Open Peripherals.
if optional_dir_from_chest ~= nil and testChestMove(chest, optional_dir_from_chest) then
return optional_dir_from_chest
end
local DIRECTIONS = { "NORTH", "SOUTH", "WEST", "EAST", "DOWN", "UP" }
for _, dir in ipairs(DIRECTIONS) do
if testChestMove(chest, dir) then
return dir
end
end
return nil
end
-- Legacy: unavailable with Plethora Peripherals. OK because this is just an optimization.
-- Returns true if chest.condenseItems() worked, potentially trying multiple times.
--
-- For some reason, it has been failing in dropItemsIntoChest sometimes with error
-- "Java Exception Thrown: java.lang.RuntimeException: You are not attached to this Computer"
local function safeChestCondense(chest)
if chest.condenseItems == nil then
return true
end
log(DEBUG, "Condensing items in chest")
local success, result_or_err = pcall(chest.condenseItems)
if not success then
log(ERROR, "safeChestCondense: " .. result_or_err)
-- Try again 5 times.
for i=1, 5 do
sleep(1)
success, result_or_err = pcall(chest.condenseItems)
if success then
break
else
log(ERROR, string.format("safeChestCondense: Retry #%d failed", i))
end
end
end
if success then
log(DEBUG, "Chest condensed")
end
return success
end
-- Returns true iff all items were transferred over.
local function dropItemsIntoChest(chest, dir_from_chest, starting_slot, items_to_keep)
-- Legacy: condense the chest if supported.
safeChestCondense(chest)
items_to_keep = deepcopy(items_to_keep)
for s=starting_slot, 16 do
local item_detail = turtle.getItemDetail(s)
if item_detail ~= nil then
local item_count = item_detail.count
local deposit_count = item_count
local item_id = item_detail.name
local to_keep_info = items_to_keep[item_id]
if to_keep_info and (not to_keep_info.damage or to_keep_info.damage == item_detail.damage) then
local keep_count = to_keep_info.count
if item_count >= keep_count then
deposit_count = item_count - keep_count
items_to_keep[item_id] = nil
else
deposit_count = 0
to_keep_info.count = keep_count - item_count
end
end
if deposit_count > 0 then
local success, result_or_err = pcall(moveItemIntoChest, chest, dir_from_chest, s, deposit_count)
if not success then
log(ERROR, string.format("dropItemsIntoChest: error %s", result_or_err))
return false
end
if result_or_err < deposit_count then
log(ERROR, string.format("dropItemsIntoChest: only deposited %d / %d", result_or_err, deposit_count))
return false
end
end
end
end
return true
end
local function fetchStackFromChest(chest, dir_from_chest, chest_slot, amount, turtle_slot)
-- Support for Plethora Peripherals.
if chest.pushItems ~= nil then
return chest.pushItems(dir_from_chest, chest_slot, amount, turtle_slot)
end
-- Legacy support for Open Peripherals.
return chest.pushItem(dir_from_chest, chest_slot, amount, turtle_slot)
end
-- Returns an iterator for the items in the chest.
local function chestIterator(chest)
-- Support for Plethora Peripherals.
if chest.list ~= nil then
local list = chest.list()
local list_iter = pairsByKeys(list)
local iter = function () -- Iterator function
local slot, partial_info = list_iter()
if slot == nil then
return nil
end
local item_info = chest.getItemMeta(slot)
return slot, item_info
end
return iter
end
-- Legacy support for Open Peripherals.
local stacks = chest.getAllStacks()
local stacks_iter = pairsByKeys(stacks)
local iter = function () -- Iterator function
local slot, proxy = stacks_iter()
if slot == nil then
return nil
end
local item_info = proxy.select()
local converted_info = { count = item_info.qty, damage = item_info.dmg, name = item_info.id, maxCount = item_info.max_size }
return slot, converted_info
end
return iter
end
-- Returns how many of the item we now have in the specified slot.
local function fetchItemFromChestIntoSlot(chest, dir_from_chest, item_id, turtle_slot, opt_amount)
local slot_item_detail = turtle.getItemDetail(turtle_slot)
local slot_qty = 0
local slot_dmg = -1
if slot_item_detail ~= nil then
if slot_item_detail.name ~= item_id then
-- TODO: Decide what to do!
else
slot_qty = slot_item_detail.count
slot_dmg = slot_item_detail.damage
end
end
if slot_qty == opt_amount then
return opt_amount
end
log(DEBUG, string.format("Fetching some %s", item_id))
-- Legacy: condense the chest if supported. Condensing should make it easier to fetch full stacks.
safeChestCondense(chest)
for slot, item_info in chestIterator(chest) do
-- Make sure item IDs and dmg match
if item_info.name == item_id and (slot_dmg == -1 or item_info.damage == slot_dmg) then
opt_amount = opt_amount or item_info.maxCount
local count = item_info.count
local countNeeded = opt_amount - slot_qty -- Figure out how much we need.
if countNeeded > 0 then
local takeCount = math.min(countNeeded, count) -- Can't take more than is there.
local receivedCount = fetchStackFromChest(chest, dir_from_chest, slot, takeCount, turtle_slot)
slot_qty = slot_qty + receivedCount
-- If we have enough now, immediately return, otherwise we need to keep iterating.
if slot_qty == opt_amount then
return slot_qty
end
else
break
end
end
end
return slot_qty
end
-- Returns -1 or an empty turtle slot.
local function getEmptySlot()
for s=1, 16 do
if turtle.getItemCount(s) == 0 then
return s
end
end
return -1
end
-- Reset turtle item selection to slot 1 so mined blocks will be stacked more efficiently.
local function resetItemSelection()
turtle.select(1)
end
-- Returns true if the given item is now selected.
local function selectItem(item_id)
local cur_item_detail = turtle.getItemDetail()
if cur_item_detail ~= nil and cur_item_detail.name == item_id then
return true
end
for s=1, 16 do
local slot_item_detail = turtle.getItemDetail(s)
if slot_item_detail ~= nil and slot_item_detail.name == item_id then
turtle.select(s)
return true
end
end
return false
end
-- Returns slot (or -1), quantity
local function fetchItemFromChest(chest, dir_from_chest, item_id, opt_amount)
local empty_slot = getEmptySlot()
if empty_slot ~= -1 then
local qty = fetchItemFromChestIntoSlot(chest, dir_from_chest, item_id, empty_slot, opt_amount)
if qty > 0 then
return empty_slot, qty
end
end
return -1, 0
end
local MAX_TORCH_SLOTS = 1
-- Returns the torch slots.
local function takeTorches(state, chest)
local torch_slots = { }
for i=1, MAX_TORCH_SLOTS do
local slot, qty = fetchItemFromChest(chest, state.dir_from_chest, "minecraft:torch")
if slot ~= -1 then
table.insert(torch_slots, slot)
else
break
end
end
return torch_slots
end
local function refuelFromChest(state, chest, fuel_wanted)
if state.fuel >= fuel_wanted then
return
end
while state.fuel < fuel_wanted do
local num_coal_needed = math.ceil((fuel_wanted - state.fuel) / 80)
local slot, qty = fetchItemFromChest(chest, state.dir_from_chest, "minecraft:coal", num_coal_needed)
if slot ~= -1 then
turtle.select(slot)
turtle.refuel()
resetItemSelection()
state.fuel = turtle.getFuelLevel()
num_coal_needed = math.ceil((fuel_wanted - state.fuel) / 80)
else
break -- There's no more coal!
end
end
end
-- Returns how much more fuel it wants (0 if success)
local function refuel(state, chest)
if state.fuel == "unlimited" then
return 0
end
-- TODO: Improve this, this estimation is coarse and over-estimates.
local return_estimate = estimateReturnFuelCost(state, false)
local blocks_for_full_inv = 1024 -- 64 * 16
local full_inv_estimate = estimateFuelCostToMineBlocks(blocks_for_full_inv)
local blocks_to_travel_a_tunnel = 2 * state.tunnel_length + state.tunnel_gap + 2 -- Estimate
local blocks_for_remaining_tunnels = (state.num_tunnels - state.cur_tunnel + 1) * blocks_to_travel_a_tunnel
local remaining_blocks_estimate = estimateFuelCostToMineBlocks(blocks_for_remaining_tunnels)
local estimate = math.min(full_inv_estimate, remaining_blocks_estimate)
estimate = 2 * (estimate + return_estimate) -- Double it for no good reason!
refuelFromChest(state, chest, estimate)
return math.max(0, estimate - state.fuel)
end
local function interactWithChest(state, side, is_done)
local chest = peripheral.wrap(side)
-- Empty our inventory, but try to keep some cobblestone if we're not done.
-- This is so we can patch things up when we mine ores.
local items_to_keep = {}
if not is_done then
items_to_keep["minecraft:cobblestone"] = { count = 32 }
end
log(DEBUG, "Dropping items into chest...")
local empty_inv = dropItemsIntoChest(chest, state.dir_from_chest, 1, items_to_keep)
while not empty_inv do
pause(state.pause_handler, "Chest is full!")
empty_inv = dropItemsIntoChest(chest, state.dir_from_chest, 1, items_to_keep)
end
if is_done then
return
end
-- Refuel if needed.
log(DEBUG, "Checking refuel...")
local fuel_needed = refuel(state, chest)
while fuel_needed ~= 0 do
pause(state.pause_handler, string.format("Need %d more fuel!", fuel_needed))
fuel_needed = refuel(state, chest)
end
-- Get torches.
log(DEBUG, "Taking torches...")
local torch_slots = takeTorches(state, chest)
while table.getn(torch_slots) == 0 do
pause(state.pause_handler, "Chest is out of torches!")
torch_slots = takeTorches(state, chest)
end
state.torch_slots = torch_slots
-- Try to get some cobblestone to use for filling in holes left by mining ores if we don't have
-- some already.
if not is_done and not selectItem("minecraft:cobblestone") then
fetchItemFromChest(chest, state.dir_from_chest, "minecraft:cobblestone", 32)
end
resetItemSelection()
end
local function isOutOfTorches(state)
return table.getn(state.torch_slots) == 0
end
local function needsFuel(state)
return state.fuel ~= "unlimited" and estimateReturnFuelCost(state, true) > state.fuel
end
local function isInventoryFull(starting_slot)
for s=starting_slot, 16 do
if turtle.getItemCount(s) == 0 then
return false
end
end
return true
end
local function manageInventory(state)
if isInventoryFull(1) or needsFuel(state) or isOutOfTorches(state) then
local old_tracker = state.cur_tracker
local return_tracker = tracker_newStartingFromOld(old_tracker)
state.cur_tracker = return_tracker
-- If the tracker was an offroad tracker, we need to rewind it first.
if state.offroad_tracker ~= nil then
tracker_rewind(state.offroad_tracker, doReverseStateAction, state)
end
-- Figure out how to go back.
local x, y, z, dir = tracker_getPosAndDir(state.cur_tracker)
dir = moveY(state, y, 1, dir)
dir = moveX(state, x, 0, dir)
dir = moveZ(state, z, 0, dir)
moveDown(state) -- As we start at 0 but above moved to 1
rotate(state, dir, BACKWARD) -- dir = 2 (backward, pos z)
-- Use the chest.
interactWithChest(state, "front", false)
-- Need a new tracker as we can't rewind while active.
local temp_tracker = tracker_newStartingFromOld(return_tracker)
state.cur_tracker = temp_tracker
tracker_rewind(return_tracker, doReverseStateAction, state)
state.cur_tracker = old_tracker
end
end
local function digMoveForwardDigVertical(state)
turtle.dig()
moveForward(state)
safeDigUp()
turtle.digDown()
manageInventory(state)
end
local function shouldPlaceTorch(blocks_from_torch, block, tunnel_length)
if blocks_from_torch >= 8 then
return true
end
local num_blocks_left = tunnel_length - block
-- If we place it with no blocks left, it'll be broken when he digs the edges.
return num_blocks_left == 2 and blocks_from_torch >= 4
end
local function placeTorch(state)
local torch_slots = state.torch_slots
local cur_slot = torch_slots[table.getn(torch_slots)]
local qty = turtle.getItemCount(cur_slot)
local right_tunnels = state.right_tunnels
local _, y = tracker_getPosAndDir(state.cur_tracker)
-- The side that we place it depends on whether we're doing tunnels on left or right.
turn(state, right_tunnels)
moveY(state, y, 0)
turtle.select(cur_slot)
local result = turtle.place()
if result and qty == 1 then
table.remove(torch_slots)
end
resetItemSelection()
moveY(state, 0, y)
turn(state, not right_tunnels)
return result
end
-- Forward declared
local checkBlockForward
local checkBlockDir
local function getUpCoordString(state)
local x, y, z = tracker_getPosAndDir(state.cur_tracker)
return coords_toString(x, y + 1, z)
end
local function getDownCoordString(state)
local x, y, z = tracker_getPosAndDir(state.cur_tracker)
return coords_toString(x, y - 1, z)
end
local function getDirCoordString(state, offset_dir)
local x, y, z, dir = tracker_getPosAndDir(state.cur_tracker)
dir = dir_offset(dir, offset_dir)
x, z = dir_forward(dir, x, z)
return coords_toString(x, y, z)
end
local function createOffroadTracker(state)
if state.offroad_tracker == nil then
state.offroad_tracker = tracker_newStartingFromOld(state.cur_tracker)
state.cur_tracker = state.offroad_tracker
return true
end
return false
end
local function possiblyDestroyOffroadTracker(state, destroy)
if destroy then
state.offroad_tracker = nil
state.cur_tracker = state.primary_tracker
end
end
-- TODO: Try to make this non-recursive.
local function checkBlockUp(state)
local succ, info = turtle.inspectUp()
if succ and ORE_ID_SET[info.name] then
if not safeDigUp() then
return
end
local created_tracker = createOffroadTracker(state)
moveUp(state)
manageInventory(state)
local checked_table = state.checked_table
local start_dir = tracker_getDir(state.cur_tracker)
local u, f, r = getUpCoordString(state), getDirCoordString(state, FORWARD), getDirCoordString(state, RIGHT)
local b, l = getDirCoordString(state, BACKWARD), getDirCoordString(state, LEFT)
-- Check em.
local uc, fc, rc = checked_table[u], checked_table[f], checked_table[r]
local bc, lc = checked_table[b], checked_table[l]
-- Mark them all checked.
local all_strings = { u, f, r, b, l }
for _, v in ipairs(all_strings) do
checked_table[v] = true
end
if not uc then
checkBlockUp(state)
end
if not fc then
checkBlockForward(state)
end
if not rc then
checkBlockDir(state, dir_offset(start_dir, RIGHT))
end
if not bc then
checkBlockDir(state, dir_offset(start_dir, BACKWARD))
end
if not lc then
checkBlockDir(state, dir_offset(start_dir, LEFT))
end
rotate(state, tracker_getDir(state.cur_tracker), start_dir)
moveDown(state)
possiblyDestroyOffroadTracker(state, created_tracker)
if selectItem("minecraft:cobblestone") then
turtle.placeUp()
resetItemSelection()
end
end
end
local function checkBlockDown(state)
local succ, info = turtle.inspectDown()
if succ and ORE_ID_SET[info.name] then
if not turtle.digDown() then
return
end
local created_tracker = createOffroadTracker(state)
moveDown(state)
manageInventory(state)
local checked_table = state.checked_table
local start_dir = tracker_getDir(state.cur_tracker)
local d, f, r = getDownCoordString(state), getDirCoordString(state, FORWARD), getDirCoordString(state, RIGHT)
local b, l = getDirCoordString(state, BACKWARD), getDirCoordString(state, LEFT)
-- Check em.
local dc, fc, rc = checked_table[d], checked_table[f], checked_table[r]
local bc, lc = checked_table[b], checked_table[l]
-- Mark them all checked.
local all_strings = { d, f, r, b, l }
for _, v in ipairs(all_strings) do
checked_table[v] = true
end
if not dc then
checkBlockDown(state)
end
if not fc then
checkBlockForward(state)
end
if not rc then
checkBlockDir(state, dir_offset(start_dir, RIGHT))
end
if not bc then
checkBlockDir(state, dir_offset(start_dir, BACKWARD))
end
if not lc then
checkBlockDir(state, dir_offset(start_dir, LEFT))
end
rotate(state, tracker_getDir(state.cur_tracker), start_dir)
moveUp(state)
possiblyDestroyOffroadTracker(state, created_tracker)
if selectItem("minecraft:cobblestone") then
turtle.placeDown()
resetItemSelection()
end
end
end
checkBlockForward = function(state)
local succ, info = turtle.inspect()
if succ and ORE_ID_SET[info.name] then
if not turtle.dig() then
return
end
local created_tracker = createOffroadTracker(state)
moveForward(state)
manageInventory(state)
local checked_table = state.checked_table
local start_dir = tracker_getDir(state.cur_tracker)
local u, f, r = getUpCoordString(state), getDirCoordString(state, FORWARD), getDirCoordString(state, RIGHT)
local d, l = getDownCoordString(state), getDirCoordString(state, LEFT)
-- Check em.
local uc, fc, rc = checked_table[u], checked_table[f], checked_table[r]
local dc, lc = checked_table[d], checked_table[l]
-- Mark them all checked.
local all_strings = { u, f, r, d, l }
for _, v in ipairs(all_strings) do
checked_table[v] = true
end
if not uc then
checkBlockUp(state)
end
if not dc then
checkBlockDown(state)
end
if not fc then
checkBlockForward(state)
end
if not rc then
checkBlockDir(state, dir_offset(start_dir, RIGHT))
end
if not lc then
checkBlockDir(state, dir_offset(start_dir, LEFT))
end
rotate(state, tracker_getDir(state.cur_tracker), start_dir)
moveBackward(state)
possiblyDestroyOffroadTracker(state, created_tracker)
if selectItem("minecraft:cobblestone") then
turtle.place()
resetItemSelection()
end
end
end
checkBlockDir = function(state, dir)
rotate(state, tracker_getDir(state.cur_tracker), dir)
checkBlockForward(state)
end
local function maybeCheckBlockForward(state)
local forward_str = getDirCoordString(state, FORWARD)
local checked_table = state.checked_table
if not checked_table[forward_str] then
checked_table[forward_str] = true
checkBlockForward(state)
end
end
local function maybeCheckBlockDown(state)
local down_str = getDownCoordString(state)
local checked_table = state.checked_table
if not checked_table[down_str] then
checked_table[down_str] = true
checkBlockDown(state)
end
end
local function maybeCheckBlockUp(state)
local up_str = getUpCoordString(state)
local checked_table = state.checked_table
if not checked_table[up_str] then
checked_table[up_str] = true
checkBlockUp(state)
end
end
local function maybeCheckBlockForwardAndDown(state)
maybeCheckBlockForward(state)
maybeCheckBlockDown(state)
end
local function maybeCheckBlockForwardAndUp(state)
maybeCheckBlockForward(state)
maybeCheckBlockUp(state)
end
-- Mark next 6 tunnel blocks checked before we get to them.
local function markNext6BlocksChecked(state, right)
local checked_table = state.checked_table
local x, y, z, dir = tracker_getPosAndDir(state.cur_tracker)
-- Forward 3 blocks
x, z = dir_forward(dir, x, z)
for i=0, 2 do
checked_table[coords_toString(x, y + i, z)] = true
end
-- Forward-(right) 3 blocks
local offset_dir = right and RIGHT or LEFT
x, z = dir_forward(dir_offset(dir, offset_dir), x, z)
for i=0, 2 do
checked_table[coords_toString(x, y + i, z)] = true
end
end
local function mineTunnelAndOres(state)
local tunnel_length = state.tunnel_length
local right_tunnels = state.right_tunnels
local blocks_from_torch = -1
state.checked_table = {}
for length=1, tunnel_length do
-- Right tunnels turn right initially, while left tunnels turn left initially
local is_odd_length = (length % 2 == 1)
local goes_l_to_r = (is_odd_length == right_tunnels)
markNext6BlocksChecked(state, goes_l_to_r)
moveForward(state)
turn(state, not goes_l_to_r)
maybeCheckBlockForwardAndDown(state)
moveUp(state)
maybeCheckBlockForward(state)
moveUp(state)
maybeCheckBlockForwardAndUp(state)
manageInventory(state)
-- Now mine the other side
turn(state, goes_l_to_r)
turn(state, goes_l_to_r)
moveForward(state)
maybeCheckBlockForwardAndUp(state)
moveDown(state)
maybeCheckBlockForward(state)
moveDown(state)
maybeCheckBlockForwardAndDown(state)
turn(state, not goes_l_to_r)
manageInventory(state)
-- Handles placing torches (only on even lengths)
if not is_odd_length then
if blocks_from_torch == -1 or shouldPlaceTorch(blocks_from_torch + 2, length, tunnel_length) then
if placeTorch(state) then
blocks_from_torch = 0
end
else
blocks_from_torch = blocks_from_torch + 2
end
end
end
end
local function mineAndCheckVertical(state, should_go_up)
if should_go_up then
maybeCheckBlockForwardAndDown(state)
moveUp(state)
maybeCheckBlockForward(state)
moveUp(state)
maybeCheckBlockForwardAndUp(state)
else
maybeCheckBlockForwardAndUp(state)
moveDown(state)
maybeCheckBlockForward(state)
moveDown(state)
maybeCheckBlockForwardAndDown(state)
end
end
local function markCurrent3BlocksChecked(state, should_go_up)
local checked_table = state.checked_table
local x, y, z, dir = tracker_getPosAndDir(state.cur_tracker)
-- Up/Down 3 blocks
for i=0, 2 do
local offset = i
if not should_go_up then
offset = 0 - offset
end
checked_table[coords_toString(x, y + offset, z)] = true
end
end
local function mineTunnelConnectorAndOres(state)
local tunnel_gap = state.tunnel_gap
local right_tunnels = state.right_tunnels
if state.cur_tunnel % 2 == 1 then -- Odd tunnel num --> turn (tunnel dir), forward, dig, start in left
for i=1, tunnel_gap + 3 do
markCurrent3BlocksChecked(state, i % 2 == 1)
mineAndCheckVertical(state, i % 2 == 1)
turn(state, right_tunnels)
moveForward(state)
turn(state, not right_tunnels)
end
mineAndCheckVertical(state, (tunnel_gap + 4) % 2 == 1)
turn(state, right_tunnels)
mineAndCheckVertical(state, (tunnel_gap + 5) % 2 == 1)
turn(state, right_tunnels)
else -- Even tunnel num --> turn (opp tunnel dir), dig, go backwards to start in left
turn(state, not right_tunnels)
moveBackward(state)
turn(state, right_tunnels)
for i=1, tunnel_gap + 3 do
markCurrent3BlocksChecked(state, i % 2 == 1)
mineAndCheckVertical(state, i % 2 == 1)
turn(state, not right_tunnels)
moveForward(state)
turn(state, right_tunnels)
end
mineAndCheckVertical(state, (tunnel_gap + 4) % 2 == 1)
turn(state, not right_tunnels)
mineAndCheckVertical(state, (tunnel_gap + 5) % 2 == 1)
-- Move back to left.
moveBackward(state)
turn(state, not right_tunnels)
end
-- Move back down.
local _, y = tracker_getPosAndDir(state.cur_tracker)
moveY(state, y, 0)
end
local function mineTunnelZigZag(state)
local tunnel_length = state.tunnel_length
local right_tunnels = state.right_tunnels
local blocks_from_torch = -1
-- Move up so we can mine up and down.
local _, y = tracker_getPosAndDir(state.cur_tracker)
moveY(state, y, 1)
for length=1, tunnel_length do
digMoveForwardDigVertical(state)
-- Right tunnels turn right initially, while left tunnels turn left initially.
local is_odd_length = (length % 2 == 1)
if is_odd_length == right_tunnels then
turnRight(state)
digMoveForwardDigVertical(state)
turnLeft(state)
else
turnLeft(state)
digMoveForwardDigVertical(state)
turnRight(state)
end
-- Handles placing torches (only on even lengths)
if not is_odd_length then
if blocks_from_torch == -1 or shouldPlaceTorch(blocks_from_torch + 2, length, tunnel_length) then
if placeTorch(state) then
blocks_from_torch = 0
end
else
blocks_from_torch = blocks_from_torch + 2
end
end
end
end
local function mineTunnelConnector(state)
local tunnel_gap = state.tunnel_gap
local right_tunnels = state.right_tunnels
local _, y = tracker_getPosAndDir(state.cur_tracker)
moveY(state, y, 1)
if state.cur_tunnel % 2 == 1 then -- Odd tunnel num --> turn (tunnel dir), forward, dig, start in left
turn(state, right_tunnels)
moveForward(state)
for i=1, tunnel_gap + 2 do
digMoveForwardDigVertical(state)
end
turn(state, right_tunnels)
else -- Even tunnel num --> turn (opp tunnel dir), dig, go backwards to start in left
turn(state, not right_tunnels)
for i=1, tunnel_gap + 2 do
digMoveForwardDigVertical(state)
end
moveBackward(state)
turn(state, not right_tunnels)
end
moveY(state, 1, y)
end
function stripMine(num_tunnels, tunnel_length, tunnel_gap, right_tunnels, opt_pause_handler, opt_dir_from_chest_to_turtle)
-- Verify the dir
local dir_from_chest_to_turtle = detectChestDirection("back", opt_dir_from_chest_to_turtle)
if dir_from_chest_to_turtle == nil then
return false
end
log(DEBUG, "Detected chest direction: " .. dir_from_chest_to_turtle)
-- Tunnel length must be even so we can end at the same side as we start
if tunnel_length % 2 == 1 then
tunnel_length = tunnel_length + 1
end
-- The primary tracker only tracks the current position.
local tracker = tracker_new(false)
local state = {
num_tunnels = num_tunnels,
tunnel_length = tunnel_length,
tunnel_gap = tunnel_gap,
right_tunnels = right_tunnels,
cur_tunnel = 1,
pause_handler = opt_pause_handler,
fuel = turtle.getFuelLevel(),
cur_tracker = tracker,
primary_tracker = tracker,
dir_from_chest = dir_from_chest_to_turtle,
torch_slots = { }
}
-- Use the chest, but face it first so it's obvious what's going on
rotate(state, FORWARD, BACKWARD)
interactWithChest(state, "front", false)
rotate(state, BACKWARD, FORWARD)
-- Dig tunnels
for tunnel=1, num_tunnels do
-- Set state
state.cur_tunnel = tunnel
mineTunnelAndOres(state)
if tunnel ~= num_tunnels then
mineTunnelConnectorAndOres(state)
end
end
-- Go back to start.
local x, y, z, dir = tracker_getPosAndDir(state.cur_tracker)
dir = moveY(state, y, 1, dir)
dir = moveZ(state, z, 0, dir)
dir = moveX(state, x, 0, dir, true)
-- As we start at 0 but above moved to 1
moveDown(state)
-- Use the chest, but face it first so it's obvious what's going on
rotate(state, dir, BACKWARD)
interactWithChest(state, "front", true)
rotate(state, BACKWARD, FORWARD)
return true
end
local function pauseUI(msg)
writeCenteredString("ATTENTION", 4)
writeCenteredString(msg, 7)
writeCenteredString("Press any key to try again...", 10)
os.pullEvent("key")
resetScreen()
end
function stripMineWithUI()
resetScreen()
writeString("Number of tunnels: ", 1, 4)
local num_tunnels = checkedInputNumber(35, 4, 1, 1000)
writeString("Tunnel length: ", 1, 5)
local tunnel_length = checkedInputNumber(35, 5, 1, 10000)
local tunnel_gap = 0
local right_tunnels = true
if num_tunnels > 1 then
writeString("Number of blocks between tunnels: ", 1, 6)
tunnel_gap = checkedInputNumber(35, 6, 1, 100)
writeString("Dig tunnels to the left or the right?", 1, 8)
right_tunnels = checkedInputTwoEnum(1, 9, "Right", "Left")
end
resetScreen()
writeString("Please place a chest behind the turtle", 1, 4)
writeString("and put some torches and coal in it.", 1, 5)
sleep(1.5)
writeCenteredString("Press any key to start...", 9)
os.pullEvent("key")
resetScreen()
if not stripMine(num_tunnels, tunnel_length, tunnel_gap, right_tunnels, pauseUI, "north") then
write("FATAL: Unable to detect chest")
else
write("** Strip Miner complete **")
end
clearLines(8, 13)
term.setCursorPos(1, 8)
closeLog()
end
stripMineWithUI()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment