Skip to content

Instantly share code, notes, and snippets.

@Panakotta00
Created September 26, 2021 21:08
Show Gist options
  • Save Panakotta00/1a90fe5b64cd9647bfb2884938573ebc to your computer and use it in GitHub Desktop.
Save Panakotta00/1a90fe5b64cd9647bfb2884938573ebc to your computer and use it in GitHub Desktop.
Crappy Conveyor-Crawler and Item-Router
---@class Node
---@field connected Node[]
local Node = {}
---@class SplitterNode : Node
---@field private outputs table<Node, number>
local SplitterNode = {}
function SplitterNode:findOutput(node)
return self.outputs[node]
end
local Graph = {}
function Graph.new()
local graph = {
nodes = {},
edges = {},
nodeAlias = {},
}
setmetatable(graph, Graph)
Graph.__index = Graph
return graph
end
function Graph:newEndNode(endObj)
local endNode = self.nodeAlias[endObj.hash]
if endNode then
return endNode
end
endNode = {
connected = {},
obj = endObj,
name = "end"
}
self.nodeAlias[endObj.hash] = endNode
table.insert(self.nodes, endNode)
setmetatable(endNode, Node)
Node.__index = Node
return endNode
end
---@param connector FactoryConnection
function Graph:nextNode(connector)
while connector.isConnected do
connector = connector:getConnected()
local owner = connector.owner
if owner:getType():isChildOf(findClass("FGBuildableConveyorBase")) then
connector = owner:getFactoryConnectors()[2]
elseif owner:getType():isChildOf(findClass("FGBuildableAttachmentMerger")) then
connector = owner:getFactoryConnectors()[3]
else
break
end
end
if not connector.isConnected then
return self:newEndNode(connector)
end
local owner = connector.owner
if owner:getType():isChildOf(findClass("CodeableSplitter")) then
return self:newSplitterNode(owner)
end
target = self:newEndNode(owner)
return target
end
---@param splitter CodeableSplitter
function Graph:newSplitterNode(splitter)
local splitterNode = self.nodeAlias[splitter.hash]
if splitterNode then
return splitterNode
end
splitterNode = {
splitter = splitter,
outputs = {},
connected = {},
name = "splitter " .. splitter.id
}
self.nodeAlias[splitter.hash] = splitterNode
table.insert(self.nodes, splitterNode)
for _, v in pairs(splitter:getFactoryConnectors()) do
if v.direction == 1 then
local node = self:nextNode(v)
splitterNode.outputs[node] = #splitterNode.connected
table.insert(splitterNode.connected, node)
end
end
setmetatable(splitterNode, SplitterNode)
SplitterNode.__index = SplitterNode
return splitterNode
end
---@param merger CodeableMerger
function Graph:newMergerStartNode(merger, input)
local id = tostring(merger.hash) .. input
local mergerNode = self.nodeAlias[id]
if mergerNode then
return mergerNode
end
mergerNode = {
merger = merger,
input = input,
connected = {}
}
self.nodeAlias[id] = mergerNode
mergerNode.connected[1] = self:nextNode(merger:getFactoryConnectors()[1])
return mergerNode
end
function Graph:findNode(alias)
return self.nodeAlias[alias.hash]
end
---@param start Node
---@param destination Node
function Graph:findPath(start, destination)
local prevNode = {}
local distance = {}
local locked = {}
local current = start
while current do
locked[current] = true
local currentDistance = distance[current] or 0
for _, connected in pairs(current.connected) do
local length = 1
if distance[connected] == nil or currentDistance + length < distance[connected] then
prevNode[connected] = current
distance[connected] = currentDistance + length
end
end
currentDistance = nil
for connected in pairs(distance) do
if not locked[connected] and (currentDistance == nil or distance[connected] < currentDistance) then
current = connected
currentDistance = distance[connected]
end
end
if not currentDistance then
break
end
end
local path = {destination}
current = destination
while current ~= start do
current = prevNode[current]
table.insert(path, 1, current)
end
return path
end
return Graph, Node, SplitterNode
_libCache = {}
function require(libName)
local lib = _libCache[libName]
if lib then
return table.unpack(lib)
end
local libPath = "/" .. libName
if not filesystem.isFile(libPath) then
libPath = libPath .. ".lua"
if not filesystem.isFile(libPath) then
return nil
end
end
print("Lib: load Lib '" .. libPath .. "'")
local libFunc = filesystem.loadFile(libPath)
if type(libFunc) ~= "function" then
print("Lib: failed to load Lib '" .. libPath .. "'!")
else
lib = {libFunc()}
_libCache[libName] = lib
return table.unpack(lib)
end
end
--local json = require("json")
local Splitter = require("splitter")
local ConveyorGraph, Node, SplitterNode = require("conveyorGraph")
local splitters = {}
---@param splitter CodeableSplitter
function getRouter(splitter)
local router = splitters[splitter.hash] or Splitter.new(splitter)
splitters[splitter.hash] = router
event.listen(splitter)
return router
end
local plate = findItem("Coal")
local graph = ConveyorGraph.new()
local input = component.proxy(startComp)
local container = component.proxy(destinationComp)
local start = graph:newMergerStartNode(input, startNum)
local dest = graph:findNode(container)
local path = graph:findPath(start, dest)
local convertOutputSplitter = {
1,
0,
2
}
for i, node in pairs(path) do
if getmetatable(node) == SplitterNode then
---@type SplitterNode
node = node
local router = getRouter(node.splitter)
local next = path[i+1]
local output = node:findOutput(next)
router:pushItem(plate, convertOutputSplitter[output+1])
end
end
input:transferItem(startNum)
while true do
local e, s, d1, d2 = event.pull(1)
if e == "ItemRequest" then
local splitter = splitters[s.hash]
if splitter then
splitter:handleItem(d1.type)
end
else
for _, splitter in pairs(splitters) do
splitter:handleItem()
end
end
end
---@class Splitter
---@field public component CodeableSplitter
---@field private itemQueues table<string, number[]>
---@field public defaultOutput number
local Splitter = {}
---@return Splitter
function Splitter.new(component, defaultOutput)
local splitter = {
component = component,
itemQueues = {},
defaultOutput = defaultOutput or 0,
}
setmetatable(splitter, Splitter)
Splitter.__index = Splitter
return splitter
end
function Splitter:pushItem(item, output)
local queue = self.itemQueues[item.name] or {}
self.itemQueues[item.name] = queue
table.insert(queue, output)
end
function Splitter:popItem(item)
local queue = self.itemQueues[item.name]
if queue then
local output = queue[1]
table.remove(queue, 1)
if #queue < 1 then
self.itemQueues[item.name] = nil
end
return output
end
return self.defaultOutput
end
function Splitter:peakItem(item)
local queue = self.itemQueues[item.name]
if queue then
return queue[1]
end
return self.defaultOutput
end
function Splitter:handleItem(item)
item = item or self.component:getInput().type
if item then
print("item!", item)
local output = self:peakItem(item)
print(output)
if self.component:transferItem(output) then
self:popItem(item)
end
end
end
return Splitter
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment