Advertisement
HandieAndy

central_server.lua

Sep 14th, 2023 (edited)
1,212
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 11.50 KB | None | 0 0
  1. --[[
  2. A central server to coordinate a rail network. This central server keeps a
  3. graph of the entire network, and handles requests to find paths to route
  4. traffic through.
  5. ]]--
  6.  
  7. local RECEIVE_CHANNEL = 45452
  8.  
  9. local g = require("simple-graphics")
  10. local W, H = term.getSize()
  11. local console = g.createConsole(W, H-2, colors.white, colors.black, "UP")
  12.  
  13. -- ONLY FOR DEBUGGING
  14. -- inspect = require("inspect")
  15. local modem = peripheral.wrap("top") or error("Missing top modem")
  16. modem.open(RECEIVE_CHANNEL)
  17.  
  18. local function logToConsole(text)
  19.     local timestamp = os.date("%F %T")
  20.     g.appendAndDrawConsole(term, console, timestamp.." "..text, 1, 3)
  21. end
  22.  
  23. local function generateStandardNode(id, edgeIds)
  24.     local node = {id = id, connections = {}, type = "JUNCTION"}
  25.     for _, edgeId in pairs(edgeIds) do
  26.         for _, edgeId2 in pairs(edgeIds) do
  27.             if edgeId2 ~= edgeId then
  28.                 table.insert(node.connections, {from = edgeId, to = edgeId2})
  29.             end
  30.         end
  31.     end
  32.     return node
  33. end
  34.  
  35. local function generateStationNode(id, displayName, edgeId)
  36.     return {
  37.         id = id,
  38.         displayName = displayName,
  39.         connections = {
  40.             {from = nil, to = edgeId},
  41.             {from = edgeId, to = nil}
  42.         },
  43.         type = "STATION"
  44.     }
  45. end
  46.  
  47. local function loadGraph()
  48.     -- local g = nil
  49.     -- local f = io.open("network_graph.tbl", "r")
  50.     -- g = textutils.unserialize(f:read("*a"))
  51.     -- f:close()
  52.     --return g
  53.     local tempGraph = {
  54.         nodes = {
  55.             generateStandardNode("Junction-HandieVale", {"handievale", "N1", "W1"}),
  56.             generateStandardNode("Junction-Middlecross", {"W1", "N2", "W2", "S1"}),
  57.             generateStandardNode("Junction-Foundry", {"E1", "N3", "W3", "N2"}),
  58.             generateStandardNode("Junction-End", {"E1", "E2", "end"}),
  59.             generateStandardNode("Junction-Klausville", {"N3", "N4", "klausville"}),
  60.             generateStandardNode("Junction-Foundry-West", {"W3", "foundry", "W4"}),
  61.             generateStandardNode("Junction-Cam", {"S1", "S2", "cam"}),
  62.             generateStationNode("station-klausville", "Klausville", "klausville"),
  63.             generateStationNode("station-handievale", "HandieVale", "handievale"),
  64.             generateStationNode("station-end", "End & Biofuel Refinery", "end"),
  65.             generateStationNode("station-foundry", "Jack's Foundry", "foundry"),
  66.             generateStationNode("station-cam", "Camville", "cam")
  67.         },
  68.         edges = {
  69.             {id = "handievale", length = 16},
  70.             {id = "end", length = 48},
  71.             {id = "foundry", length = 45},
  72.             {id = "klausville", length = 12},
  73.             {id = "cam", length = 16},
  74.             {id = "N1", length = nil},
  75.             {id = "W1", length = 300},
  76.             {id = "N2", length = 600},
  77.             {id = "E1", length = 75},
  78.             {id = "W2", length = nil},
  79.             {id = "S1", length = 420},
  80.             {id = "S2", length = nil},
  81.             {id = "W3", length = 50},
  82.             {id = "W4", length = nil},
  83.             {id = "N3", length = 350},
  84.             {id = "N4", length = nil}
  85.         }
  86.     }
  87.     return tempGraph
  88. end
  89.  
  90. local function filterTable(arr, func)
  91.     local new_index = 1
  92.     local size_orig = #arr
  93.     for old_index, v in ipairs(arr) do
  94.         if func(v, old_index) then
  95.             arr[new_index] = v
  96.             new_index = new_index + 1
  97.         end
  98.     end
  99.     for i = new_index, size_orig do arr[i] = nil end
  100. end
  101.  
  102. local function findNodeById(graph, nodeId)
  103.     for _, node in pairs(graph.nodes) do
  104.         if node.id == nodeId then return node end
  105.     end
  106.     return nil
  107. end
  108.  
  109. local function findEdgeById(graph, edgeId)
  110.     for _, edge in pairs(graph.edges) do
  111.         if edge.id == edgeId then return edge end
  112.     end
  113.     return nil
  114. end
  115.  
  116. local function findEdgeBetweenNodes(graph, fromNode, toNode)
  117.     local edgeIdsFrom = {}
  118.     for _, conn in pairs(fromNode.connections) do
  119.         if conn.to and not tableContains(edgeIdsFrom, conn.to) then
  120.             table.insert(edgeIdsFrom, conn.to)
  121.         end
  122.     end
  123.     local edgeIds = {}
  124.     for _, conn in pairs(toNode.connections) do
  125.         if conn.from and tableContains(edgeIdsFrom, conn.from) and not tableContains(edgeIds, conn.from) then
  126.             table.insert(edgeIds, conn.from)
  127.         end
  128.     end
  129.  
  130. end
  131.  
  132. local function tableContains(table, value)
  133.     for _, item in pairs(table) do
  134.         if item == value then return true end
  135.     end
  136.     return false
  137. end
  138.  
  139. local function removeElement(table, value)
  140.     local idx = nil
  141.     for i, item in pairs(table) do
  142.         if item == value then idx = i break end
  143.     end
  144.     if idx then table.remove(table, idx) end
  145. end
  146.  
  147. local function findNextEdges(graph, edgeId)
  148.     local edges = {}
  149.     for _, node in pairs(graph.nodes) do
  150.         for _, connection in pairs(node.connections) do
  151.             if connection.from == edgeId then
  152.                 table.insert(edges, findEdgeById(connection.to))
  153.             end
  154.         end
  155.     end
  156.     return edges
  157. end
  158.  
  159. -- Find the set of nodes directly connected to this one via some edges
  160. local function findConnectedNodes(graph, startNode)
  161.     local edges = {}
  162.     local edgeIds = {}
  163.     for _, conn in pairs(startNode.connections) do
  164.         if conn.to ~= nil then
  165.             local edge = findEdgeById(graph, conn.to)
  166.             if edge ~= nil and edge.length ~= nil and not tableContains(edgeIds, edge.id) then
  167.                 table.insert(edges, edge)
  168.                 table.insert(edgeIds, edge.id)
  169.             end
  170.         end
  171.     end
  172.     local connections = {}
  173.     for _, edge in pairs(edges) do
  174.         for _, node in pairs(graph.nodes) do
  175.             if node.id ~= startNode.id then
  176.                 for _, conn in pairs(node.connections) do
  177.                     if conn.from == edge.id then
  178.                         table.insert(connections, {node = node, distance = edge.length, via = edge.id})
  179.                         break
  180.                     end
  181.                 end
  182.             end
  183.         end
  184.     end
  185.     return connections
  186. end
  187.  
  188. local function findPath(graph, startNode, endNode)
  189.     local INFINITY = 1000000000
  190.     local dist = {}
  191.     local prev = {}
  192.     local queue = {}
  193.     for _, node in pairs(graph.nodes) do
  194.         dist[node.id] = INFINITY
  195.         prev[node.id] = nil
  196.         table.insert(queue, node)
  197.     end
  198.     dist[startNode.id] = 0
  199.  
  200.     while #queue > 0 do
  201.         local minIdx = nil
  202.         local minDist = INFINITY + 1
  203.         for i, node in pairs(queue) do
  204.             if dist[node.id] < minDist then
  205.                 minIdx = i
  206.                 minDist = dist[node.id]
  207.             end
  208.         end
  209.         if minIdx == nil then return nil end
  210.         local u = table.remove(queue, minIdx)
  211.         if u.id == endNode.id and (prev[u.id] or u.id == startNode.id) then
  212.             local s = {}
  213.             while u ~= nil do
  214.                 local via = nil
  215.                 local distance = nil
  216.                 local node = u
  217.                 u = nil
  218.                 if prev[node.id] then
  219.                     via = prev[node.id].via
  220.                     distance = prev[node.id].distance
  221.                     u = prev[node.id].node
  222.                 end
  223.                 table.insert(s, 1, {node = node, via = via, distance = distance})
  224.             end
  225.             return s
  226.         end
  227.         for _, neighbor in pairs(findConnectedNodes(graph, u)) do
  228.             local unvisited = false
  229.             for _, node in pairs(queue) do
  230.                 if node.id == neighbor.node.id then
  231.                     unvisited = true
  232.                     break
  233.                 end
  234.             end
  235.             if unvisited then
  236.                 local alt = dist[u.id] + neighbor.distance
  237.                 if alt < dist[neighbor.node.id] then
  238.                     dist[neighbor.node.id] = alt
  239.                     prev[neighbor.node.id] = {node = u, via = neighbor.via, distance = neighbor.distance}
  240.                 end
  241.             end
  242.         end
  243.     end
  244.     return nil
  245. end
  246.  
  247. local function getReachableStations(graph, startNode)
  248.     local queue = findConnectedNodes(graph, startNode)
  249.     local stations = {}
  250.     local visitedNodeIds = {startNode.id}
  251.     while #queue > 0 do
  252.         local node = table.remove(queue, 1).node
  253.         if node.type == "STATION" and not tableContains(visitedNodeIds, node.id) then
  254.             table.insert(stations, node)
  255.         end
  256.         table.insert(visitedNodeIds, node.id)
  257.         for _, conn in pairs(findConnectedNodes(graph, node)) do
  258.             if not tableContains(visitedNodeIds, conn.node.id) then
  259.                 table.insert(queue, conn)
  260.             end
  261.         end
  262.     end
  263.     return stations
  264. end
  265.  
  266. local function handleRouteRequest(graph, replyChannel, msg)
  267.     if not msg.startNode or not msg.endNode then
  268.         modem.transmit(replyChannel, RECEIVE_CHANNEL, {success = false, error = "Invalid request"})
  269.         return
  270.     end
  271.     local startNode = findNodeById(graph, msg.startNode)
  272.     local endNode = findNodeById(graph, msg.endNode)
  273.     if not startNode or not endNode then
  274.         modem.transmit(replyChannel, RECEIVE_CHANNEL, {success = false, error = "Unknown node(s)"})
  275.         return
  276.     end
  277.     logToConsole("Finding path from "..startNode.id.." to "..endNode.id.."...")
  278.     local path = findPath(graph, startNode, endNode)
  279.     if not path then
  280.         logToConsole("Couldn't find a path!")
  281.         modem.transmit(replyChannel, RECEIVE_CHANNEL, {success = false, error = "No valid path"})
  282.         return
  283.     end
  284.  
  285.     local pathStr = ""
  286.     for i, segment in pairs(path) do
  287.         pathStr = pathStr .. segment.node.id
  288.         if i < #path - 1 then pathStr = pathStr .. ", " end
  289.     end
  290.     logToConsole("Found path: "..pathStr)
  291.     modem.transmit(replyChannel, RECEIVE_CHANNEL, {success = true, route = path})
  292. end
  293.  
  294. local function handleGetRoutesRequest(graph, replyChannel, msg)
  295.     if not msg.startNode then
  296.         modem.transmit(replyChannel, RECEIVE_CHANNEL, {success = false, error = "Invalid request"})
  297.         return
  298.     end
  299.     local startNode = findNodeById(graph, msg.startNode)
  300.     if not startNode then
  301.         modem.transmit(replyChannel, RECEIVE_CHANNEL, {success = false, error = "Unknown node"})
  302.         return
  303.     end
  304.     logToConsole("Finding reachable stations from "..startNode.id.."...")
  305.     local stations = getReachableStations(graph, startNode)
  306.     logToConsole("Found "..#stations.." results.")
  307.     modem.transmit(replyChannel, RECEIVE_CHANNEL, {success = true, stations = stations})
  308. end
  309.  
  310. local function handleRequests(graph, console)
  311.     while true do
  312.         local event, side, channel, replyChannel, msg, dist = os.pullEvent("modem_message")
  313.         if channel == RECEIVE_CHANNEL and msg and msg.command and type(msg.command) == "string" then
  314.             logToConsole("Got request on CH: "..channel..", RCH: "..replyChannel..", CMD: "..msg.command)
  315.             if msg.command == "ROUTE" then
  316.                 handleRouteRequest(graph, replyChannel, msg)
  317.             elseif msg.command == "GET_ROUTES" then
  318.                 handleGetRoutesRequest(graph, replyChannel, msg)
  319.             else
  320.                 modem.transmit(replyChannel, RECEIVE_CHANNEL, {success = false, error = "Invalid command"})
  321.             end
  322.         end
  323.     end
  324. end
  325.  
  326. g.clear(term, colors.black)
  327. g.drawTextCenter(term, W/2, 1, "CC-Rail Central Server", colors.yellow, colors.black)
  328. g.drawXLine(term, 1, W, 2, colors.gray)
  329. logToConsole("Now taking requests.")
  330. handleRequests(loadGraph(), console)
  331.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement