vx13

bin/rubber-farm.lua

Feb 12th, 2017
233
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- IC2 Rubber Farm v0.4
  2.  
  3. local component = require "component"
  4. local computer = require "computer"
  5. local robot = require "erobot"
  6. local shell = require "shell"
  7. local sides = require "sides"
  8. local event = require "event"
  9.  
  10. require "table_io"
  11.  
  12. local pathfinder = require "pathfinder"
  13. local inv
  14. local geolyzer
  15.  
  16. -- farmMap metatable
  17. -- Use farmMapArr() as constructor, v(x,y,z) as acces and v:setTree(x, z, h) as set value
  18. if not farmMapArr then farmMapArr = function() return setmetatable({
  19.   treeData = { },  
  20.   setTree = function(t, x, z, h)
  21.     t.treeData[z] = t.treeData[z] or {}
  22.     t.treeData[z][x] = h
  23.   end,
  24.   }, { __call = function(t, x, y, z)
  25.     if y == 0 then
  26.       return (x ~= 0) or (z ~= 0)
  27.     end
  28.     if not t.treeData[z] or not t.treeData[z][x] then return nil end
  29.     return y <= t.treeData[z][x]
  30.   end
  31. })end end
  32.  
  33. local farmMap
  34. local farmPoints
  35. local maxTry = 20
  36. local SLEEP_TIME = 60 * 5
  37. local RUB_WOOD = "IC2:blockRubWood"
  38. local RUB_SAPLING = "IC2:blockRubSapling"
  39. local ETREETAP_USE_COST = 50
  40. local ETREETAP_CAP = 10000
  41. local MIN_ENERGY = 500
  42. local START_ENERGY = 19000
  43.  
  44. function sleep(timeout)
  45.   checkArg(1, timeout, "number", "nil")
  46.   local deadline = computer.uptime() + (timeout or 0)
  47.   repeat
  48.     local ev = event.pull(deadline - computer.uptime(), "interrupt")
  49.     if ev ~= nil then
  50.       robot.rotateTo(robot.getStartOrientation())
  51.       os.exit(1)
  52.     end
  53.   until computer.uptime() >= deadline
  54. end
  55.  
  56. function checkSide(side)
  57.   return side and side >= 2 and side <= 5
  58. end
  59.  
  60. function swingmove(direction)
  61.   for try = 1, maxTry do
  62.     if checkEnergy() then
  63.       return false, "service"
  64.     end
  65.     if component.robot.detect(direction) then
  66.       local ok, substance = component.robot.swing(direction) --air, block, entity
  67.       if not ok and (substance ~= "air") then
  68.         return false, "fatal barrier ("..tostring(substance)..")"
  69.       end
  70.     else
  71.       local ok, err = component.robot.move(direction)
  72.       if ok then
  73.         return true
  74.       elseif err == "impossible move" then
  75.         return false, err
  76.       end
  77.     end
  78.   end
  79.   return false, "movement limit"
  80. end
  81.  
  82. function moveTo(x, y, z, noabort)
  83.   local rX, rY, rZ = robot.toRealCoord(x, y, z)
  84.   while 1 do
  85.     local ok, err = robot.moveTo(rX, rY, rZ, swingmove)
  86.     if ok then
  87.       return
  88.     end
  89.     if noabort then
  90.       print(err)
  91.       sleep(5)
  92.     else
  93.       if err == "service" then
  94.         service()
  95.       else
  96.         emergency(err)
  97.       end
  98.     end
  99.   end
  100. end
  101.  
  102. function moveToF(x, y, z, noabort)
  103.   local pos = { }
  104.   pos.x, pos.y, pos.z = robot.toVirtCoord(robot.getPos())
  105.   local crumb = pathfinder.findPath(farmMap, pos, { x = x, y = y, z = z })
  106.   if crumb == nil then
  107.     emergency('Path not found: '..'['.. pos.x..","..pos.y..","..pos.z.."]->"..'['.. p.x..","..p.y..","..p.z.."]")
  108.   else
  109.     while crumb.next ~= nil do
  110.       crumb = crumb.next
  111.       moveTo(crumb.pos.x, crumb.pos.y, crumb.pos.z, noabort)
  112.     end
  113.   end
  114. end
  115.  
  116. function go_home()
  117.   moveToF(0, 0, 0, true)
  118.   robot.rotateTo(robot.getStartOrientation())
  119. end
  120.  
  121. function checkEnergy(l)
  122.   l = l or MIN_ENERGY
  123.   return computer.energy() < l
  124. end
  125.  
  126. function service()
  127.   local x, y, z = robot.toVirtCoord(robot.getPos())
  128.   local d = robot.getOrientation()
  129.   go_home()
  130.   while checkEnergy(START_ENERGY) do
  131.     sleep(10)
  132.   end
  133.   moveToF(x, y, z)
  134.   robot.rotateTo(d)
  135. end
  136.  
  137. function emergency(err)
  138.   error("emergency: "..err)
  139. end
  140.  
  141. function clear_inventory()
  142.   for slot = 1, robot.inventorySize() do
  143.     robot.select(slot)
  144.     robot.dropDown()
  145.   end
  146.   robot.select(1)
  147. end
  148.  
  149. function check_tool(energy)
  150.   return robot.durability() * ETREETAP_CAP < energy
  151. end
  152.  
  153. function charge_tool(energy)
  154.   while check_tool(energy) do
  155.     robot.turnRight()
  156.     inv.equip()
  157.     inv.dropIntoSlot(sides.front, 1)
  158.     sleep(10)
  159.     inv.suckFromSlot(sides.front, 1)
  160.     inv.equip()
  161.     robot.turnLeft()
  162.   end
  163. end
  164.  
  165. local nedded_check_use_cost = false
  166.  
  167. function treetap_use()
  168.   if not nedded_check_use_cost then
  169.     robot.use()
  170.     return
  171.   end
  172.   local e1 = robot.durability() * ETREETAP_CAP
  173.   robot.use()
  174.   local e2 = robot.durability() * ETREETAP_CAP
  175.   local e = e2 - e1
  176.   if e < ETREETAP_USE_COST then
  177.     ETREETAP_USE_COST = e
  178.   end
  179.   nedded_check_use_cost = false
  180. end
  181.  
  182. -- 3, 9  - south
  183. -- 2, 8  - north
  184. -- 5, 11 - east
  185. -- 4, 10 - west
  186.  
  187. --            z
  188. --           (-)
  189. --          north
  190. -- x (-) west   east (+) x
  191. --          south
  192. --           (+)
  193. --            z
  194. local rubberPoint = {
  195.   [2] = function(x, y, z)  return sides.south, x, y, z - 1 end, -- north
  196.   [3] = function(x, y, z)  return sides.north, x, y, z + 1 end, -- south
  197.   [4] = function(x, y, z)  return sides.east, x - 1, y, z end, -- west
  198.   [5] = function(x, y, z)  return sides.west, x + 1, y, z end, -- east,
  199. }
  200. rubberPoint[8] = rubberPoint[2]
  201. rubberPoint[9] = rubberPoint[3]
  202. rubberPoint[10] = rubberPoint[4]
  203. rubberPoint[11] = rubberPoint[5]
  204.  
  205. function wood_analyze()
  206.   local d = geolyzer.analyze(sides.left)
  207.   if d.name == RUB_WOOD then
  208.     if d.metadata >= 2 then
  209.       local p = { }
  210.       p.x, p.y, p.z = robot.getPos()
  211.       p.x, p.y, p.z = robot.toVirtCoord(p.x, p.y, p.z)
  212.       p.x = p.x + 1
  213.       p.side, p.x, p.y, p.z = rubberPoint[d.metadata](robot.toRealCoord(p.x, p.y, p.z))
  214.       p.x, p.y, p.z = robot.toVirtCoord(p.x, p.y, p.z)
  215.       table.insert(farmPoints, p)
  216.     end
  217.     return true
  218.   end
  219.   return false
  220. end
  221.  
  222. function tree_analyze()
  223.   local h = 0
  224.   while 1 do
  225.     local wood = wood_analyze()
  226.     if not wood then
  227.       if h == 0 then
  228.         return 10
  229.       end
  230.       break
  231.     end
  232.     h = h + 1
  233.     local ok, err = swingmove(sides.up)
  234.     if not ok then
  235.       if err == "service" then
  236.         service()
  237.       else
  238.         emergency(err)
  239.       end
  240.     end
  241.   end
  242.   for i = 1, h do
  243.     local ok, err = swingmove(sides.down)
  244.     if not ok then
  245.       if err == "service" then
  246.         service()
  247.       else
  248.         emergency(err)
  249.       end
  250.     end
  251.   end
  252.   return h
  253. end
  254.  
  255. function treeCoord(ix, iz)
  256.   return (ix - 1) * 2 + 1, (iz - 1) * 2 + 1
  257. end
  258.  
  259. function farm_analyze(l, w)
  260.   print("Анализ фермы")
  261.   farmPoints = { }
  262.   local move = function(side)
  263.     local ok, err = swingmove(side)
  264.     if not ok then
  265.       if err == "service" then
  266.         service()
  267.       else
  268.         emergency(err)
  269.       end
  270.     end
  271.   end
  272.   move(sides.up)
  273.   for ix = 1, l do
  274.     robot.turnRight()
  275.     for iz = 1, w do
  276.       move(sides.forward)
  277.       local x, z = treeCoord(ix, iz)
  278.       farmMap:setTree(x, z, tree_analyze())
  279.       move(sides.forward)
  280.     end
  281.     robot.turnAround()
  282.     for iz = 1, w do
  283.       move(sides.forward)
  284.       move(sides.forward)
  285.     end
  286.     robot.turnRight()
  287.     move(sides.forward)
  288.     move(sides.forward)
  289.   end
  290.   robot.turnAround()
  291.   for ix = 1, l do
  292.     move(sides.forward)
  293.     move(sides.forward)
  294.   end
  295.   move(sides.down)
  296.   robot.turnAround()
  297.   local n = #farmPoints
  298.   print("Найдено "..n.. " точек сбора")
  299. --   for _,p in ipairs(farmPoints) do
  300. --     print('['..p.x..","..p.y..","..p.z.."]")
  301. --   end
  302.   if n > 0 then
  303.     print("Оптимизация пути")
  304.     farmPoints = opt_points(farmPoints)
  305.   end
  306.   print("Анализ завершён.")
  307. --   for _,p in ipairs(farmPoints) do
  308. --     print('['..p.x..","..p.y..","..p.z.."]")
  309. --   end
  310. end
  311.  
  312. function distance(blockA, blockB)
  313.   pathfinder.findPath(farmMap, blockA, blockB)
  314.   local crumb = pathfinder.findPath(farmMap, blockA, blockB)
  315.   local n = 0
  316.   if crumb == nil then
  317.     error('Path not found: '..'['.. blockA.x..","..blockA.y..","..blockA.z.."]->"..'['..  blockB.x..",".. blockB.y..",".. blockB.z.."]")
  318.   else
  319.     while crumb.next ~= nil do
  320.       crumb = crumb.next
  321.       n = n + 1
  322.     end
  323.   end
  324.   return n
  325. end
  326.  
  327. function closest_point(point, points)
  328.   local cl_num = 1
  329.   local length = distance(point, points[1])
  330.   for i=1, #points do
  331.     local l = distance(point, points[i])
  332.     if l < length then
  333.       cl_num = i
  334.       length = l
  335.     end
  336.   end
  337.   return cl_num
  338. end
  339.  
  340. function opt_points(holes_table, last)
  341.   local way_table = {}
  342.   local count = #holes_table
  343.   table.insert(way_table, {x=0, z=0, y=0})
  344.   while count ~= #way_table - 1 do
  345.     local j = closest_point(way_table[#way_table], holes_table)
  346.     table.insert(way_table, holes_table[j])
  347.     table.remove(holes_table, j)
  348.   end
  349.   return way_table, last
  350. end
  351.  
  352. function farm_collect()
  353.   local pos = { }
  354.   pos.x, pos.y, pos.z = robot.toVirtCoord(robot.getPos())
  355.   for i,p in ipairs(farmPoints) do
  356.     moveToF(p.x, p.y, p.z)
  357.     if p.side then
  358.       robot.rotateTo(p.side)
  359.       local d = geolyzer.analyze(sides.front)
  360.       if d.name ~= RUB_WOOD or d.metadata <= 1 then
  361.         print("В точке ".."["..p.x..","..p.y..","..p.z.."]".." нет подтёка!")
  362.       end
  363.       if d.metadata < 8 then
  364.         treetap_use()
  365.       end
  366.     end
  367.   end
  368.   go_home()
  369. end
  370.  
  371. function workGuard(func, ...)
  372.   local ok, err = pcall(func, ...)
  373.   if not ok then
  374.     if type(err) ~= "table" then
  375.       print(err)
  376.     end
  377.     print('Ошибка/препятствие. Возврат робота.')
  378.     go_home()
  379.     return false
  380.   end
  381.   return true
  382. end
  383.  
  384. local farmDataFileName = "/home/rubber-farm.cfg"
  385.  
  386. function farm(w, l, analyze, side)
  387.   farmMap = farmMapArr()
  388.   if analyze then
  389.     robot.navInit(side)
  390.     for ix = 1, l do
  391.       for iy = 1, w do
  392.         local x, z = treeCoord(ix, iy)
  393.         farmMap:setTree(x, z, 10)
  394.       end
  395.     end
  396.     workGuard(farm_analyze, w, l)
  397.     local farmCfg= { }
  398.     farmCfg.points = farmPoints
  399.     farmCfg.treeData = farmMap.treeData
  400.     farmCfg.side = side
  401.     local err = table.save(farmCfg, farmDataFileName)
  402.     if err then
  403.       print("Не могу сохранить конфигурацию фермы: "..err)
  404.       os.exit(1)
  405.     end
  406.   else
  407.     local farmCfg, err = table.load(farmDataFileName)
  408.     if err then
  409.       print("Не могу загрузить конфигурацию фермы: "..err)
  410.       os.exit(1)
  411.     end
  412.     if not (farmCfg.points and farmCfg.treeData and checkSide(farmCfg.side)) then
  413.       print("Неверный формат файла конфигурации фермы.")
  414.       os.exit(1)
  415.     end
  416.     farmPoints = farmCfg.points or { }
  417.     farmMap.treeData = farmCfg.treeData
  418.     robot.navInit(farmCfg.side)
  419.     local n = #farmPoints
  420.     if n > 0 then
  421.       local neddedEnergy = n * ETREETAP_USE_COST
  422.       while 1 do
  423.         if check_tool(neddedEnergy) then
  424.           charge_tool(neddedEnergy)
  425.         end
  426.         nedded_check_use_cost = true
  427.         workGuard(farm_collect)
  428.         go_home()
  429.         clear_inventory()
  430.         sleep(SLEEP_TIME)
  431.       end
  432.     else
  433.       print("На ферме нет подтёков!")
  434.       os.exit(1)
  435.     end
  436.   end
  437. end
  438.  
  439. function farm_check_components()
  440.   if not component.isAvailable("inventory_controller") then
  441.     print("Контроллер инвентаря не обнаружен. Принудительная остановка программы.")
  442.     os.exit(1)
  443.   end
  444.   inv = component.inventory_controller
  445.   if not component.isAvailable("geolyzer") then
  446.     print("Геосканер не обнаружен. Принудительная остановка программы.")
  447.     os.exit(1)
  448.   end
  449.   geolyzer = component.geolyzer
  450.   if robot.durability() == nil then
  451.     print("Инструмент не обнаружен. Принудительная остановка программы.")
  452.     os.exit(1)
  453.   end
  454. end
  455.  
  456. function main(args, options)
  457.   local function argNumber(x)
  458.     local v = tonumber(x)
  459.     if type(v) ~= 'number' then
  460.       io.write("Аргументы должны быть заданы в виде чисел.\n")
  461.       os.exit(1)
  462.     end
  463.     return v
  464.   end
  465.   local function getNumberOption(name)
  466.     local v = options[name]
  467.     if v then
  468.       v = argNumber(v)
  469.     end
  470.     return v
  471.   end
  472.   if options.help then
  473.     io.write("ссылка\n")
  474.     return
  475.   end
  476.   farm_check_components()
  477.   if options.analyze and #args == 3 then
  478.     local l = argNumber(args[1])
  479.     local w = argNumber(args[2])
  480.     local side = sides[args[3]]
  481.     if not side then
  482.       local tr = {
  483.         ["юг"] = sides.south,
  484.         ["север"] = sides.north,
  485.         ["запад"] = sides.west,
  486.         ["восток"] = sides.east,
  487.         ["ю"] = sides.south,
  488.         ["с"] = sides.north,
  489.         ["з"] = sides.west,
  490.         ["в"] = sides.east,
  491.         ["s"] = sides.south,
  492.         ["n"] = sides.north,
  493.         ["w"] = sides.west,
  494.         ["e"] = sides.east,
  495.       }
  496.       side = tr[args[3]]
  497.       if not side then
  498.         print("Неверная сторона")
  499.         os.exit(1)
  500.       end
  501.     end
  502.     farm(l, w, options["analyze"], side)
  503.   elseif options.run then
  504.     farm()
  505.   else
  506.     io.write("Запуск:\nrubber-farm --analyze ряды_вперёд ряды_справа сторона_света\n")
  507.     io.write("Запуск:\nrubber-farm\n")
  508.     io.write("Опции:\n")
  509.     io.write("--help — информация о программе\n")
  510.   end
  511. end
  512.  
  513. main(shell.parse(...))
RAW Paste Data