SHARE
TWEET

Untitled

a guest Jun 26th, 2019 62 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. if redstone then
  2.   function require(lib) shell.run(lib .. '.lua') end
  3. end
  4.  
  5. require 'lib/class'
  6. require 'lib/serpent'
  7. require 'lib/utils'
  8. require 'lib/items'
  9.  
  10. local defaultConfig = {
  11.   state_file = 'state/state.lua',
  12.   position_file = 'state/position.lua',
  13.   storage_file = 'state/storage.lua',
  14.   name_file = 'state/name.txt',
  15.   error_log = 'error.log',
  16.   max_fuel = 1600,
  17.   refuel_treshhold = 0.6,
  18.   lumberjack_interval = 200,
  19.   reeds_interval = 200,
  20.   stuck_count = 200,
  21.   mining_area = 30,
  22.   base_height = 18,
  23.   base_spacing = 20,
  24. }
  25.  
  26. local startingState = {
  27.   have_base = false,
  28.   have_disk_drive = false,
  29.   have_floppy = false,
  30.   have_baby = false,
  31.   num_babies = 0,
  32.   mining_fails = 0,
  33. }
  34.  
  35. local cardinal = enum {'north', 'east', 'south', 'west'}
  36.  
  37. local startingPosition = {
  38.   x = 0, y = 0, z = 0,
  39.   bearing = cardinal.north,
  40. }
  41.  
  42. local startingMaterials = Materials.fromTable {
  43.   'crafting_table', 'diamond_pickaxe', {'coal', 12}, {'cobblestone', 40},
  44.   {'dirt', 5}, {'log', 10}, {'reeds', 2}, {'sapling', 6}, {'water_bucket', 2}
  45. }
  46.  
  47. local relocatingMaterials = Materials.fromTable {
  48.   {'cobblestone', 40}, {'dirt', 7}, {'log', 10}, {'reeds', 2},
  49.   {'sapling', 6}, {'water_bucket', 2}
  50. }
  51.  
  52. local inventoryMaterials = Materials.fromTable {
  53.   {'cobblestone', 64 * 2}, {'dirt', 32}, {'coal', 64},
  54. }
  55.  
  56. local storageMaterials = Materials.fromTable {
  57.   {'redstone', 64 * 3}, {'sand', 64 * 2}, 'log', 'iron_ore', 'diamond',
  58.   'reeds', 'sapling', 'glass_pane', 'iron_ingot', 'ComputerCraft:CC-Computer',
  59.   'bucket', 'water_bucket', 'diamond_pickaxe', 'crafting_table'
  60. }
  61.  
  62. local miningMaterials = Materials.fromTable {
  63.   'coal_ore', 'diamond_ore', 'iron_ore', 'lit_redstone_ore',
  64.   'redstone_ore', 'sand'
  65. }
  66.  
  67. local unwantedMaterials = Materials.fromTable {
  68.   'brown_mushroom', 'cactus', 'carrots', 'deadbush', 'double_plant', 'flint',
  69.   'leaves', 'leaves2', 'log2', 'melon_block', 'melon_seeds', 'melon_stem',
  70.   'potatoes', 'pumpkin', 'pumpkin_seeds', 'pumpkin_stem', 'red_flower',
  71.   'red_mushroom', 'tallgrass', 'torch', 'vine', 'waterlily', 'web', 'wheat',
  72.   'wheat_seeds', 'yellow_flower', 'water', 'flowing_water'
  73. }
  74.  
  75. local Storage = class('Storage')
  76.  
  77. function Storage:initialize(position, filename, replicator)
  78.   self.position = position
  79.   self.filename = filename
  80.   self.replicator = replicator
  81.   if fs.exists(self.filename) then
  82.     local file = fs.open(self.filename, 'r')
  83.     local success, data = serpent.load(file.readAll())
  84.     if success then
  85.       self.top = Materials:new(data.top)
  86.       self.bottom = Materials:new(data.bottom)
  87.     else
  88.       error('Corrupted materials data.')
  89.     end
  90.   else
  91.     self.top = Materials:new({})
  92.     self.bottom = Materials:new({})
  93.   end
  94. end
  95.  
  96. function Storage:reset()
  97.   self.top = Materials:new({})
  98.   self.bottom = Materials:new({})
  99.   self:write()
  100. end
  101.  
  102. function Storage:count(item)
  103.   return self.top:count(item) + self.bottom:count(item)
  104. end
  105.  
  106. function Storage:store(item, count)
  107.   if not comparePosition(self.replicator.position, self.position) then
  108.     error('Not at storage.')
  109.   end
  110.  
  111.   local item = Item.resolve(item)
  112.   if count == nil then
  113.     count = item.count
  114.   end
  115.  
  116.   local topCount = self.top:count(item)
  117.   local bottomCount = self.bottom:count(item)
  118.  
  119.   local direction, storage
  120.  
  121.   if topCount == 0 and bottomCount == 0 then
  122.     if self.bottom:numMaterials() > self.top:numMaterials() then
  123.       direction = 'up'
  124.       storage = self.top
  125.     else
  126.       direction = 'down'
  127.       storage = self.bottom
  128.     end
  129.   else
  130.     if bottomCount > topCount then
  131.       direction = 'down'
  132.       storage = self.bottom
  133.     else
  134.       direction = 'up'
  135.       storage = self.top
  136.     end
  137.   end
  138.  
  139.   storage:addItem(item, count)
  140.   self.replicator:drop(item, count, direction)
  141.   self:write()
  142. end
  143.  
  144. function Storage:retrieve(item, count)
  145.   if not comparePosition(self.replicator.position, self.position) then
  146.     error('Not at storage.')
  147.   end
  148.  
  149.   local item = Item.resolve(item)
  150.   local retrieved = 0
  151.  
  152.   local topCount = self.top:count(item)
  153.   local bottomCount = self.bottom:count(item)
  154.  
  155.   if count == nil then
  156.     count = item.count
  157.   end
  158.  
  159.   if count > (topCount + bottomCount) then
  160.     error('Not enough items in storage.')
  161.   end
  162.  
  163.   while retrieved < count do
  164.  
  165.     self.replicator:compactInventory()
  166.  
  167.     local slot = self.replicator:getEmptySlot()
  168.     if slot == nil then
  169.       error('No space in inventory.')
  170.     end
  171.  
  172.     turtle.select(slot)
  173.     local direction, input, output, outFn
  174.  
  175.     if topCount > bottomCount then
  176.       direction = 'up'
  177.       input = self.top
  178.       output = self.bottom
  179.       turtle.suckUp()
  180.       outFn = turtle.dropDown
  181.     else
  182.       direction = 'down'
  183.       input = self.bottom
  184.       output = self.top
  185.       turtle.suckDown()
  186.       outFn = turtle.dropUp
  187.     end
  188.  
  189.     local slotItem = self.replicator:inspect(slot)
  190.     if slotItem == nil then
  191.       error('Storage inconsistency.')
  192.     end
  193.  
  194.     input:removeItem(slotItem)
  195.     self:write()
  196.  
  197.     local need = count - retrieved
  198.     if slotItem.name == item.name then
  199.       if slotItem.count > need then
  200.         self:store(slotItem, slotItem.count - need)
  201.         retrieved = retrieved + need
  202.       else
  203.         retrieved = retrieved + slotItem.count
  204.       end
  205.     else
  206.       outFn()
  207.       output:addItem(slotItem)
  208.       self:write()
  209.     end
  210.  
  211.     topCount = self.top:count(item)
  212.     bottomCount = self.bottom:count(item)
  213.   end
  214. end
  215.  
  216. function Storage:write()
  217.   local file = fs.open(self.filename, 'w')
  218.   file.write(serpent.dump({top=self.top, bottom=self.bottom}))
  219.   file.close()
  220. end
  221.  
  222. local Replicator = class('Replicator')
  223.  
  224. function Replicator:initialize(config)
  225.   self.config = {}
  226.   for key,default in pairs(defaultConfig) do
  227.     if config and config[key] then
  228.       self.config[key] = config[key]
  229.     else
  230.       self.config[key] = default
  231.     end
  232.   end
  233.   if fs.exists(self.config.state_file) then
  234.     local file = fs.open(self.config.state_file, 'r')
  235.     local success, data = serpent.load(file.readAll())
  236.     if success then
  237.       self.state = data
  238.     else
  239.       error('Corrupted state data.')
  240.     end
  241.   else
  242.     self.state = copy(startingState)
  243.   end
  244.   if fs.exists(self.config.position_file) then
  245.     local file = fs.open(self.config.position_file, 'r')
  246.     local success, data = serpent.load(file.readAll())
  247.     if success then
  248.       self.position = data
  249.     else
  250.       error('Corrupted position data.')
  251.     end
  252.   else
  253.     self.position = copy(startingPosition)
  254.   end
  255.   self.storage = Storage:new({x=1,y=0,z=0}, self.config.storage_file, self)
  256.   self.lastLumberTrip = -self.config.lumberjack_interval
  257.   self.lastReedsTrip = -self.config.reeds_interval
  258. end
  259.  
  260. function Replicator:simpleMove(direction, steps)
  261.   if steps == nil then
  262.     steps = 1
  263.   end
  264.  
  265.   local move, attack, detect, dig, axis, delta
  266.  
  267.   if direction == 'forward' or direction == nil then
  268.     move = turtle.forward
  269.     attack = turtle.attack
  270.     detect = turtle.detect
  271.     dig = turtle.dig
  272.  
  273.     if self.position.bearing == cardinal.north then
  274.       axis = 'y'
  275.       delta = 1
  276.     elseif self.position.bearing == cardinal.south then
  277.       axis = 'y'
  278.       delta = -1
  279.     elseif self.position.bearing == cardinal.east then
  280.       axis = 'x'
  281.       delta = 1
  282.     elseif self.position.bearing == cardinal.west then
  283.       axis = 'x'
  284.       delta = -1
  285.     else
  286.       error('Invalid bearing.')
  287.     end
  288.  
  289.   elseif direction == 'up' then
  290.     move = turtle.up
  291.     attack = turtle.attackUp
  292.     detect = turtle.detectUp
  293.     dig = turtle.digUp
  294.  
  295.     axis = 'z'
  296.     delta = 1
  297.  
  298.   elseif direction == 'down' then
  299.     move = turtle.down
  300.     attack = turtle.attackDown
  301.     detect = turtle.detectDown
  302.     dig = turtle.digDown
  303.  
  304.     axis = 'z'
  305.     delta = -1
  306.  
  307.   else
  308.     error('Invalid direction.')
  309.   end
  310.  
  311.   local successfulMove, tries
  312.   for _ = 1,steps do
  313.     successfulMove = false
  314.     tries = 0
  315.     while not successfulMove do
  316.       local block = self:inspect(direction)
  317.       if block then
  318.         if not isTurtle(block) then
  319.           dig()
  320.         else
  321.           local side
  322.           if direction == nil or direction == 'forward' then
  323.             side = 'front'
  324.           elseif direction == 'up' then
  325.             side = 'top'
  326.           else
  327.             side = 'bottom'
  328.           end
  329.           if peripheral.isPresent(side) and not peripheral.call(side, 'isOn') then
  330.             -- dig turtles that are off
  331.             dig()
  332.           elseif math.random() < 0.2 then
  333.             -- do the anti-collison dance
  334.             if turtle.forward() then
  335.               sleep(3) while not turtle.back() do end
  336.             elseif turtle.down() then
  337.               sleep(3) while not turtle.up() do end
  338.             else
  339.               turtle.turnLeft()
  340.               if turtle.forward() then
  341.                 sleep(3) while not turtle.back() do end
  342.               end
  343.               turtle.turnRight()
  344.             end
  345.           end
  346.         end
  347.       end
  348.       if not move() then
  349.         if turtle.getFuelLevel() == 0 then
  350.           self:refuel()
  351.         else
  352.           attack()
  353.         end
  354.       else
  355.         successfulMove = true
  356.         self.position[axis] = self.position[axis] + delta
  357.         self:writePosition()
  358.       end
  359.       tries = tries + 1
  360.       if tries > self.config.stuck_count then
  361.         error('Got stuck')
  362.       end
  363.     end
  364.   end
  365. end
  366.  
  367. function Replicator:move(axis, position)
  368.   if type(axis) == 'table' then
  369.     self:move('x', axis.x)
  370.     self:move('y', axis.y)
  371.     self:move('z', axis.z)
  372.     if axis.bearing then self:turn(axis.bearing) end
  373.     return
  374.   end
  375.   local steps = difference(self.position[axis], position)
  376.   if steps == 0 then
  377.     return
  378.   end
  379.   local increase = self.position[axis] < position
  380.   local direction = 'forward'
  381.   if axis == 'x' then
  382.     if increase then self:turn(cardinal.east) else self:turn(cardinal.west) end
  383.   elseif axis == 'y' then
  384.     if increase then self:turn(cardinal.north) else self:turn(cardinal.south) end
  385.   elseif axis == 'z' then
  386.     if increase then direction = 'up' else direction = 'down' end
  387.   else
  388.     error('Invalid axis.')
  389.   end
  390.   self:simpleMove(direction, steps)
  391. end
  392.  
  393. function Replicator:turn(direction)
  394.   if direction == 'right' then
  395.     direction = ((self.position.bearing - 1 + 1) % 4) + 1
  396.   elseif direction == 'left' then
  397.     direction = ((self.position.bearing - 1 - 1) % 4) + 1
  398.   end
  399.  
  400.   local delta = (direction - self.position.bearing + 2) % 4 - 2
  401.   local numTurns = math.abs(delta)
  402.  
  403.   local step, turn
  404.   if delta > 0 then
  405.     turn = turtle.turnRight
  406.     step = 1
  407.   else
  408.     turn = turtle.turnLeft
  409.     step = -1
  410.   end
  411.  
  412.   for _ = 1,numTurns do
  413.     while not turn() do end
  414.     self.position.bearing = ((self.position.bearing - 1 + step) % 4) + 1
  415.     self:writePosition()
  416.   end
  417. end
  418.  
  419. function Replicator:getEmptySlot()
  420.   for slotIdx = 1,16 do
  421.     local slotItem = self:inspect(slotIdx)
  422.     if slotItem == nil then
  423.       return slotIdx
  424.     end
  425.   end
  426.   return null
  427. end
  428.  
  429. function Replicator:inspect(slotOrDirection)
  430.   if slotOrDirection == nil or type(slotOrDirection) == 'string' then
  431.     local inspect
  432.     if slotOrDirection == nil or slotOrDirection == 'forward' then
  433.       inspect = turtle.inspect
  434.     elseif slotOrDirection == 'up' then
  435.       inspect = turtle.inspectUp
  436.     elseif slotOrDirection == 'down' then
  437.       inspect = turtle.inspectDown
  438.     else
  439.       error('Invalid direction.')
  440.     end
  441.     local success, data = inspect()
  442.     if success then
  443.       return Item.fromTable(data)
  444.     else
  445.       return nil
  446.     end
  447.   elseif type(slotOrDirection) == 'number' then
  448.     if slotOrDirection > 16 or slotOrDirection < 1 then
  449.       error('Invalid slot.')
  450.     end
  451.     local item = Item.resolve(turtle.getItemDetail(slotOrDirection))
  452.     if item then
  453.       item.slot = slotOrDirection
  454.     end
  455.     return item
  456.   else
  457.     error('Invalid argument.')
  458.   end
  459. end
  460.  
  461. function Replicator:inspectAll(filter)
  462.   local filter = Item.resolve(filter)
  463.   local items = {}
  464.   for slotIdx = 1,16 do
  465.     local slotItem = self:inspect(slotIdx)
  466.     if slotItem ~= nil and (filter == nil or slotItem.name == filter.name) then
  467.       table.insert(items, slotItem)
  468.     end
  469.   end
  470.   return items
  471. end
  472.  
  473. function Replicator:detect(item, direction)
  474.   local item = Item.resolve(item)
  475.   local detected = self:inspect(direction)
  476.   if detected and (item == nil or item.name == detected.name) then
  477.     return true
  478.   end
  479.   return false
  480. end
  481.  
  482. function Replicator:detectAny(materials, direction)
  483.   local materials = Materials.resolve(materials)
  484.   local detected = self:inspect(direction)
  485.   return materials:count(detected) > 0
  486. end
  487.  
  488. function Replicator:select(item)
  489.   local item = Item.resolve(item)
  490.   if not item then return nil end
  491.   for slotIdx = 1,16 do
  492.     local slotItem = self:inspect(slotIdx)
  493.     if slotItem ~= nil and slotItem.name == item.name then
  494.       turtle.select(slotIdx)
  495.       return item
  496.     end
  497.   end
  498.   return nil
  499. end
  500.  
  501. function Replicator:count(item)
  502.   local item = Item.resolve(item)
  503.   local count = 0
  504.   for slotIdx = 1,16 do
  505.     local slotItem = self:inspect(slotIdx)
  506.     if slotItem ~= nil and slotItem.name == item.name then
  507.       count = count + slotItem.count
  508.     end
  509.   end
  510.   return count
  511. end
  512.  
  513. function Replicator:place(item, direction)
  514.   local item = Item.resolve(item)
  515.  
  516.   local place
  517.   if direction == nil or direction == 'forward' then
  518.     place = turtle.place
  519.   elseif direction == 'up' then
  520.     place = turtle.placeUp
  521.   elseif direction == 'down' then
  522.     place = turtle.placeDown
  523.   else
  524.     error('Invalid direction.')
  525.   end
  526.  
  527.   if self:select(item) then
  528.     return place()
  529.   end
  530.   return false
  531. end
  532.  
  533. function Replicator:drop(item, count, direction)
  534.   local item = Item.resolve(item)
  535.  
  536.   local drop
  537.   if direction == nil or direction == 'forward' then
  538.     drop = turtle.drop
  539.   elseif direction == 'up' then
  540.     drop = turtle.dropUp
  541.   elseif direction == 'down' then
  542.     drop = turtle.dropDown
  543.   else
  544.     error('Invalid direction.')
  545.   end
  546.  
  547.   local items = self:inspectAll(item)
  548.   local function compare(a, b) return a.count < b.count end
  549.   table.sort(items, compare)
  550.  
  551.   local dropped = 0
  552.   if count == nil then
  553.     local total = 0
  554.     for _,slotItem in ipairs(items) do
  555.       total = total + slotItem.count
  556.     end
  557.     count = total
  558.   end
  559.  
  560.   for _,item in ipairs(items) do
  561.       local toDrop = math.min(item.count, count - dropped)
  562.       if toDrop > 0 then
  563.         turtle.select(item.slot)
  564.         drop(toDrop)
  565.         dropped = dropped + toDrop
  566.       end
  567.   end
  568.  
  569.   return dropped
  570. end
  571.  
  572. function Replicator:exec(instructions)
  573.   local selectedItem = nil
  574.   for cmd in string.gmatch(instructions, '%S+') do
  575.     local control = string.lower(string.sub(cmd, 0, 1))
  576.     if control == '!' then
  577.       selectedItem = self:select(string.sub(cmd, 2))
  578.     elseif control == 'x' or control == 'y' or control == 'z' then
  579.       self:move(control, tonumber(string.sub(cmd, 2)))
  580.     else
  581.       cmd = string.upper(cmd)
  582.       if     cmd == 'F'  then self:simpleMove('forward')
  583.       elseif cmd == 'U'  then self:simpleMove('up')
  584.       elseif cmd == 'D'  then self:simpleMove('down')
  585.       elseif cmd == 'P'  then self:place(selectedItem)
  586.       elseif cmd == 'PD' then self:place(selectedItem, 'down')
  587.       elseif cmd == 'PU' then self:place(selectedItem, 'up')
  588.       elseif cmd == 'L'  then self:turn('left')
  589.       elseif cmd == 'R'  then self:turn('right')
  590.       elseif cmd == 'N'  then self:turn(cardinal.north)
  591.       elseif cmd == 'E'  then self:turn(cardinal.east)
  592.       elseif cmd == 'S'  then self:turn(cardinal.south)
  593.       elseif cmd == 'W'  then self:turn(cardinal.west)
  594.       elseif cmd == 'SF' then turtle.suck()
  595.       elseif cmd == 'SD' then turtle.suckDown()
  596.       elseif cmd == 'SU' then turtle.suckUp()
  597.       elseif cmd == 'Q'  then turtle.dig()
  598.       elseif cmd == 'QU' then turtle.digUp()
  599.       elseif cmd == 'QD' then turtle.digDown()
  600.       else error('Invalid command.') end
  601.     end
  602.   end
  603. end
  604.  
  605. function Replicator:refuel(ignoreLimit)
  606.   if turtle.getFuelLevel() == 'unlimited' then
  607.     return 0
  608.   end
  609.  
  610.   local fuelLevel = turtle.getFuelLevel()
  611.   local fuelLimit = self.config.max_fuel
  612.  
  613.   if ignoreLimit == true then
  614.     fuelLimit = turtle.getFuelLimit()
  615.   end
  616.  
  617.   if fuelLevel > fuelLimit - 80 then -- 1 coal gives 80 fuel
  618.     return 0
  619.   end
  620.  
  621.   local coalStacks = self:inspectAll('coal')
  622.   if table.getn(coalStacks) == 0 then
  623.     if fuelLevel == 0 then
  624.       error('Out of fuel')
  625.     end
  626.     return 0
  627.   end
  628.  
  629.   local function compare(a, b) return a.count < b.count end
  630.   table.sort(coalStacks, compare)
  631.  
  632.   local numWanted = math.ceil((fuelLimit - fuelLevel) / 80)
  633.   local numConsumed = 0
  634.  
  635.   for _,item in ipairs(coalStacks) do
  636.     local consume = math.min(item.count, numWanted - numConsumed)
  637.     if consume > 0 then
  638.       turtle.select(item.slot)
  639.       turtle.refuel(consume)
  640.       numConsumed = numConsumed + consume
  641.     end
  642.   end
  643.  
  644.   return numConsumed
  645. end
  646.  
  647. function Replicator:craft(recipe, amount, move)
  648.   local recipe = Recipe.resolve(recipe)
  649.   if amount == nil then amount = 1 end
  650.   if move == nil then move = true end
  651.  
  652.   local craftSlotMap = {1, 2, 3, 5, 6, 7, 9, 10, 11}
  653.   local storageSlotMap = {4, 8, 12, 13, 14, 15, 16}
  654.  
  655.   local materials = recipe:getMaterials(amount)
  656.   local items = materials:getItems()
  657.  
  658.   if not self:haveMaterials(materials) then
  659.     error('Not enough materials for recipe.')
  660.   end
  661.  
  662.   if materials:numMaterials() > 6 then
  663.     error('Recipe too complex. Max 6 materials.')
  664.   end
  665.  
  666.   for _,item in ipairs(items) do
  667.     if item.count > 64 then
  668.       error('Can not craft using using multiple stacks of the same material.')
  669.     end
  670.   end
  671.  
  672.   if move then
  673.     self:move('y', 2)
  674.   end
  675.  
  676.   -- -- Drop everything but the needed materials in pit/chest
  677.   for _,item in ipairs(self:inspectAll()) do
  678.     if materials.data[item.name] == nil then
  679.       self:drop(item, nil, 'down')
  680.     end
  681.   end
  682.   for _,item in ipairs(items) do
  683.     self:drop(item, self:count(item) - item.count, 'down')
  684.   end
  685.  
  686.   -- Sort the material types in different slots
  687.   for i,item in ipairs(items) do
  688.     local storageSlot = storageSlotMap[i]
  689.     local slotItem  = self:inspect(storageSlot)
  690.     if slotItem and slotItem.name ~= item.name then
  691.       turtle.select(storageSlot)
  692.       turtle.transferTo(self:getEmptySlot())
  693.     end
  694.     self:select(item)
  695.     turtle.transferTo(storageSlot)
  696.   end
  697.  
  698.   -- Arrange recipe
  699.   for i,item in ipairs(items) do
  700.     turtle.select(storageSlotMap[i])
  701.     for slotIdx,slotItem in pairs(recipe.items) do
  702.       if item.name == slotItem.name then
  703.         turtle.transferTo(craftSlotMap[slotIdx], slotItem.count * amount)
  704.       end
  705.     end
  706.   end
  707.  
  708.   -- Finally craft
  709.   turtle.select(1)
  710.   turtle.craft()
  711.  
  712.   -- Pickup dropped items
  713.   while turtle.suckDown() do end
  714.  
  715.   -- Go home
  716.   if move then
  717.     self:move('y', 0)
  718.   end
  719. end
  720.  
  721. function Replicator:smelt(item, fuel)
  722.   local item = Item.resolve(item)
  723.   local fuel = Item.resolve(fuel)
  724.   self:exec [[ Z1 W ]]
  725.   self:drop(fuel, fuel.count)
  726.   self:exec [[ Z2 X-1 ]]
  727.   self:drop(item, item.count, 'down')
  728.   self:exec [[ X0 Z0 X-1 ]]
  729.   sleep(item.count * 10)
  730.   while turtle.suckUp() do end
  731.   self:exec [[ X0 ]]
  732. end
  733.  
  734. function Replicator:retrieve(item, count)
  735.   self:move(self.storage.position)
  736.   self.storage:retrieve(item, count)
  737.   self:move(startingPosition)
  738. end
  739.  
  740. function Replicator:store(item, count)
  741.   self:move(self.storage.position)
  742.   self.storage:store(item, count)
  743.   self:move(startingPosition)
  744. end
  745.  
  746. function Replicator:writePosition()
  747.   local file = fs.open(self.config.position_file, 'w')
  748.   file.write(serpent.dump(self.position))
  749.   file.close()
  750. end
  751.  
  752. function Replicator:writeState()
  753.   local file = fs.open(self.config.state_file, 'w')
  754.   file.write(serpent.dump(self.state))
  755.   file.close()
  756. end
  757.  
  758. function Replicator:haveMaterials(materials)
  759.   local materials = Materials.resolve(materials)
  760.   for name,count in pairs(materials.data) do
  761.     local have = self:count(name)
  762.     if self.state.have_base then
  763.       have = have + self.storage:count(name)
  764.     end
  765.     if have < count then
  766.       return false
  767.     end
  768.   end
  769.   return true
  770. end
  771.  
  772. function Replicator:prepareMaterials(materials)
  773.   local materials = Materials.resolve(materials)
  774.   for _,item in ipairs(materials:getItems()) do
  775.     local inInventory = self:count(item)
  776.     local inStorage = self.storage:count(item)
  777.     if item.count > inInventory then
  778.       self:move(self.storage.position)
  779.       self.storage:retrieve(item, item.count - inInventory)
  780.     end
  781.   end
  782.   self:move(startingPosition)
  783. end
  784.  
  785. function Replicator:compactInventory()
  786.   for targetIdx = 1,16 do
  787.     local target = self:inspect(targetIdx)
  788.     if target then
  789.       for slotIdx = targetIdx+1,16 do
  790.         local slotItem = self:inspect(slotIdx)
  791.         if slotItem and slotItem.name == target.name and turtle.getItemSpace(targetIdx) > 0 then
  792.           turtle.select(slotIdx)
  793.           turtle.transferTo(targetIdx)
  794.         end
  795.       end
  796.     end
  797.   end
  798. end
  799.  
  800. function Replicator:inventoryCleaning()
  801.   for _,item in ipairs(inventoryMaterials:getItems()) do
  802.     local itemCount = self:count(item)
  803.     local materialCount = inventoryMaterials:count(item)
  804.     if itemCount > materialCount then
  805.       if item.name == 'minecraft:coal' then
  806.         itemCount = itemCount - self:refuel(true)
  807.       end
  808.       self:drop(item, itemCount - materialCount, 'down')
  809.     end
  810.   end
  811.  
  812.   for _,item in ipairs(self:inspectAll()) do
  813.     if item.name == 'minecraft:sapling' and (item.metadata == 5 or item.metadata == 3) then
  814.       self:drop(item, nil, 'down')
  815.     end
  816.     if unwantedMaterials:count(item) > 0 then
  817.       self:drop(item, nil, 'down')
  818.     end
  819.     local maxStored = storageMaterials:count(item)
  820.     if maxStored > 1 then
  821.       local toDrop = self.storage:count(item) + item.count - maxStored
  822.       if toDrop > 0 then
  823.         self:drop(item, toDrop, 'down')
  824.       end
  825.     end
  826.   end
  827.  
  828.   if comparePosition(self.position, startingPosition) then
  829.     for _,item in ipairs(storageMaterials:getItems()) do
  830.       local itemCount = self:count(item)
  831.       if itemCount > 0 then
  832.         self:move(self.storage.position)
  833.         self.storage:store(item, itemCount)
  834.       end
  835.     end
  836.     self:move(startingPosition)
  837.  
  838.     for _,item in ipairs(self:inspectAll()) do
  839.       if not inventoryMaterials.data[item.name] then
  840.         self:drop(item, nil, 'down')
  841.       end
  842.     end
  843.   end
  844. end
  845.  
  846. function Replicator:drawBackground()
  847.   local w, h = term.getSize()
  848.   for x = 1,w do
  849.     for y = 1,h do
  850.       if math.random() < 0.3 then
  851.         term.setCursorPos(x, y)
  852.         if math.random() < 0.5 then
  853.           term.write('1')
  854.         else
  855.           term.write('0')
  856.         end
  857.       end
  858.     end
  859.   end
  860. end
  861.  
  862. function Replicator:drawStartingScreen()
  863.   local w, h = term.getSize()
  864.   local status = {}
  865.   local materials = Materials.resolve(startingMaterials)
  866.   for name,count in pairs(materials.data) do
  867.     local have = self:count(name)
  868.     if count > have then
  869.       table.insert(status, '  ' .. Item.fromString(name):displayName())
  870.       table.insert(status, '                ' .. count - have)
  871.     end
  872.   end
  873.  
  874.   term.clear()
  875.   term.setCursorPos(w, h)
  876.   textutils.tabulate(status)
  877.  
  878.   term.setCursorPos(3, 2)
  879.   term.write('INSERT MATERIALS')
  880. end
  881.  
  882. function Replicator:findBaseSpot()
  883.   local foundBase = false
  884.   local walk = RandomWalk:new()
  885.  
  886.   self:move('z', -3)
  887.  
  888.   while not foundBase do
  889.     local pos = walk:next()
  890.     if pos == false then
  891.       walk = RandomWalk:new(walk.position)
  892.       pos = walk:next()
  893.     end
  894.  
  895.     local x = pos.x * self.config.base_spacing
  896.     local y = pos.y * self.config.base_spacing
  897.  
  898.     self:move('x', x - 1)
  899.     self:move('y', y)
  900.     self:move('z', -2)
  901.  
  902.     if not self:detect('cobblestone', 'up') then
  903.       foundBase = true
  904.       self:move('x', x)
  905.       self:move('z', -1)
  906.     else
  907.       self:move('z', -3)
  908.     end
  909.   end
  910. end
  911.  
  912. function Replicator:buildBase()
  913.   self:exec [[
  914.     N !cobblestone P L P L L U N F F D PD P E P W P U F F D PD F PD L F PD
  915.     L F PD P R P U F R F PD F R F !dirt PD F PD !cobblestone F R PD F PD F PD F
  916.     PD R F R F !water_bucket PD L F R F PD F U R !reeds PD F PD F D
  917.     !cobblestone F PD F PD F R F PD F PD F PD F R F PD F PD R F PD F PD F PD R
  918.     !dirt F PD R F PD F PD R R U !sapling PD F PD F PD L F F D L F F F R F F F N
  919.   ]]
  920.   self.position = copy(startingPosition)
  921.   self:writePosition()
  922.   self:exec [[ Y2 ]]
  923.   self:craft(Recipes.planks, 7, false)
  924.   self:craft(Recipes.chest, 3, false)
  925.   self:exec [[ Y2 !chest PD Y0 X1 PU PD X0 ]]
  926.   self:craft(Recipes.furnace, 1)
  927.   self:exec [[ W !furnace U P D N ]]
  928.   if self:count('coal') == 0 then
  929.     self:smelt('log', 'planks')
  930.   end
  931.   self:exec [[ Y2 ]]
  932.   self:craft(Recipes.stick, 1, false)
  933.   self:craft(Recipes.torch, 1, false)
  934.   self:exec [[ Y6 Z1 X-1 !torch PD F PD F PD F F Y0 E F F PD F D X0 ]]
  935.   self.state.have_base = true
  936.   self:writeState()
  937. end
  938.  
  939. function Replicator:logRefuel()
  940.   local minLogs = 10
  941.   local numLogs = self.storage:count('log')
  942.   if turtle.getFuelLevel() < 80 then
  943.     minLogs = 3
  944.   end
  945.   if numLogs >= minLogs then
  946.     numLogs = math.min(numLogs, 10)
  947.     self:retrieve('log', numLogs)
  948.     self:smelt('log', 'log')
  949.     if numLogs - 2 > 1 then
  950.       self:smelt({'log', numLogs - 2}, 'coal')
  951.     end
  952.     self:refuel()
  953.   end
  954. end
  955.  
  956. function Replicator:mine(shafts)
  957.   if not shafts then shafts = 1 end
  958.  
  959.   function spinmine()
  960.     for i=1,4 do
  961.       local block = self:inspect()
  962.       if block then
  963.         if miningMaterials:count(block) > 0 or
  964.           (self:count('cobblestone') < 124 and block.name == 'minecraft:stone') or
  965.           (self:count('dirt') < 32 and block.name == 'minecraft:dirt') then
  966.           turtle.dig()
  967.         end
  968.       end
  969.       if i ~= 4 then
  970.         self:turn('left')
  971.       end
  972.     end
  973.     if self.position.z % 8 == 0 then
  974.       self:inventoryCleaning()
  975.       self:compactInventory()
  976.     end
  977.   end
  978.  
  979.   local moveHeight = -math.random(5, 10)
  980.   local area = math.floor(self.config.mining_area / 5)
  981.   local tries = 0
  982.  
  983.   self:move('z', moveHeight)
  984.   self:move('x', math.random(-area, area) * 5)
  985.   self:move('y', math.random(-area, area) * 5)
  986.  
  987.   for _ = 1,shafts do
  988.     local foundSpot = false
  989.     repeat
  990.       while not self:detect(nil, 'down') or self:detectAny(unwantedMaterials, 'down') do
  991.         self:simpleMove('down')
  992.       end
  993.       if self:detect('cobblestone', 'down') then
  994.         tries = tries + 1
  995.         if tries > 20 then
  996.           self.state.mining_fails = self.state.mining_fails + 1
  997.           self:writeState()
  998.           return
  999.         end
  1000.         self:simpleMove('up', 2)
  1001.         if math.random() < 0.5 then
  1002.           if math.random() < 0.5 then
  1003.             self:turn('left')
  1004.           else
  1005.             self:turn('right')
  1006.           end
  1007.         end
  1008.         self:exec [[ F F L F R ]]
  1009.       else
  1010.         foundSpot = true
  1011.       end
  1012.     until foundSpot
  1013.  
  1014.     self:simpleMove('down')
  1015.     for i = 1,4 do
  1016.       turtle.dig()
  1017.       self:place('cobblestone')
  1018.       self:turn('left')
  1019.     end
  1020.     self:simpleMove('down')
  1021.     self:place('cobblestone', 'up')
  1022.  
  1023.     local startZ = self.position.z
  1024.     local atBottom = false
  1025.     while not atBottom do
  1026.       local below = self:inspect('down')
  1027.       if isTurtle(below) then
  1028.         return
  1029.       end
  1030.       if below and below.name == 'minecraft:bedrock' then
  1031.         atBottom = true
  1032.       else
  1033.         self:simpleMove('down')
  1034.         spinmine()
  1035.       end
  1036.     end
  1037.  
  1038.     self:exec [[ !cobblestone U PD U PD U PD U PD U PD ]]
  1039.  
  1040.     foundSpot = false
  1041.     repeat
  1042.       if math.random() < 0.5 then
  1043.         if math.random() < 0.5 then
  1044.           self:turn('left')
  1045.         else
  1046.           self:turn('right')
  1047.         end
  1048.       end
  1049.       self:exec [[ F F L F R ]]
  1050.       while not self:detectAny({'cobblestone', 'bedrock'}, 'down') do
  1051.         self:simpleMove('down')
  1052.       end
  1053.       if not self:detect('cobblestone', 'down') then
  1054.         foundSpot = true
  1055.       else
  1056.         tries = tries + 1
  1057.         if tries > 20 then
  1058.           self.state.mining_fails = self.state.mining_fails + 1
  1059.           self:writeState()
  1060.           return
  1061.         end
  1062.       end
  1063.     until foundSpot
  1064.  
  1065.     while not self:detect('bedrock', 'down') do
  1066.       self:simpleMove('down')
  1067.     end
  1068.  
  1069.     local numCobble = 0
  1070.     while self.position.z < startZ do
  1071.       spinmine()
  1072.       self:simpleMove('up')
  1073.       if numCobble < 6 then
  1074.         self:exec [[ !cobblestone PD ]]
  1075.         numCobble = numCobble + 1
  1076.       end
  1077.     end
  1078.  
  1079.     self:simpleMove('up')
  1080.     for i = 1,4 do
  1081.       turtle.dig()
  1082.       self:place('cobblestone')
  1083.       self:turn('left')
  1084.     end
  1085.     self:simpleMove('up')
  1086.     self:place('cobblestone', 'down')
  1087.   end
  1088.  
  1089.   self:move('z', moveHeight)
  1090.   self:exec [[ X0 Y0 Z0 ]]
  1091. end
  1092.  
  1093. function Replicator:findSand()
  1094.   self:move('z', -5)
  1095.   self:move('x', math.random(-10, 10))
  1096.   self:move('y', math.random(-10, 10))
  1097.   while not self:inspect('down') do
  1098.     self:simpleMove('down')
  1099.   end
  1100.  
  1101.   local pos = {x=0, y=0}
  1102.   local gridSize = 10
  1103.   local visited = {}
  1104.   local foundSand = false
  1105.   local numSteps = 0
  1106.   local numTries = 0
  1107.  
  1108.   visited[pos.x .. pos.y] = true
  1109.  
  1110.   while not foundSand and numSteps < 200 and numTries < 20 do
  1111.     local axis = 'x'
  1112.     if math.random() > 0.5 then axis = 'y' end
  1113.     local dir = 1
  1114.     if math.random() > 0.5 then dir = -1 end
  1115.     local nextPos = {x=pos.x, y=pos.y}
  1116.     nextPos[axis] = nextPos[axis] + dir
  1117.     if not visited[nextPos.x .. nextPos.y] then
  1118.       numTries = 0
  1119.       pos = nextPos
  1120.       visited[pos.x .. pos.y] = true
  1121.       for _ = 1,gridSize do
  1122.         local stepsDown = 0
  1123.         local onGround = false
  1124.         while not onGround do
  1125.           local below = self:inspect('down')
  1126.           if below and below.name == 'minecraft:sand' then
  1127.             foundSand = true
  1128.           end
  1129.           if (not below or unwantedMaterials:count(below) > 0) and stepsDown < 6 then
  1130.             self:simpleMove('down')
  1131.             numSteps = numSteps + 1
  1132.             stepsDown = stepsDown + 1
  1133.           else
  1134.             onGround = true
  1135.           end
  1136.         end
  1137.         if foundSand then break end
  1138.         local inFront = self:inspect()
  1139.         while inFront and unwantedMaterials:count(inFront) == 0 do
  1140.           self:simpleMove('up')
  1141.           numSteps = numSteps + 1
  1142.           inFront = self:inspect()
  1143.         end
  1144.         self:move(axis, self.position[axis] + 1)
  1145.         numSteps = numSteps + 1
  1146.       end
  1147.     else
  1148.       numTries = numTries + 1
  1149.     end
  1150.   end
  1151.  
  1152.   local numBacktracks = 0
  1153.   while foundSand do
  1154.     while self:detect('sand', 'down') do
  1155.       self:simpleMove('down')
  1156.     end
  1157.     while self:detect('sand') do
  1158.       numBacktracks = 0
  1159.       turtle.dig() sleep(0.5)
  1160.       if self:count('sand') >= 64 then
  1161.         foundSand = false
  1162.         break
  1163.       end
  1164.     end
  1165.     self:simpleMove()
  1166.     local turns = 0
  1167.     while not self:detect('sand') do
  1168.       self:turn('right')
  1169.       turns = turns + 1
  1170.       if turns > 2 then
  1171.         if numBacktracks < 2 then
  1172.           self:turn('left')
  1173.           self:simpleMove()
  1174.           numBacktracks = numBacktracks + 1
  1175.         else
  1176.           foundSand = false
  1177.         end
  1178.         break
  1179.       end
  1180.     end
  1181.   end
  1182.  
  1183.   self:exec [[ Z-7 X0 Y0 Z0 ]]
  1184. end
  1185.  
  1186. function Replicator:lumberjack()
  1187.   local saplingCount = self.storage:count('sapling')
  1188.   if saplingCount > 0 then
  1189.     self:retrieve('sapling', math.min(3, saplingCount))
  1190.   end
  1191.   self:exec [[ Y4 ]]
  1192.   for pos=1,3 do
  1193.     self:move('x', -pos)
  1194.     self:turn(cardinal.north)
  1195.     if self:detect('log') then
  1196.       local leafStart, above
  1197.       local height = 0
  1198.       repeat
  1199.         above = self:detect(nil, 'up')
  1200.         height = height + 1
  1201.         self:simpleMove('up')
  1202.         if leafStart == nil and above then
  1203.           leafStart = math.max(height, 2)
  1204.         end
  1205.       until not above and not self:detect()
  1206.       if not leafStart then leafStart = height end
  1207.       self:exec [[ F ]]
  1208.       while self.position.z > leafStart do
  1209.         self:exec [[ F D Q R F Q R F F F Q R F F Q R F F Q R F L ]]
  1210.       end
  1211.       while self.position.z > 2 do
  1212.         self:simpleMove('down')
  1213.       end
  1214.       self:exec [[ D QD !sapling PD S F D ]]
  1215.     elseif not self:detect('sapling') then
  1216.       self:exec [[ !sapling P ]]
  1217.     end
  1218.   end
  1219.   self:exec [[ X0 Y0 ]]
  1220.   if self.storage:count('sapling') > 60 then
  1221.     self:drop('sapling', nil, 'down')
  1222.   end
  1223.   self.lastLumberTrip = os.clock()
  1224. end
  1225.  
  1226. function Replicator:reedsjack()
  1227.   self:exec [[ Z2 Y1 X-3 W Q D Q Y2 Z2 W Q D Q Z0 X0 Y0 ]]
  1228.   self.lastReedsTrip = os.clock()
  1229. end
  1230.  
  1231. function Replicator:buildDiskDrive()
  1232.   self:smelt({'cobblestone', 7}, {'coal', 1})
  1233.   self:exec [[ Y2 ]]
  1234.   self:craft(Recipes.diskDrive, 1, false)
  1235.   self:exec [[ Y2 E !ComputerCraft:CC-Peripheral P Y0 ]]
  1236.   self.state.have_disk_drive = true
  1237.   self:writeState()
  1238. end
  1239.  
  1240. function Replicator:buildFloppy()
  1241.   self:move('y', 2)
  1242.   self:craft(Recipes.paper, 1, false)
  1243.   self:craft(Recipes.floppyDisk, 1, false)
  1244.   self:exec [[ Y2 E ]]
  1245.   self:drop('ComputerCraft:disk', 1)
  1246.   self:setupFloppy('front')
  1247.   self.state.have_floppy = true
  1248.   self:writeState()
  1249.   self:move(startingPosition)
  1250. end
  1251.  
  1252. function Replicator:setupFloppy(direction)
  1253.   local deps = {
  1254.     'lib/class.lua',
  1255.     'lib/items.lua',
  1256.     'lib/serpent.lua',
  1257.     'lib/utils.lua',
  1258.   }
  1259.  
  1260.   -- Sometimes the disk drive won't show up
  1261.   local diskTries = 0
  1262.   local diskPath = nil
  1263.   while diskPath == nil do
  1264.     if diskTries > 20 then
  1265.       error('Unable to access disk drive.')
  1266.     end
  1267.     diskPath = disk.getMountPath(direction)
  1268.     if diskTries > 10 then
  1269.       self:simpleMove('up')
  1270.       sleep(1)
  1271.       self:simpleMove('down')
  1272.       sleep(10)
  1273.     end
  1274.     sleep(2)
  1275.     diskTries = diskTries + 1
  1276.   end
  1277.  
  1278.   disk.setLabel(direction, 'rEpliCatoR v.' .. self.state.num_babies)
  1279.  
  1280.   fs.makeDir(diskPath .. '/lib')
  1281.   fs.makeDir(diskPath .. '/state')
  1282.  
  1283.   for _,dep in ipairs(deps) do
  1284.     local filename = diskPath .. '/' .. dep
  1285.     fs.delete(filename)
  1286.     fs.copy(dep, filename)
  1287.   end
  1288.  
  1289.   fs.delete(diskPath .. '/startup')
  1290.   fs.copy('bootstrap', diskPath .. '/startup')
  1291.  
  1292.   local startupSource
  1293.   if fs.exists('replicator') then
  1294.     startupSource = 'replicator'
  1295.   else
  1296.     startupSource = 'startup'
  1297.   end
  1298.  
  1299.   fs.delete(diskPath .. '/replicator')
  1300.   fs.copy(startupSource, diskPath .. '/replicator')
  1301.  
  1302.   local nameFile = fs.open(diskPath .. '/' .. self.config.name_file, 'w')
  1303.   local name = os.getComputerLabel()
  1304.   if name == nil then
  1305.     name = 'r'
  1306.   end
  1307.  
  1308.   nameFile.writeLine(name .. '-' .. self.state.num_babies)
  1309.   nameFile.close()
  1310. end
  1311.  
  1312. function Replicator:updateFloppy()
  1313.   self:exec [[ Y2 E ]]
  1314.   self:setupFloppy('front')
  1315.   self:move(startingPosition)
  1316. end
  1317.  
  1318. function Replicator:isInBase()
  1319.   return comparePosition(self.position, startingPosition)
  1320. end
  1321.  
  1322. function Replicator:haveEnoughFuel()
  1323.   return turtle.getFuelLevel() > self.config.max_fuel * self.config.refuel_treshhold
  1324. end
  1325.  
  1326. function Replicator:loop()
  1327.   self:drawBackground()
  1328.   self:compactInventory()
  1329.  
  1330.   if not self.state.have_base then
  1331.     while not self:haveMaterials(startingMaterials) do
  1332.       self:drawStartingScreen()
  1333.       sleep(0.5)
  1334.     end
  1335.  
  1336.     for _ = 1,20 do
  1337.       self:drawBackground()
  1338.       sleep(0.1)
  1339.     end
  1340.  
  1341.     self:select('crafting_table')
  1342.     turtle.equipLeft()
  1343.  
  1344.     self:select('diamond_pickaxe')
  1345.     turtle.equipRight()
  1346.  
  1347.     self:refuel()
  1348.  
  1349.     local haveParent = fs.exists(self.config.name_file)
  1350.     if haveParent then
  1351.       -- Figure out bearing since turtles are placed differently
  1352.       -- depending on the world orientation of the parent.
  1353.       while not self:detect('ComputerCraft:CC-Peripheral') do
  1354.         self:turn('left')
  1355.       end
  1356.  
  1357.       self.position = {x=1, y=1, z=0, bearing=cardinal.north}
  1358.       self:writePosition()
  1359.       self:findBaseSpot()
  1360.     else
  1361.       self:simpleMove('up', self.config.base_height)
  1362.     end
  1363.  
  1364.     self:buildBase()
  1365.     return
  1366.   end
  1367.  
  1368.   if not self:isInBase() then
  1369.     -- Not at home position at loop start means we lost power
  1370.     if self.position.z < 0 then
  1371.       -- Below platform, should be ok just to plow our way home
  1372.       self:exec [[ Z-8 Y0 X0 Z0 ]]
  1373.     else
  1374.       -- In base probably, navigate more carefully
  1375.       while self.position.z < 8 do
  1376.         while self:inspect('up') do
  1377.           while self:inspect() do
  1378.             self:turn('left')
  1379.           end
  1380.           self:simpleMove()
  1381.         end
  1382.         self:simpleMove('up')
  1383.       end
  1384.       self:exec [[ X0 Y0 Z0 ]]
  1385.     end
  1386.     return
  1387.   end
  1388.  
  1389.   self:inventoryCleaning()
  1390.   self:compactInventory()
  1391.  
  1392.   if self.storage:count('log') < 120 or self.storage:count('sapling') < 16 then
  1393.     if os.clock() - self.lastLumberTrip > self.config.lumberjack_interval then
  1394.       self:lumberjack()
  1395.       return
  1396.     end
  1397.   end
  1398.  
  1399.   if not self:haveEnoughFuel() then
  1400.     if self:count('coal') > 16 then
  1401.       self:refuel()
  1402.     else
  1403.       self:logRefuel()
  1404.     end
  1405.   end
  1406.  
  1407.   if not self:haveEnoughFuel() then
  1408.     self:logRefuel()
  1409.     sleep(2)
  1410.     return
  1411.   end
  1412.  
  1413.   if self.storage:count('reeds') < 20 then
  1414.     if os.clock() - self.lastReedsTrip > self.config.reeds_interval then
  1415.       self:reedsjack()
  1416.       return
  1417.     end
  1418.   end
  1419.  
  1420.   if self.state.mining_fails > 5 and self:haveMaterials(relocatingMaterials) then
  1421.     local optionalMaterials = Materials.fromTable {
  1422.       'diamond', 'iron_ore', 'sand', 'redstone', 'diamond_pickaxe'
  1423.     }
  1424.     self:move(self.storage.position)
  1425.     local items = concat(relocatingMaterials:getItems(), optionalMaterials:getItems())
  1426.     for _,item in ipairs(items) do
  1427.       local count = self.storage:count(item)
  1428.       if count > 0 then
  1429.         self.storage:retrieve(item, math.min(count, 64))
  1430.       end
  1431.     end
  1432.     self:move(startingPosition)
  1433.     if self:count('cobblestone') > 64 then
  1434.       self:drop('cobblestone', self:count('cobblestone') - 64, 'down')
  1435.     end
  1436.     self.storage:reset()
  1437.     self.state.mining_fails = 0
  1438.     self.state.have_base = false
  1439.     self.state.have_baby = false
  1440.     self.state.have_floppy = false
  1441.     self.state.have_disk_drive = false
  1442.     self:writeState()
  1443.     self:exec [[ !dirt D PU D PU ]]
  1444.     self:findBaseSpot()
  1445.     self:buildBase()
  1446.     return
  1447.   end
  1448.  
  1449.   if self.storage:count('sand') < 6 and self.storage:count('glass_pane') == 0 then
  1450.     self:findSand()
  1451.     return
  1452.   end
  1453.  
  1454.   if not self.state.have_disk_drive then
  1455.     local driveMaterials = {{'cobblestone', 7}, {'redstone', 2}, {'coal', 1}}
  1456.     if self:haveMaterials(driveMaterials) then
  1457.       self:prepareMaterials(driveMaterials)
  1458.       self:buildDiskDrive()
  1459.     else
  1460.       self:mine(1)
  1461.     end
  1462.     return
  1463.   end
  1464.  
  1465.   if not self.state.have_floppy then
  1466.     local floppyMaterials = {{'redstone', 1}, {'reeds', 3}}
  1467.     if self:haveMaterials(floppyMaterials) then
  1468.       self:prepareMaterials(floppyMaterials)
  1469.       self:buildFloppy()
  1470.     else
  1471.       self:mine(1)
  1472.     end
  1473.     return
  1474.   end
  1475.  
  1476.   if self:haveMaterials{{'iron_ore', 8}, 'coal'} and self.storage:count('iron_ingot') < 16 then
  1477.     self:prepareMaterials{{'iron_ore', 8}, 'coal'}
  1478.     self:smelt({'iron_ore', 8}, 'coal')
  1479.     self:store('iron_ingot', 8)
  1480.     return
  1481.   end
  1482.  
  1483.   if self:haveMaterials{{'sand', 6}, {'coal', 1}} and self.storage:count('glass_pane') < 1 then
  1484.     self:prepareMaterials{{'sand', 6}, {'coal', 1}}
  1485.     self:smelt({'sand', 6}, {'coal', 1})
  1486.     self:craft(Recipes.glassPane, 1)
  1487.     return
  1488.   end
  1489.  
  1490.   local computerMaterials = {{'cobblestone', 7}, 'redstone', 'glass_pane', 'coal'}
  1491.   if self:haveMaterials(computerMaterials) and self.storage:count('ComputerCraft:CC-Computer') < 1 then
  1492.     self:prepareMaterials(computerMaterials)
  1493.     self:smelt({'cobblestone', 7}, 'coal')
  1494.     self:craft(Recipes.computer)
  1495.     self:store('ComputerCraft:CC-Computer')
  1496.     return
  1497.   end
  1498.  
  1499.   if self.state.have_baby == false then
  1500.     local turtleMaterials = {'ComputerCraft:CC-Computer', {'log', 2}, {'iron_ingot', 7}}
  1501.     if self:haveMaterials(turtleMaterials) then
  1502.       self:prepareMaterials(turtleMaterials)
  1503.       self:move('y', 2)
  1504.       self:craft(Recipes.planks, 2, false)
  1505.       self:craft(Recipes.chest, 1, false)
  1506.       self:craft(Recipes.turtle, 1, false)
  1507.       self:exec [[ Y1 E !ComputerCraft:CC-Turtle P ]]
  1508.       self:move(startingPosition)
  1509.       self.state.have_baby = true
  1510.       self.state.num_babies = self.state.num_babies + 1
  1511.       self:writeState()
  1512.       return
  1513.     end
  1514.   end
  1515.  
  1516.   if self:haveMaterials{{'bucket', 2}} and self.storage:count('water_bucket') < 2 then
  1517.     self:prepareMaterials{{'bucket', 2}}
  1518.     self:exec [[ Y1 X-2 !bucket PD X-3 PD X0 Y0 ]]
  1519.     return
  1520.   end
  1521.  
  1522.   if self.state.have_baby == true then
  1523.     if self:haveMaterials{{'iron_ingot', 6}} and self.storage:count('bucket') < 2 then
  1524.       self:prepareMaterials{{'iron_ingot', 6}}
  1525.       self:craft(Recipes.bucket, 2)
  1526.       return
  1527.     end
  1528.     if self:haveMaterials{'log', {'diamond', 3}} and self.storage:count('diamond_pickaxe') < 1 then
  1529.       self:prepareMaterials{'log', {'diamond', 3}}
  1530.       self:move('y', 2)
  1531.       self:craft(Recipes.planks, 1, false)
  1532.       self:craft(Recipes.stick, 2, false)
  1533.       self:craft(Recipes.diamondPick, 1, false)
  1534.       self:move('y', 0)
  1535.       return
  1536.     end
  1537.     if self:haveMaterials{'log'} and self.storage:count('crafting_table') < 1 then
  1538.       self:prepareMaterials{'log'}
  1539.       self:move('y', 2)
  1540.       self:craft(Recipes.planks, 1, false)
  1541.       self:craft(Recipes.craftingTable, 1, false)
  1542.       self:move('y', 0)
  1543.       return
  1544.     end
  1545.     if self:haveMaterials(startingMaterials) then
  1546.       self:updateFloppy()
  1547.       self:prepareMaterials(startingMaterials)
  1548.       self:exec [[ Y1 E ]]
  1549.       for _,item in ipairs(startingMaterials:getItems()) do
  1550.         self:drop(item, item.count)
  1551.       end
  1552.       -- give extra starting materials if possible
  1553.       local extraMaterials = Materials:new({})
  1554.       local checkMaterials = Materials.fromTable {
  1555.         {'sand', 12}, {'log', 32}, {'diamond', 6}, {'iron_ore', 16},
  1556.         {'redstone', 16}, {'reeds', 6}, {'coal', 32}, {'sapling', 16},
  1557.       }
  1558.  
  1559.       for _,item in ipairs(checkMaterials:getItems()) do
  1560.         local inStorage = self.storage:count(item)
  1561.         local maxGive = 64
  1562.         if inStorage > item.count then
  1563.           maxGive = 64 - startingMaterials:count(item)
  1564.           extraMaterials:addItem(item, math.min(maxGive, math.floor(inStorage / 2)))
  1565.         end
  1566.       end
  1567.  
  1568.       if extraMaterials:numMaterials() > 0 then
  1569.         self:exec [[ Y0 ]]
  1570.         self:prepareMaterials(extraMaterials)
  1571.         self:exec [[ Y1 E ]]
  1572.         for _,item in ipairs(extraMaterials:getItems()) do
  1573.           self:drop(item, item.count)
  1574.         end
  1575.       end
  1576.  
  1577.       peripheral.call('front', 'turnOn')
  1578.       self.state.have_baby = false
  1579.       self:writeState()
  1580.       self:move(startingPosition)
  1581.       return
  1582.     end
  1583.   end
  1584.  
  1585.   self:mine(math.random(1, 2))
  1586. end
  1587.  
  1588. function Replicator:run()
  1589.   self.running = true
  1590.   while self.running do
  1591.     local status, err = pcall(self.loop, self)
  1592.     if not status then
  1593.       local h = fs.open(self.config.error_log, 'a')
  1594.       h.writeLine(err)
  1595.       h.close()
  1596.  
  1597.       term.clear()
  1598.       term.setCursorPos(3, 2)
  1599.       term.write('ERROR')
  1600.       term.setCursorPos(3, 5)
  1601.       term.write(err)
  1602.       term.setCursorPos(1, 7)
  1603.  
  1604.       self:stop()
  1605.  
  1606.       if turtle.getFuelLevel() == 0 then
  1607.         os.shutdown()
  1608.       else
  1609.         sleep(600)
  1610.         os.reboot()
  1611.       end
  1612.     end
  1613.   end
  1614. end
  1615.  
  1616. function Replicator:stop()
  1617.   self.running = false
  1618. end
  1619.  
  1620. term.setBackgroundColor(colors.white)
  1621. term.setTextColor(colors.black)
  1622. term.setCursorBlink(false)
  1623. term.clear()
  1624.  
  1625. replicator = Replicator:new()
  1626. replicator:run()
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top