joserobjr

crafting_unit.lua

Feb 3rd, 2018
140
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 20.67 KB | None | 0 0
  1. local sides = require("sides")
  2. local component = require("component")
  3. local computer = require("computer")
  4. local uptime = computer.uptime
  5. local beep = computer.beep
  6.  
  7. local lib = {}
  8.  
  9. ---@class autocompressor.inventory
  10. ---@field unit autocompressor.crafting_unit
  11. ---@field side number
  12. local inventory = {}
  13.  
  14. ---@param unit autocompressor.crafting_unit
  15. ---@param side number
  16. ---@return component.inventory
  17. function inventory:new(unit, side)
  18.     local instance = {unit = unit, side = side}
  19.     setmetatable(instance, {__index = inventory})
  20.     return instance
  21. end
  22.  
  23. ---@return component.transposer
  24. function inventory:getTransposer()
  25.     return self.unit:getTransposer()
  26. end
  27.  
  28. ---@return number
  29. function inventory:getInventorySize()
  30.     return self.unit:getInventorySize(self.side)
  31. end
  32.  
  33. ---@param slot number
  34. ---@return _item_stack
  35. function inventory:getStackInSlot(slot)
  36.     if slot == -1 then return nil end
  37.     return self:getTransposer().getStackInSlot(self.side, slot)
  38. end
  39.  
  40. ---@param slots number[]
  41. ---@return table<number, _item_stack>
  42. function inventory:getStacksFromSlots(slots)
  43.     local stacks = {}
  44.     local i = 0
  45.     for _, slot in ipairs(slots) do
  46.         i = i + 1
  47.         if slot ~= -1 then
  48.             local stack = self:getStackInSlot(slot)
  49.             if stack ~= nil then
  50.                 stacks[i] = stack
  51.             end
  52.         end
  53.     end
  54.     return stacks
  55. end
  56.  
  57. ---@class autocompressor.compressor : autocompressor.inventory
  58. ---@field status string
  59. ---@field producing autocompressor.grid
  60. local compressor = {status = "init"}
  61. setmetatable(compressor, {__index = inventory})
  62.  
  63. ---@param unit autocompressor.crafting_unit
  64. ---@param side number
  65. ---@return autocompressor.compressor
  66. function compressor:new(unit, side)
  67.     local instance = inventory:new(unit, side)
  68.     setmetatable(instance, {__index = compressor})
  69.     return instance
  70. end
  71.  
  72. function compressor:isCircuit()
  73.     return self:getInventorySize() == 7
  74. end
  75.  
  76. ---@return boolean
  77. function compressor:isElectric()
  78.     return self:getInventorySize() == 12
  79. end
  80.  
  81. ---@return number[]
  82. function compressor:getCoalSlots()
  83.     if self:isElectric() or self:isCircuit() then
  84.         return {}
  85.     else
  86.         return {1}
  87.     end
  88. end
  89.  
  90. ---@return number[]
  91. function compressor:getBatterySlots()
  92.     if self:isElectric() or self:isCircuit() then
  93.         return {1}
  94.     else
  95.         return {}
  96.     end
  97. end
  98.  
  99. ---@return number[]
  100. function compressor:getOutputSlots()
  101.     if self:isElectric() then
  102.         return {2, 3}
  103.     elseif self:isCircuit() then
  104.         return {7}
  105.     else
  106.         return {2}
  107.     end
  108. end
  109.  
  110. ---@return number[]
  111. function compressor:getMatrixSlots()
  112.     if self:isElectric() then
  113.         return {
  114.             4,  5,  6,
  115.             7,  8,  9,
  116.             10, 11, 12
  117.         }
  118.     elseif self:isCircuit() then
  119.         return {
  120.             2, -1, 6,
  121.             3, 5, -1,
  122.             4, -1, -1
  123.         }
  124.     else
  125.         return {
  126.             3,  4,  5,
  127.             6,  7,  8,
  128.             9, 10, 11
  129.         }
  130.     end
  131. end
  132.  
  133. ---@return table<number, _item_stack>
  134. function compressor:getCoalStacks()
  135.     return self:getStacksFromSlots(self:getCoalSlots())
  136. end
  137.  
  138. ---@return table<number, _item_stack>
  139. function compressor:getOutputStacks()
  140.     return self:getStacksFromSlots(self:getOutputSlots())
  141. end
  142.  
  143. ---@return table<number, _item_stack>
  144. function compressor:getMatrixStacks()
  145.     return self:getStacksFromSlots(self:getMatrixSlots())
  146. end
  147.  
  148. ---@class autocompressor.chest : autocompressor.inventory
  149. ---@field grids autocompressor.grid[]
  150. local chest = {}
  151. setmetatable(chest, {__index = inventory})
  152.  
  153. ---@param unit autocompressor.crafting_unit
  154. ---@param side number
  155. ---@return autocompressor.chest
  156. function chest:new(unit, side)
  157.     ---@type autocompressor.chest
  158.     local instance = inventory:new(unit, side)
  159.     setmetatable(instance, {__index = chest})
  160.  
  161.     instance.grids = {}
  162.     --instance.delayed = {}
  163.     return instance
  164. end
  165.  
  166. ---@return number[]
  167. function chest:getGridSlots()
  168.     if self:getInventorySize() >= 27 then
  169.  
  170.         return {
  171.             7,  8,  9,
  172.             16, 17, 18,
  173.             25, 26, 27
  174.         }
  175.         --[[
  176.         return {
  177.             25, 16, 7,
  178.             26, 17, 8,
  179.             27, 18, 9
  180.         }
  181.         ]]--
  182.     else
  183.         return {}
  184.     end
  185. end
  186.  
  187. ---@return table<number, _item_stack>
  188. function chest:getGridStacks()
  189.     return self:getStacksFromSlots(self:getGridSlots())
  190. end
  191.  
  192. ---@return number[]
  193. function chest:getInputSlots()
  194.     local slots = {}
  195.     for i = 1, self:getInventorySize() do
  196.         table.insert(slots, i)
  197.     end
  198.     return slots
  199. end
  200.  
  201. ---@return table<number, _item_stack>
  202. function chest:getInputStacks()
  203.     return self:getStacksFromSlots(self:getInputSlots())
  204. end
  205.  
  206. ---@class autocompressor.grid.item
  207. ---@field name string
  208. ---@field amount number
  209. local grid_item = {}
  210.  
  211. ---@param name string
  212. ---@param amount number
  213. ---@return autocompressor.grid.item
  214. function grid_item:new(name, amount)
  215.     assert(name ~= nil, "Name can't be null")
  216.     assert(amount ~= nil, "Amount can't be null")
  217.     local instance = {name = name, amount = amount}
  218.     setmetatable(instance, {__index = grid_item})
  219.     return instance
  220. end
  221.  
  222. ---@class autocompressor.grid.stack : autocompressor.grid.item
  223. ---@field id string
  224. ---@field metadata number
  225. local grid_stack = {}
  226. setmetatable(grid_stack, {__index = grid_item})
  227.  
  228. function grid_item:isStackValid(stack) return false end
  229.  
  230.  
  231. ---@param name string
  232. ---@param amount number
  233. ---@param id string
  234. ---@param metadata number
  235. ---@return autocompressor.grid.stack
  236. function grid_stack:new(name, amount, id, metadata)
  237.     ---@type autocompressor.grid.stack
  238.     local instance = grid_item:new(name, amount)
  239.     setmetatable(instance, {__index = grid_stack})
  240.  
  241.     instance.id = id
  242.     instance.metadata = metadata
  243.     return instance
  244. end
  245.  
  246. ---@param stack _item_stack
  247. ---@return boolean
  248. function grid_stack:isStackValid(stack)
  249.     return stack.name == self.id and stack.damage == self.metadata
  250. end
  251.  
  252. ---@class autocompressor.grid
  253. ---@field matrix table<number, autocompressor.grid.stack>
  254. ---@field product string
  255. ---@field amount number
  256. local grid = {}
  257.  
  258. ---@param matrix table<number, autocompressor.grid.item>
  259. ---@param product string
  260. ---@param amount number
  261. ---@return autocompressor.grid
  262. function grid:new(matrix, product, amount)
  263.     checkArg(3, amount, "number")
  264.     local instance = {matrix = matrix, product=product, amount=amount}
  265.     setmetatable(instance, {__index = grid})
  266.     return instance
  267. end
  268.  
  269. ---@param slot number
  270. ---@param stack _item_stack
  271. ---@return boolean
  272. function grid:isStackValid(slot, stack)
  273.     local item = self.matrix[slot]
  274.     if item == nil and stack == nil then
  275.         return true
  276.     elseif item == nil or stack == nil then
  277.         --print("nil!", lib.serialize(item), lib.serialize(stack))
  278.         return false
  279.     else
  280.         local result = item:isStackValid(stack)
  281.         if not result then
  282.             --print("not!", lib.serialize(item), lib.serialize(stack))
  283.         end
  284.         return result
  285.     end
  286. end
  287.  
  288. ---@return table<string, table<number, number>>
  289. function grid:requiredMaterials()
  290.     local requirements = {}
  291.     for _, item in pairs(self.matrix) do
  292.         if item ~= nil then
  293.             local itemTable = requirements[item.id] or {}
  294.             requirements[item.id] = itemTable
  295.  
  296.             local amount = itemTable[item.metadata] or 0
  297.             amount = amount + item.amount
  298.             itemTable[item.metadata] = amount
  299.         end
  300.     end
  301.  
  302.     return requirements
  303. end
  304.  
  305. ---@param inventoryContents table<number, _item_stack>
  306. ---@param chest autocompressor.chest
  307. ---@return table<string, table<number, table<number, number> > >
  308. function grid:findResources(inventoryContents, chest)
  309.     local requirements = self:requiredMaterials()
  310.     local resources = {}
  311.  
  312.     --[[
  313.     local save = io.open("requirements.lua", "w")
  314.     save:write(require("serialization").serialize(requirements))
  315.     save:close()
  316.     ]]--
  317.  
  318.     for slot, stack in pairs(inventoryContents) do
  319.         if stack ~= nil and not stack.hasTag and stack.size > 0 then
  320.             --[[
  321.             local delays = chest.delayed[slot]
  322.             local reserved = 0
  323.             if delays ~= nil then
  324.                 for i, delay in ipairs(delays) do
  325.                     reserved = reserved + delay.amount
  326.                 end
  327.             end
  328.             ]]--
  329.             local itemTable = requirements[stack.name]
  330.             if itemTable ~= nil --[[ and stack.size - reserved > 0 ]] then
  331.                 local need = itemTable[stack.damage] or 0
  332.                 if need > 0 then
  333.                     local take = stack.size --[[ - reserved ]]--
  334.                     if take > need then
  335.                         take = need
  336.                     end
  337.                     need = need - take
  338.  
  339.                     if not stack.originalSize then
  340.                         stack.originalSize = stack.size
  341.                     end
  342.  
  343.                     stack.size = stack.size - take
  344.  
  345.                     local foundItemTable = resources[stack.name] or {}
  346.                     resources[stack.name] = foundItemTable
  347.  
  348.                     local damageTable = foundItemTable[stack.damage] or {}
  349.                     foundItemTable[stack.damage] = damageTable
  350.  
  351.                     damageTable[slot] = take
  352.  
  353.                     if need > 0 then
  354.                         itemTable[stack.damage] = need
  355.                     else
  356.                         itemTable[stack.damage] = nil
  357.                         if next(itemTable) == nil then
  358.                             requirements[stack.name] = nil
  359.                             if next(requirements) == nil then
  360.                                 return resources
  361.                             end
  362.                         end
  363.                     end
  364.                 end
  365.             end
  366.         end
  367.     end
  368.  
  369.     for k, stack in pairs(inventoryContents) do
  370.         if stack.originalSize then
  371.             stack.size = stack.originalSize
  372.             stack.originalSize = nil
  373.         end
  374.     end
  375.  
  376.     return nil
  377. end
  378.  
  379. ---@param chest autocompressor.chest
  380. ---@param compressor autocompressor.compressor
  381. ---@param optional stacks table<number, _item_stack>
  382. function grid:moveResources(chest, compressor, stacks)
  383.     if chest.unit ~= compressor.unit then
  384.         error("Chest and compressor are from different unities")
  385.     end
  386.  
  387.     stacks = stacks or chest:getInputStacks()
  388.  
  389.     --[[
  390.     local save = io.open("debug.lua", "w")
  391.     save:write(require("serialization").serialize(stacks))
  392.     save:close()
  393.     ]]--
  394.  
  395.     local resources = self:findResources(stacks, chest)
  396.     if resources == nil then
  397.         return false
  398.     end
  399.  
  400.     --[[
  401.     save = io.open("resources.lua", "w")
  402.     save:write(require("serialization").serialize(resources))
  403.     save:close()
  404.     ]]
  405.  
  406.     local compressorStacks = compressor:getMatrixStacks()
  407.     if next(compressorStacks) ~= nil then
  408.         local expectedAmount = select(2, next(compressorStacks)).size
  409.         for slot, compressorStack in pairs(compressorStacks) do
  410.             if compressorStack.size ~= expectedAmount then
  411.                 --beep(2000)
  412.                 return false
  413.             elseif not self:isStackValid(slot, compressorStack) then
  414.                 --beep()
  415.                 return false
  416.             elseif compressorStack ~= nil and compressorStack.size + self.matrix[slot].amount > compressorStack.maxSize then
  417.                 --beep(20)
  418.                 return false
  419.             end
  420.         end
  421.     end
  422.  
  423.     local transposer = chest:getTransposer()
  424.     local compressorSlots = compressor:getMatrixSlots()
  425.     local chestSlots = chest:getInputSlots()
  426.  
  427.     local failed = false
  428.  
  429.     for matrixIndex, item in pairs(self.matrix) do
  430.         if item ~= nil and compressorSlots[matrixIndex] ~= -1 then
  431.             if item.id == nil then error("item.id = nil!")
  432.             elseif item.metadata == nil then error("item.metadata = nil!")
  433.             elseif resources == nil then error("resources = nil!")
  434.             elseif resources[item.id] == nil then error("resources[item.id] = nil! item.id="..item.id)
  435.             elseif resources[item.id][item.metadata] == nil then error("resources[item.id][item.metadata] = nil! item.id="..item.id.." item.metadata="..item.metadata)
  436.             end
  437.             local resouceMap = resources[item.id][item.metadata]
  438.             local need = item.amount
  439.             for chestIndex, available in pairs(resouceMap) do
  440.                 if available > 0 then
  441.                     local take = available
  442.                     if take > need then
  443.                         take = need
  444.                     end
  445.                     if not transposer.transferItem(chest.side, compressor.side, take, chestSlots[chestIndex], compressorSlots[matrixIndex]) then
  446.                         failed = true
  447.                         --[[
  448.                         local delayed = chest.delayed[chestSlots[chestIndex] ]
  449.                         if not delayed then
  450.                             delayed = {}
  451.                             chest.delayed[chestSlots[chestIndex] ] = delayed
  452.                         end
  453.                         table.insert(delayed, delayMove(chestSlots[chestIndex], take, compressor.side, compressorSlots[matrixIndex]))
  454.                         ]]--
  455.                     end
  456.                     --print("Moved "..take.." "..item.name.." from "..chestSlots[chestIndex].." to "..compressorSlots[matrixIndex])
  457.                     need = need - take
  458.                     resouceMap[chestIndex] = available - take
  459.                     if need <= 0 then
  460.                         break
  461.                     end
  462.                 end
  463.             end
  464.         end
  465.     end
  466.  
  467.     if failed then
  468.         for i, slot in ipairs(compressor:getMatrixSlots()) do
  469.             if slot ~= -1 then
  470.                 transposer.transferItem(compressor.side, chest.side, 64, slot)
  471.             end
  472.         end
  473.         return false
  474.     end
  475.  
  476.     return true
  477. end
  478.  
  479. ---@param stacks table<number, _item_stack>
  480. ---@param product string
  481. ---@param amount number
  482. ---@return boolean
  483. function chest:createGrid(stacks, product, amount)
  484.     if next(stacks) == nil then
  485.         return false
  486.     end
  487.  
  488.     local pattern = {}
  489.     for slot, stack in pairs(stacks) do
  490.         if stack ~= nil then
  491.             pattern[slot] = grid_stack:new(stack.label, stack.size, stack.name, stack.damage)
  492.         end
  493.     end
  494.  
  495.     table.insert(self.grids, grid:new(pattern, product, amount))
  496.     return true
  497. end
  498.  
  499. --[[
  500. function chest:processDelayedMoves()
  501.     for slot, k in pairs(self.delayed) do
  502.         for i, delayed in ipairs(k) do
  503.             if self:getTransposer().transferItem(self.side, delayed.toSide, delayed.amount, delayed.slot, delayed.toSlot) then
  504.                 table.remove(k, i)
  505.             end
  506.         end
  507.         if not next(k) then
  508.             self.delayed[slot] = nil
  509.         end
  510.     end
  511. end
  512. ]]--
  513.  
  514. function chest:scanPatterns()
  515.     local compressors = self.unit:getCompressors()
  516.     local stacks = lib.checkTime("getInputStacks", self.getInputStacks, self)
  517.     for k, g in pairs(self.grids) do
  518.         for k, c in pairs(compressors) do
  519.             if lib.checkTime("moveResources", g.moveResources, g, self, c, stacks) then
  520.                 c.producing = g
  521.                 --stacks = lib.checkTime("getInputStacks", self.getInputStacks, self)
  522.             end
  523.         end
  524.     end
  525. end
  526.  
  527. function compressor:updateStatus()
  528.     if self.producing == nil then
  529.         self.status = "waiting"
  530.     else
  531.         local slot, item = next(self.producing.matrix)
  532.         if slot == nil or item == nil then
  533.             self.status = "error1"
  534.             return
  535.         end
  536.  
  537.         local stack = self:getStackInSlot(self:getMatrixSlots()[slot])
  538.         if stack == nil then
  539.             if next(self:getMatrixStacks()) then
  540.                 self.status = "error2"
  541.             else
  542.                 self.producing = nil
  543.                 self.status = "waiting"
  544.             end
  545.         else
  546.             local unit = self.producing.amount / item.amount
  547.             self.status = (stack.size * unit).."x"..self.producing.product
  548.         end
  549.     end
  550. end
  551.  
  552.  
  553. ---@class autocompressor.crafting_unit
  554. ---@field status string
  555. ---@field name string
  556. ---@field address string
  557. ---@field private compressors table<number, autocompressor.compressor>
  558. ---@field private inputs table<number, autocompressor.chest>
  559. local unit = {status="init"}
  560.  
  561. ---@return component.transposer
  562. function unit:getTransposer()
  563.     local comp, err = component.get(self.address, "transposer")
  564.     if comp == nil then
  565.         error(err)
  566.     else
  567.         return component.proxy(comp)
  568.     end
  569. end
  570.  
  571. ---@return number[]
  572. function unit:getInputSides()
  573.     local inputs = {}
  574.     for _, sideName in ipairs(sides) do
  575.         local side = sides[sideName]
  576.         local size = self:getInventorySize(side)
  577.         if(size ~= nil and size > 0 and size ~= 12 and size ~= 11 and size ~= 7) then
  578.             table.insert(inputs, side)
  579.         end
  580.     end
  581.  
  582.     return inputs
  583. end
  584.  
  585. ---@return number[]
  586. function unit:getOutputSides()
  587.     local outputs = {}
  588.     for _, sideName in ipairs(sides) do
  589.         local side = sides[sideName]
  590.         local size = self:getInventorySize(side)
  591.         if(size == 12 or size == 11 or size == 7) then
  592.             table.insert(outputs, side)
  593.         end
  594.     end
  595.  
  596.     return outputs
  597. end
  598.  
  599. ---@return table<number, autocompressor.compressor>
  600. function unit:getCompressors()
  601.     local compressors = {}
  602.     for _, side in ipairs(self:getOutputSides()) do
  603.         local instance = self.compressors[side]
  604.         if instance == nil then
  605.             instance = compressor:new(self, side)
  606.             self.compressors[side] = instance
  607.         end
  608.         compressors[side] = instance
  609.     end
  610.     return compressors
  611. end
  612.  
  613. ---@return table<number, autocompressor.chest>
  614. function unit:getInputs()
  615.     local inputs = {}
  616.     for _, side in ipairs(self:getInputSides()) do
  617.         local instance = self.inputs[side]
  618.         if instance == nil then
  619.             instance = chest:new(self, side)
  620.             self.inputs[side] = instance
  621.         end
  622.         inputs[side] = instance
  623.     end
  624.     return inputs
  625. end
  626.  
  627. function unit:getInventorySize(side)
  628.     local cache = self.cachedSize[side]
  629.     if cache ~= nil and uptime() - cache.time < 10 then
  630.         return cache.size
  631.     end
  632.  
  633.     local result = {size=self:getTransposer().getInventorySize(side) or 0, time=uptime()}
  634.     self.cachedSize[side] = result
  635.     return result.size
  636. end
  637.  
  638. -- local unities = {}
  639.  
  640. ---@param transposer component.transposer|string
  641. ---@param optional name string
  642. ---@return autocompressor.crafting_unit
  643. function lib.wrap(transposer, name)
  644.     local address
  645.     if type(transposer) == "table" then
  646.         address = transposer.address
  647.     elseif type(transposer) == "string" then
  648.         address = transposer
  649.     else
  650.         error("Expected a transpoer address or the component's table")
  651.     end
  652.  
  653.     --[[
  654.     local instance = unities[address]
  655.     if instance ~= nil then
  656.         if name ~= nil then
  657.             instance.name = name
  658.         end
  659.         return instance
  660.     end
  661.     ]]--
  662.  
  663.     name = name or address:sub(1,8)
  664.  
  665.     local instance = {address = address, name = name, compressors={}, inputs={}, cachedSize={}}
  666.     setmetatable(instance, {__index = unit})
  667.     --unities[address] = instance
  668.     return instance
  669. end
  670.  
  671. function lib.checkTime(name, func, ...)
  672.     local start = uptime()
  673.     local stat, result = xpcall(function(...) return table.pack(func(...)) end, function(msg)
  674.         -- msg can be a custom error object
  675.         if type(msg) == 'table' then
  676.             if msg.reason ~= "terminated" then
  677.                 io.stderr:write(msg.reason.."\n")
  678.             end
  679.             return msg.code or 0
  680.         end
  681.         local stack = debug.traceback():gsub('^([^\n]*\n)[^\n]*\n[^\n]*\n','%1')
  682.         local file = io.open("err-stack.txt", "a")
  683.         file:write(string.format('%s:\n%s', msg or '', stack))
  684.         file:close()
  685.         return 128 -- syserr
  686.     end, ...)
  687.     local finish = uptime()
  688.     --print(name.." took "..(finish - start).." seconds SUCCESS:"..tostring(stat or false))
  689.     --[[
  690.     local save = io.open("result.lua", "w")
  691.     save:write(require("serialization").serialize({stat=stat, err=err, result=result}))
  692.     save:close()
  693.     ]]
  694.     if not result then
  695.         return nil
  696.     else
  697.         return table.unpack(result)
  698.     end
  699. end
  700.  
  701. function lib.tablelength(T)
  702.     local count = 0
  703.     for _ in pairs(T) do count = count + 1 end
  704.     return count
  705. end
  706.  
  707. function lib.serialize(t)
  708.     return require("serialization").serialize(t, true)
  709. end
  710.  
  711. return lib
Add Comment
Please, Sign In to add comment