SHARE
TWEET

Untitled

a guest Jan 12th, 2017 72 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. --[[
  2.   Branch mining program for OpenComputers robots.
  3.   This program is designed to dig out branches, in a fashion that allows
  4.   players to easily navigate the dug out tunnels. The primary concern was
  5.   not the performance of the mining, only a good detection rate, and nice
  6.   tunnels. Suggested upgrades for this are the geolyzer and inventory
  7.   controller upgrade, and depending on your world gen (ravines?) a hover
  8.   upgrade might be necessary. The rest is up to you (chunkloading, more
  9.   inventory, battery upgrades).
  10.   By Sangar, 2015
  11.   This program is licensed under the MIT license.
  12.   http://opensource.org/licenses/mit-license.php
  13. ]]
  14.  
  15. local component = require("component")
  16. local computer = require("computer")
  17. local robot = require("robot")
  18. local shell = require("shell")
  19. local sides = require("sides")
  20. local args, options = shell.parse(...)
  21.  
  22. --[[ Config ]]-----------------------------------------------------------------
  23.  
  24. -- Every how many blocks to dig a side shaft. The default makes for a
  25. -- two wide wall between tunnels, making sure we don't miss anything.
  26. local shaftInterval = 3
  27.  
  28. -- Max recursion level for mining ore veins. We abort early because we
  29. -- assume we'll encounter the same vein again from an adjacent tunnel.
  30. local maxVeinRecursion = 8
  31.  
  32. -- Every how many blocks to place a torch when placing torches.
  33. local torchInverval = 11
  34.  
  35. --[[ Constants ]]--------------------------------------------------------------
  36.  
  37. -- Quick look-up table for inverting directions.
  38. local oppositeSides = {
  39.   [sides.north] = sides.south,
  40.   [sides.south] = sides.north,
  41.   [sides.east] = sides.west,
  42.   [sides.west] = sides.east,
  43.   [sides.up] = sides.down,
  44.   [sides.down] = sides.up
  45. }
  46.  
  47. -- For pushTurn() readability.
  48. local left = false
  49. local right = not left
  50.  
  51. --[[ State ]]------------------------------------------------------------------
  52.  
  53. -- Slots we don't want to drop. Filled in during initialization, based
  54. -- on items already in the inventory. Useful for stuff like /dev/null.
  55. local keepSlot = {}
  56.  
  57. -- Slots that we keep torches in, updated when stocking up on torches.
  58. local torchSlots = {}
  59.  
  60. --[[ "Passive" logic ]]--------------------------------------------------------
  61.  
  62. -- Keep track of moves we're away from our origin, and average energy used per
  63. -- move. This is used to compute the threshold at which we have to return to
  64. -- maintenance to recharge.
  65. local preMoveEnergy, averageMoveCost, distanceToOrigin = 0, 15, 0
  66.  
  67. -- The actual callback called in postMove().
  68. local onMove
  69.  
  70. -- Called whenever we're about to move, used to compute move cost.
  71. local function preMove()
  72.   preMoveEnergy = computer.energy()
  73. end
  74.  
  75. -- Called whenever we're done moving, used for automatic torch placement an digging.
  76. local function postMove()
  77.   local moveCost = preMoveEnergy - computer.energy()
  78.   if moveCost > 0 then
  79.     averageMoveCost = (averageMoveCost + moveCost) / 2
  80.   end
  81.   if onMove then
  82.     onMove()
  83.   end
  84. end
  85.  
  86. --[[ Utility ]]----------------------------------------------------------------
  87.  
  88. local function prompt(message)
  89.   io.write(message .. " [Y/n] ")
  90.   local result = io.read()
  91.   return result and (result == "" or result:lower() == "y")
  92. end
  93.  
  94. -- Check if a block with the specified info should be mined.
  95. local function shouldMine(info)
  96.   return info and info.name and (info.name:match(".*ore.*") or info.name:match(".*Ore.*"))
  97. end
  98.  
  99. -- Number of stacks of torches to keep; default is 1 per inventory upgrade.
  100. local function torchStacks()
  101.   return math.max(1, math.ceil(robot.inventorySize() / 16))
  102. end
  103.  
  104. -- Look for the first empty slot in our inventory.
  105. local function findEmptySlot()
  106.   for slot = 1, robot.inventorySize() do
  107.     if robot.count(slot) == 0 then
  108.       return slot
  109.     end
  110.   end
  111. end
  112.  
  113. -- Find the first torch slot that still contains torches.
  114. local function findTorchSlot()
  115.   for _, slot in ipairs(torchSlots) do
  116.     if robot.count(slot) > 0 then
  117.       return slot
  118.     end
  119.   end
  120. end
  121.  
  122. -- Since robot.select() is an indirect call, we can speed things up a bit.
  123. local selectedSlot
  124. local function cachedSelect(slot)
  125.   if slot ~= selectedSlot then
  126.     robot.select(slot)
  127.     selectedSlot = slot
  128.   end
  129. end
  130.  
  131. -- Place a single torch above the robot, if there are any torches left.
  132. local function placeTorch()
  133.   local slot = findTorchSlot()
  134.   local result = false
  135.   if slot then
  136.     cachedSelect(slot)
  137.     result = robot.placeUp()
  138.     cachedSelect(1)
  139.   end
  140.   return result
  141. end
  142.  
  143. -- Dig out a block on the specified side, without tool if possible.
  144. local function dig(side, callback)
  145.   repeat
  146.     -- Check for maintenance first, to make sure we make the return trip when
  147.     -- our batteries are running low.
  148.     local emptySlot = findEmptySlot()
  149.     if callback then
  150.       callback(not emptySlot) -- Parameter: is inventory full.
  151.       emptySlot = findEmptySlot()
  152.     end
  153.     cachedSelect(1)
  154.  
  155.     local something, what = component.robot.detect(side)
  156.     if not something or what == "replaceable" or what == "liquid" then
  157.       return true -- We can just move into whatever is there.
  158.     end
  159.  
  160.     local brokeSomething
  161.  
  162.     local info = component.isAvailable("geolyzer") and
  163.                  component.geolyzer.analyze(side)
  164.     if info and info.name == "OpenComputers:robot" then
  165.       brokeSomething = true -- Wait for other robot to go away.
  166.       os.sleep(0.5)
  167.     elseif component.isAvailable("inventory_controller") and emptySlot then
  168.       cachedSelect(emptySlot)
  169.       component.inventory_controller.equip() -- Save some tool durability.
  170.       cachedSelect(1)
  171.       brokeSomething = component.robot.swing(side)
  172.       cachedSelect(emptySlot)
  173.       component.inventory_controller.equip()
  174.       cachedSelect(1)
  175.     end
  176.     if not brokeSomething then
  177.       brokeSomething = component.robot.swing(side)
  178.     end
  179.   until not brokeSomething
  180. end
  181.  
  182. -- Force a move towards in the specified direction.
  183. local function forceMove(side, delta)
  184.   preMove()
  185.   local result = component.robot.move(side)
  186.   if result then
  187.     distanceToOrigin = distanceToOrigin + delta
  188.     postMove()
  189.   else
  190.     -- Obstructed, try to clear the way.
  191.     if side == sides.back then
  192.       -- Moving backwards, turn around.
  193.       component.robot.turn(left)
  194.       component.robot.turn(left)
  195.       repeat
  196.         dig(sides.forward)
  197.         preMove()
  198.       until robot.forward()
  199.       distanceToOrigin = distanceToOrigin + delta
  200.       component.robot.turn(left)
  201.       component.robot.turn(left)
  202.       postMove() -- Slightly falsifies move cost, but must ensure we're rotated
  203.                  -- correctly in case postMove() triggers going to maintenance.
  204.     else
  205.       repeat
  206.         dig(side)
  207.         preMove()
  208.       until component.robot.move(side)
  209.       distanceToOrigin = distanceToOrigin + delta
  210.       postMove()
  211.     end
  212.   end
  213.   return true
  214. end
  215.  
  216. --[[ Navigation ]]-------------------------------------------------------------
  217.  
  218. -- Keeps track of our moves to allow "undoing" them for returning to the
  219. -- docking station. Format is a list of moves, represented as tables
  220. -- containing the type of move and distance to move, e.g.
  221. --   {move=sides.back, count=10},
  222. --   {turn=true, count=2}
  223. -- means we first moved back 10 blocks, then turned around.
  224. local moves = {}
  225.  
  226. -- Undo a *single* move, i.e. reduce the count of the latest move type.
  227. local function undoMove(move)
  228.   if move.move then
  229.     local side = oppositeSides[move.move]
  230.     forceMove(side, -1)
  231.   else
  232.     local direction = not move.turn
  233.     component.robot.turn(direction)
  234.   end
  235.   move.count = move.count - 1
  236. end
  237.  
  238. -- Make a turn in the specified direction.
  239. local function pushTurn(direction)
  240.   component.robot.turn(direction)
  241.   if moves[#moves] and moves[#moves].turn == direction then
  242.     moves[#moves].count = moves[#moves].count + 1
  243.   else
  244.     moves[#moves + 1] = {turn=direction, count=1}
  245.   end
  246.   return true -- Allows for `return pushMove() and pushTurn() and pushMove()`.
  247. end
  248.  
  249. -- Try to make a move towards the specified side.
  250. local function pushMove(side, force)
  251.   preMove()
  252.   local result, reason = (force and forceMove or component.robot.move)(side, 1)
  253.   if result then
  254.     if moves[#moves] and moves[#moves].move == side then
  255.       moves[#moves].count = moves[#moves].count + 1
  256.     else
  257.       moves[#moves + 1] = {move=side, count=1}
  258.     end
  259.     if not force then
  260.       distanceToOrigin = distanceToOrigin + 1
  261.     end
  262.     postMove()
  263.   end
  264.   return result, reason
  265. end
  266.  
  267. -- Undo the most recent move *type*. I.e. will undo all moves of the most
  268. -- recent type (say we moved forwards twice, this will go back twice).
  269. local function popMove()
  270.   -- Deep copy the move for returning it.
  271.   local move = moves[#moves] and {move=moves[#moves].move,
  272.                                   turn=moves[#moves].turn,
  273.                                   count=moves[#moves].count}
  274.   while moves[#moves] and moves[#moves].count > 0 do
  275.     undoMove(moves[#moves])
  276.   end
  277.   moves[#moves] = nil
  278.   return move
  279. end
  280.  
  281. -- Get the current top and count values, to be used as a position snapshot
  282. -- that can be restored later on by calling setTop().
  283. local function getTop()
  284.   if moves[#moves] then
  285.     return #moves, moves[#moves].count
  286.   else
  287.     return 0, 0
  288.   end
  289. end
  290.  
  291. -- Undo some moves based on a stored top and count received from getTop().
  292. local function setTop(top, count, unsafe)
  293.   assert(top >= 0)
  294.   assert(top <= #moves)
  295.   assert(count >= 0)
  296.   assert(top < #moves or count <= moves[#moves].count)
  297.   while #moves > top do
  298.     if unsafe then
  299.       if moves[#moves].move then
  300.         distanceToOrigin = distanceToOrigin - moves[#moves].count
  301.       end
  302.       moves[#moves] = nil
  303.     else
  304.       popMove()
  305.     end
  306.   end
  307.   local move = moves[#moves]
  308.   if move then
  309.     while move.count > count do
  310.       if unsafe then
  311.         move.count = move.count - 1
  312.         distanceToOrigin = distanceToOrigin - 1
  313.       else
  314.         undoMove(move)
  315.       end
  316.     end
  317.     if move.count < 1 then
  318.       moves[#moves] = nil
  319.     end
  320.   end
  321. end
  322.  
  323. -- Undo *all* moves made since program start, return the list of moves.
  324. local function popMoves()
  325.   local result = {}
  326.   local move = popMove()
  327.   while move do
  328.     table.insert(result, 1, move)
  329.     move = popMove()
  330.   end
  331.   return result
  332. end
  333.  
  334. -- Repeat the specified set of moves.
  335. local function pushMoves(moves)
  336.   for _, move in ipairs(moves) do
  337.     if move.move then
  338.       for _ = 1, move.count do
  339.         pushMove(move.move, true)
  340.       end
  341.     else
  342.       for _ = 1, move.count do
  343.         pushTurn(move.turn)
  344.       end
  345.     end
  346.   end
  347. end
  348.  
  349. --[[ Maintenance ]]------------------------------------------------------------
  350.  
  351. -- Energy required to return to docking bay.
  352. local function costToReturn()
  353.   -- Overestimate a bit, to account for obstacles such as gravel or mobs.
  354.   return 5000 + averageMoveCost * distanceToOrigin * 1.25
  355. end
  356.  
  357. -- Checks whether we need maintenance.
  358. local function needsMaintenance()
  359.   return not robot.durability() or -- Tool broken?
  360.          computer.energy() < costToReturn() or -- Out of juice?
  361.          not findTorchSlot() -- No more torches?
  362. end
  363.  
  364. -- Drops all inventory contents that are not marked for keeping.
  365. local function dropMinedBlocks()
  366.   if component.isAvailable("inventory_controller") then
  367.     if not component.inventory_controller.getInventorySize(sides.down) then
  368.       io.write("There doesn't seem to be an inventory below me! Waiting to avoid spilling stuffs into the world.\n")
  369.     end
  370.     repeat os.sleep(5) until component.inventory_controller.getInventorySize(sides.down)
  371.   end
  372.   io.write("Dropping what I found.\n")
  373.   for slot = 1, robot.inventorySize() do
  374.     while not keepSlot[slot] and robot.count(slot) > 0 do
  375.       cachedSelect(slot)
  376.       robot.dropDown()
  377.     end
  378.   end
  379.   cachedSelect(1)
  380. end
  381.  
  382. -- Ensures we have a tool with durability.
  383. local function checkTool()
  384.   if not robot.durability() then
  385.     io.write("Tool is broken, getting a new one.\n")
  386.     if component.isAvailable("inventory_controller") then
  387.       cachedSelect(findEmptySlot()) -- Select an empty slot for working.
  388.       repeat
  389.         component.inventory_controller.equip() -- Drop whatever's in the tool slot.
  390.         while robot.count() > 0 do
  391.           robot.dropDown()
  392.         end
  393.         robot.suckUp(1) -- Pull something from above and equip it.
  394.         component.inventory_controller.equip()
  395.       until robot.durability()
  396.       cachedSelect(1)
  397.     else
  398.       -- Can't re-equip autonomously, wait for player to give us a tool.
  399.       io.write("HALP! I need a new tool.\n")
  400.       repeat
  401.         event.pull(10, "inventory_changed")
  402.       until robot.durability()
  403.     end
  404.   end
  405. end
  406.  
  407. -- Ensures we have some torches.
  408. local function checkTorches()
  409.   -- First, clean up our list and look for empty slots.
  410.   io.write("Getting my fill of torches.\n")
  411.   local oldTorchSlots = torchSlots
  412.   torchSlots = {}
  413.   for _, slot in ipairs(oldTorchSlots) do
  414.     keepSlot[slot] = nil
  415.     if robot.count(slot) > 0 then
  416.       torchSlots[#torchSlots + 1] = slot
  417.     end
  418.   end
  419.   while #torchSlots < torchStacks() do
  420.     local slot = findEmptySlot()
  421.     if not slot then
  422.       break -- This should never happen...
  423.     end
  424.     torchSlots[#torchSlots + 1] = slot
  425.   end
  426.   -- Then fill the slots with torches.
  427.   robot.turnLeft()
  428.   for _, slot in ipairs(torchSlots) do
  429.     keepSlot[slot] = true
  430.     if robot.space(slot) > 0 then
  431.       cachedSelect(slot)
  432.       repeat
  433.         local before = robot.space()
  434.         robot.suck(robot.space())
  435.         if robot.space() == before then
  436.           os.sleep(5) -- Don't busy idle.
  437.         end
  438.       until robot.space() < 1
  439.       cachedSelect(1)
  440.     end
  441.   end
  442.   robot.turnRight()
  443. end
  444.  
  445. -- Recharge our batteries.
  446. local function recharge()
  447.   io.write("Waiting until my batteries are full.\n")
  448.   while computer.maxEnergy() - computer.energy() > 100 do
  449.     os.sleep(1)
  450.   end
  451. end
  452.  
  453. -- Go back to the docking bay for general maintenance if necessary.
  454. local function gotoMaintenance(force)
  455.   if not force and not needsMaintenance() then
  456.     return -- No need yet.
  457.   end
  458.  
  459.   -- Save some values for later, temporarily remove onMove callback.
  460.   local returnCost = costToReturn()
  461.   local moveCallback = onMove
  462.   onMove = nil
  463.  
  464.   local top, count = getTop()
  465.  
  466.   io.write("Going back for maintenance!\n")
  467.   local moves = popMoves()
  468.  
  469.   assert(distanceToOrigin == 0)
  470.  
  471.   dropMinedBlocks()
  472.   checkTool()
  473.   checkTorches()
  474.   recharge() -- Last so we can charge some during the other operations.
  475.  
  476.   if moves and #moves > 0 then
  477.     if returnCost * 2 > computer.maxEnergy() and
  478.        not options.f and
  479.        not prompt("Going back will cost me half my energy. There's a good chance I will not return. Do you want to send me to my doom anyway?")
  480.     then
  481.       os.exit()
  482.     end
  483.     io.write("Returning to where I left off.\n")
  484.     pushMoves(moves)
  485.   end
  486.  
  487.   local newTop, newCount = getTop()
  488.   assert(top == newTop)
  489.   assert(count == newCount)
  490.  
  491.   onMove = moveCallback
  492. end
  493.  
  494. --[[ Mining ]]-----------------------------------------------------------------
  495.  
  496. -- Move towards the specified direction, digging out blocks as necessary.
  497. -- This is a "soft" version of forceMove in that it will try to clear its path,
  498. -- but fail if it can't.
  499. local function move(side)
  500.   local result, reason, retry
  501.   repeat
  502.     retry = false
  503.     if side ~= sides.back then
  504.       retry = dig(side, gotoMaintenance)
  505.     else
  506.       gotoMaintenance()
  507.     end
  508.     result, reason = pushMove(side)
  509.   until result or not retry
  510.   return result, reason
  511. end
  512.  
  513. -- Turn to face the specified, relative orientation.
  514. local function turnTowards(side)
  515.   if side == sides.left then
  516.     pushTurn(left)
  517.   elseif side == sides.right then
  518.     pushTurn(right)
  519.   elseif side == sides.back then
  520.     pushTurn(left)
  521.     pushTurn(left)
  522.   end
  523. end
  524.  
  525. --[[ On move callbacks ]]------------------------------------------------------
  526.  
  527. -- Start automatically placing torches in the configured interval.
  528. local function beginPlacingTorches()
  529.   local counter = 2
  530.   onMove = function()
  531.     if counter < 1 then
  532.       if placeTorch() then
  533.         counter = torchInverval
  534.       end
  535.     else
  536.       counter = counter - 1
  537.     end
  538.   end
  539. end
  540.  
  541. -- Start digging out the block below us after each move.
  542. local function beginDigginTrench()
  543.   onMove = function()
  544.     dig(sides.down, gotoMaintenance)
  545.   end
  546. end
  547.  
  548. -- Stop automatically placing torches.
  549. local function clearMoveCallback()
  550.   onMove = nil
  551. end
  552.  
  553. --[[ Moving ]]-----------------------------------------------------------------
  554.  
  555. -- Dig out any interesting ores adjacent to the current position, recursively.
  556. -- POST: back to the starting position and facing.
  557. local function digVein(maxDepth)
  558.   if maxDepth < 1 then return end
  559.   for _, side in ipairs(sides) do
  560.     side = sides[side]
  561.     if shouldMine(component.geolyzer.analyze(side)) then
  562.       local top, count = getTop()
  563.       turnTowards(side)
  564.       if side == sides.up or side == sides.down then
  565.         move(side)
  566.       else
  567.         move(sides.forward)
  568.       end
  569.       digVein(maxDepth - 1)
  570.       setTop(top, count)
  571.     end
  572.   end
  573. end
  574.  
  575. -- Dig out any interesting ores adjacent to the current position, recursively.
  576. -- Also checks blocks adjacent to above block in exhaustive mode.
  577. -- POST: back at the starting position and facing.
  578. local function digVeins(exhaustive)
  579.   if component.isAvailable("geolyzer") then
  580.     digVein(maxVeinRecursion)
  581.     if exhaustive and move(sides.up) then
  582.       digVein(maxVeinRecursion)
  583.       popMove()
  584.     end
  585.   end
  586. end
  587.  
  588. -- Dig a 1x2 tunnel of the specified length. Checks for ores.
  589. -- Also checks upper row for ores in exhaustive mode.
  590. -- PRE: bottom front of tunnel to dig.
  591. -- POST: at the end of the tunnel.
  592. local function dig1x2(length, exhaustive)
  593.   while length > 0 and move(sides.forward) do
  594.     dig(sides.up, gotoMaintenance)
  595.     digVeins(exhaustive)
  596.     length = length - 1
  597.   end
  598.   return length < 1
  599. end
  600.  
  601. -- Dig a 1x3 tunnel of the specified length.
  602. -- PRE: center front of tunnel to dig.
  603. -- POST: at the end of the tunnel.
  604. local function dig1x3(length)
  605.   while length > 0 and move(sides.forward) do
  606.     dig(sides.up, gotoMaintenance)
  607.     dig(sides.down, gotoMaintenance)
  608.     length = length - 1
  609.   end
  610.   return length < 1
  611. end
  612.  
  613. -- Dig out a main shaft.
  614. -- PRE: bottom front of main shaft.
  615. -- POST: bottom front of main shaft.
  616. local function digMainShaft(length)
  617.   io.write("Digging main shaft.\n")
  618.  
  619.   if not move(sides.up) then
  620.     return false
  621.   end
  622.  
  623.   local top, count = getTop()
  624.  
  625.   if not (dig1x3(length) and
  626.           pushTurn(left) and
  627.           dig1x3(1) and
  628.           pushTurn(left) and
  629.           dig1x3(length - 1) and
  630.           (placeTorch() or true) and -- Just keep going...
  631.           pushTurn(left) and
  632.           dig1x3(1))
  633.   then
  634.     return false
  635.   end
  636.  
  637.    -- Create snapshot for shortcut below.
  638.   local midTop, midCount = getTop()
  639.  
  640.   if not (dig1x3(1) and
  641.           pushTurn(left) and
  642.           dig1x3(length - 1))
  643.   then
  644.     return false
  645.   end
  646.   placeTorch()
  647.  
  648.   -- Shortcut: manually move back to start, do an unsafe setTop.
  649.   -- Otherwise we'd have to retrace all three rows.
  650.   setTop(midTop, midCount)
  651.   if pushTurn(left) and move(sides.back) then
  652.     setTop(top, count, true)
  653.     return true
  654.   end
  655.  
  656.   return false
  657. end
  658.  
  659. -- Dig all shafts in one cardinal direction (the one we're facing).
  660. -- PRE: bottom front of a main shaft.
  661. -- POST: bottom front of a main shaft.
  662. local function digShafts(length)
  663.   local top, count = getTop() -- Remember start of main shaft.
  664.   local ok = digMainShaft(length)
  665.   setTop(top, count)
  666.   if not ok then
  667.     io.write("Failed digging main shaft, skipping.\n")
  668.     return
  669.   end
  670.  
  671.   io.write("Beginning work on side shafts.\n")
  672.   for i = shaftInterval, length, shaftInterval do
  673.     io.write("Working on shafts #" .. (i / shaftInterval) .. ".\n")
  674.  
  675.     if not dig1x2(shaftInterval) then -- Move to height of shaft.
  676.       break
  677.     end
  678.     local sideTop, sideCount = getTop() -- Remember position.
  679.  
  680.     pushTurn(left) -- Dig left shaft.
  681.     dig1x2(i + 2, true)
  682.     beginPlacingTorches()
  683.     setTop(sideTop, sideCount)
  684.     clearMoveCallback()
  685.  
  686.     pushTurn(right) -- Dig right shaft.
  687.     dig1x2(i + 2, true)
  688.     beginPlacingTorches()
  689.     setTop(sideTop, sideCount)
  690.     clearMoveCallback()
  691.   end
  692.  
  693.   -- Go back to start of main shaft. Dig out the center of the main shaft
  694.   -- while we're at it, so we break through the ceiling between levels.
  695.   beginDigginTrench()
  696.   setTop(top, count)
  697.   clearMoveCallback()
  698. end
  699.  
  700. -- Moves to the next main shaft, clockwise.
  701. -- PRE: bottom front of nth main shaft.
  702. -- POST: bottom front of (n+1)th main shaft.
  703. local function gotoNextMainShaft()
  704.   return pushTurn(right) and
  705.          dig1x2(2) and
  706.          pushTurn(right) and
  707.          dig1x2(2) and
  708.          pushTurn(left)
  709. end
  710.  
  711. --[[ Main ]]-------------------------------------------------------------------
  712.  
  713. local function main(radius, levels, full)
  714.   -- We dig tunnels every three blocks, to have a spacing of
  715.   -- two blocks between them (so we don't really have to follow
  716.   -- veins but can just break the blocks in the wall that are
  717.   -- interesting to us). So adjust the length accordingly.
  718.   radius = radius - radius % shaftInterval
  719.  
  720.   -- Flag slots that contain something as do-not-drop and check
  721.   -- that we have some free inventory space at all.
  722.   local freeSlots = robot.inventorySize()
  723.   for slot = 1, robot.inventorySize() do
  724.     if robot.count(slot) > 0 then
  725.       keepSlot[slot] = true
  726.       freeSlots = freeSlots - 1
  727.     end
  728.   end
  729.   if freeSlots < 2 + torchStacks() then -- Place for mined blocks + torches.
  730.     io.write("Sorry, but I need more empty inventory space to work.\n")
  731.     os.exit()
  732.   end
  733.   gotoMaintenance(true)
  734.  
  735.   if not move(sides.forward) then
  736.     io.write("Exit from docking bay obstructed, aborting.\n")
  737.     os.exit()
  738.   end
  739.  
  740.   for level = 1, levels do
  741.     if level > 1 then
  742.       for _ = 1, 4 do
  743.         if not move(sides.down) then
  744.           io.write("Access to level " .. level .. " obstructed, aborting.\n")
  745.           popMoves()
  746.           gotoMaintenance(true)
  747.           os.exit()
  748.         end
  749.       end
  750.     end
  751.  
  752.     local top, count = getTop()
  753.  
  754.     for shaft = 1, full and 4 or 1 do
  755.       if shaft > 1 and not gotoNextMainShaft() then
  756.         break
  757.       end
  758.       digShafts(radius)
  759.     end
  760.     if full then
  761.       gotoNextMainShaft() -- Finish the circle.
  762.     end
  763.  
  764.     setTop(top, count)
  765.   end
  766.  
  767.   io.write("All done! Going home to clean up.\n")
  768.   popMoves()
  769.   gotoMaintenance(true)
  770. end
  771.  
  772. if options.h or options.help then
  773.   io.write("Usage: miner [-hsf] [radius [levels [full]]]\n")
  774.   io.write("  -h:     this help listing.\n")
  775.   io.write("  -s:     start without prompting.\n")
  776.   io.write("  -f:     force mining to continue even if max\n")
  777.   io.write("          fuel may be insufficient to return.\n")
  778.   io.write("  radius: the radius in blocks of the area to\n")
  779.   io.write("          mine. Adjusted to be a multiple of\n")
  780.   io.write("          three. Default: 9.\n")
  781.   io.write("  levels: the number of vertical levels to mine.\n")
  782.   io.write("          Default: 1.\n")
  783.   io.write("  full:   whether to mine a full level (all four\n")
  784.   io.write("          cardinal directions). Default: false.\n")
  785.   os.exit()
  786. end
  787.  
  788. local radius = tonumber(args[1]) or 9
  789. local levels = tonumber(args[2]) or 1
  790. local full = args[3] and args[3] == "true" or args[3] == "yes"
  791.  
  792. io.write("Will mine " .. levels .. " levels in a radius of " .. radius .. ".\n")
  793. if full then
  794.   io.write("Will mine all four cardinal directions.\n")
  795. end
  796. if not component.isAvailable("geolyzer") then
  797.   io.write("Installing a geolyzer upgrade is strongly recommended.\n")
  798. end
  799. if not component.isAvailable("inventory_controller") then
  800.   io.write("Installing an inventory controller upgrade is strongly recommended.\n")
  801. end
  802.  
  803. io.write("I'll drop mined out stuff below me.\n")
  804. io.write("I'll be looking for torches on my left.\n")
  805. if component.isAvailable("inventory_controller") then
  806.   io.write("I'll try to get new tools from above me.\n")
  807. else
  808.   io.write("You'll need to manually provide me with new tools if they break.\n")
  809. end
  810.  
  811. io.write("Run with -h or --help for parameter info.\n")
  812.  
  813. if options.s or prompt("Shall we begin?") then
  814.   main(radius, levels, full)
  815. end
RAW Paste Data
Top