mirror of
https://github.com/Xevion/project-cybersyn.git
synced 2025-12-10 16:08:13 -06:00
beginning to add depot logic
This commit is contained in:
@@ -2,3 +2,6 @@ finish wagon manifest
|
|||||||
add rail networks
|
add rail networks
|
||||||
figure out how to make the area-of-effect graphic the correct size
|
figure out how to make the area-of-effect graphic the correct size
|
||||||
close gui when the combinator is destroyed
|
close gui when the combinator is destroyed
|
||||||
|
Improve localization
|
||||||
|
support space elevator
|
||||||
|
add an "all" train class
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
require("scripts.constants")
|
require("scripts.constants")
|
||||||
require("scripts.global")
|
require("scripts.global")
|
||||||
require("scripts.controller")
|
require("scripts.central-planning")
|
||||||
require("scripts.layout")
|
require("scripts.layout")
|
||||||
require("scripts.gui")
|
require("scripts.gui")
|
||||||
require("scripts.alerts")
|
require("scripts.alerts")
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ lost-train=A train has become lost
|
|||||||
nonempty-train=A train has parked in a depot while still containing items; it cannot be dispatched until it is empty
|
nonempty-train=A train has parked in a depot while still containing items; it cannot be dispatched until it is empty
|
||||||
|
|
||||||
[cybersyn-gui]
|
[cybersyn-gui]
|
||||||
operation=Choose combinator type
|
operation=Combinator type
|
||||||
|
network=Network
|
||||||
comb1=Primary controller
|
comb1=Primary controller
|
||||||
comb2=Secondary station control
|
comb2=Secondary station control
|
||||||
depot=Depot
|
depot=Depot
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
local get_distance = require("__flib__.misc").get_distance
|
local get_distance = require("__flib__.misc").get_distance
|
||||||
local math = math
|
local math = math
|
||||||
local INF = math.huge
|
local INF = math.huge
|
||||||
|
local btest = bit32.btest
|
||||||
|
local band = bit32.band
|
||||||
|
|
||||||
local create_loading_order_condition = {type = "inactivity", compare_type = "and", ticks = 120}
|
local create_loading_order_condition = {type = "inactivity", compare_type = "and", ticks = 120}
|
||||||
---@param stop LuaEntity
|
---@param stop LuaEntity
|
||||||
@@ -72,6 +74,33 @@ local function get_signals(station)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param depot Depot
|
||||||
|
local function set_depot_signals(depot)
|
||||||
|
local comb = depot.entity_comb
|
||||||
|
if depot.network_name and comb.valid and (comb.status == defines.entity_status.working or comb.status == defines.entity_status.low_power) then
|
||||||
|
depot.priority = 0
|
||||||
|
depot.network_flag = 1
|
||||||
|
local signals = comb.get_merged_signals(defines.circuit_connector_id.combinator_input)
|
||||||
|
if signals then
|
||||||
|
for k, v in pairs(signals) do
|
||||||
|
local item_name = v.signal.name
|
||||||
|
local item_count = v.count
|
||||||
|
if item_name then
|
||||||
|
if item_name == SIGNAL_PRIORITY then
|
||||||
|
depot.priority = item_count
|
||||||
|
end
|
||||||
|
if item_name == depot.network_name then
|
||||||
|
depot.network_flag = item_count
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
depot.priority = 0
|
||||||
|
depot.network_flag = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
---@param map_data MapData
|
---@param map_data MapData
|
||||||
---@param comb LuaEntity
|
---@param comb LuaEntity
|
||||||
---@param signals ConstantCombinatorParameters[]?
|
---@param signals ConstantCombinatorParameters[]?
|
||||||
@@ -144,7 +173,7 @@ end
|
|||||||
---@param station Station
|
---@param station Station
|
||||||
---@param layout_id uint
|
---@param layout_id uint
|
||||||
local function station_accepts_layout(station, layout_id)
|
local function station_accepts_layout(station, layout_id)
|
||||||
return true
|
return station.accepted_layouts[layout_id]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -157,9 +186,12 @@ local function get_valid_train(map_data, r_station_id, p_station_id, item_type)
|
|||||||
--NOTE: this code is the critical section for run-time optimization
|
--NOTE: this code is the critical section for run-time optimization
|
||||||
local r_station = map_data.stations[r_station_id]
|
local r_station = map_data.stations[r_station_id]
|
||||||
local p_station = map_data.stations[p_station_id]
|
local p_station = map_data.stations[p_station_id]
|
||||||
|
---@type string
|
||||||
|
local network_name = p_station.network_name
|
||||||
|
|
||||||
local p_to_r_dist = get_stop_dist(p_station.entity_stop, r_station.entity_stop)
|
local p_to_r_dist = get_stop_dist(p_station.entity_stop, r_station.entity_stop)
|
||||||
if p_to_r_dist == INF then
|
local netand = band(p_station.network_flag, r_station.network_flag)
|
||||||
|
if p_to_r_dist == INF or netand == 0 then
|
||||||
return nil, INF
|
return nil, INF
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -168,20 +200,22 @@ local function get_valid_train(map_data, r_station_id, p_station_id, item_type)
|
|||||||
local valid_train_exists = false
|
local valid_train_exists = false
|
||||||
|
|
||||||
local is_fluid = item_type == "fluid"
|
local is_fluid = item_type == "fluid"
|
||||||
for train_id, _ in pairs(map_data.trains_available) do
|
for depot_id, train_id in pairs(map_data.trains_available[network_name]) do
|
||||||
|
local depot = map_data.depots[depot_id]
|
||||||
local train = map_data.trains[train_id]
|
local train = map_data.trains[train_id]
|
||||||
--check cargo capabilities
|
--check cargo capabilities
|
||||||
--check layout validity for both stations
|
--check layout validity for both stations
|
||||||
if
|
if
|
||||||
((is_fluid and train.fluid_capacity > 0) or (not is_fluid and train.item_slot_capacity > 0))
|
depot.network_name == network_name and
|
||||||
and station_accepts_layout(r_station, train.layout_id)
|
btest(netand, depot.network_flag)
|
||||||
and station_accepts_layout(p_station, train.layout_id)
|
((is_fluid and train.fluid_capacity > 0) or (not is_fluid and train.item_slot_capacity > 0)) and
|
||||||
and train.entity.station
|
station_accepts_layout(r_station, train.layout_id) and
|
||||||
|
station_accepts_layout(p_station, train.layout_id)
|
||||||
then
|
then
|
||||||
valid_train_exists = true
|
valid_train_exists = true
|
||||||
--check if exists valid path
|
--check if exists valid path
|
||||||
--check if path is shortest so we prioritize locality
|
--check if path is shortest so we prioritize locality
|
||||||
local d_to_p_dist = get_stop_dist(train.entity.station, p_station.entity_stop)
|
local d_to_p_dist = get_stop_dist(depot.entity_stop, p_station.entity_stop) - DEPOT_PRIORITY_MULT*depot.priority
|
||||||
|
|
||||||
local dist = d_to_p_dist
|
local dist = d_to_p_dist
|
||||||
if dist < best_dist then
|
if dist < best_dist then
|
||||||
@@ -206,6 +240,7 @@ end
|
|||||||
---@param primary_item_name string
|
---@param primary_item_name string
|
||||||
---@param economy Economy
|
---@param economy Economy
|
||||||
local function send_train_between(map_data, r_station_id, p_station_id, train, primary_item_name, economy)
|
local function send_train_between(map_data, r_station_id, p_station_id, train, primary_item_name, economy)
|
||||||
|
--trains and stations expected to be of the same network
|
||||||
local r_station = map_data.stations[r_station_id]
|
local r_station = map_data.stations[r_station_id]
|
||||||
local p_station = map_data.stations[p_station_id]
|
local p_station = map_data.stations[p_station_id]
|
||||||
|
|
||||||
@@ -301,8 +336,9 @@ local function send_train_between(map_data, r_station_id, p_station_id, train, p
|
|||||||
r_station.deliveries[item.name] = (r_station.deliveries[item.name] or 0) + item.count
|
r_station.deliveries[item.name] = (r_station.deliveries[item.name] or 0) + item.count
|
||||||
p_station.deliveries[item.name] = (p_station.deliveries[item.name] or 0) - item.count
|
p_station.deliveries[item.name] = (p_station.deliveries[item.name] or 0) - item.count
|
||||||
|
|
||||||
local r_stations = economy.r_stations_all[item.name]
|
local item_network_name = (r_station.network_name and item.name + ":" + r_station.network_name) or item.name
|
||||||
local p_stations = economy.p_stations_all[item.name]
|
local r_stations = economy.r_stations_all[item_network_name]
|
||||||
|
local p_stations = economy.p_stations_all[item_network_name]
|
||||||
for i, id in ipairs(r_stations) do
|
for i, id in ipairs(r_stations) do
|
||||||
if id == r_station_id then
|
if id == r_station_id then
|
||||||
table.remove(r_stations, i)
|
table.remove(r_stations, i)
|
||||||
@@ -336,26 +372,30 @@ function tick(map_data, mod_settings)
|
|||||||
local economy = {
|
local economy = {
|
||||||
r_stations_all = {},
|
r_stations_all = {},
|
||||||
p_stations_all = {},
|
p_stations_all = {},
|
||||||
all_items = {},
|
|
||||||
total_ticks = total_ticks,
|
total_ticks = total_ticks,
|
||||||
}
|
}
|
||||||
local r_stations_all = economy.r_stations_all
|
local r_stations_all = economy.r_stations_all
|
||||||
local p_stations_all = economy.p_stations_all
|
local p_stations_all = economy.p_stations_all
|
||||||
local all_items = economy.all_items
|
local all_names = {}
|
||||||
|
|
||||||
|
for depot_id, _ in pairs(map_data.trains_available) do
|
||||||
|
set_depot_signals(map_data.depots[depot_id])
|
||||||
|
end
|
||||||
|
|
||||||
for station_id, station in pairs(stations) do
|
for station_id, station in pairs(stations) do
|
||||||
if station.deliveries_total < station.entity_stop.trains_limit then
|
if station.network_name and station.deliveries_total < station.entity_stop.trains_limit then
|
||||||
station.r_threshold = mod_settings.r_threshold
|
station.r_threshold = mod_settings.r_threshold
|
||||||
station.p_threshold = mod_settings.p_threshold
|
station.p_threshold = mod_settings.p_threshold
|
||||||
station.priority = 0
|
station.priority = 0
|
||||||
station.locked_slots = 0
|
station.locked_slots = 0
|
||||||
|
station.network_flag = 1
|
||||||
local signals = get_signals(station)
|
local signals = get_signals(station)
|
||||||
if signals then
|
if signals then
|
||||||
for k, v in pairs(signals) do
|
for k, v in pairs(signals) do
|
||||||
local item_name = v.signal.name
|
local item_name = v.signal.name
|
||||||
local item_count = v.count
|
local item_count = v.count
|
||||||
local item_type = v.signal.type
|
local item_type = v.signal.type
|
||||||
if item_name and item_type then
|
if item_name then
|
||||||
if item_type == "virtual" then
|
if item_type == "virtual" then
|
||||||
if item_name == SIGNAL_PRIORITY then
|
if item_name == SIGNAL_PRIORITY then
|
||||||
station.priority = item_count
|
station.priority = item_count
|
||||||
@@ -368,6 +408,9 @@ function tick(map_data, mod_settings)
|
|||||||
end
|
end
|
||||||
signals[k] = nil
|
signals[k] = nil
|
||||||
end
|
end
|
||||||
|
if item_name == station.network_name then
|
||||||
|
station.network_flag = item_count
|
||||||
|
end
|
||||||
else
|
else
|
||||||
signals[k] = nil
|
signals[k] = nil
|
||||||
end
|
end
|
||||||
@@ -380,19 +423,21 @@ function tick(map_data, mod_settings)
|
|||||||
|
|
||||||
if item_name then
|
if item_name then
|
||||||
if -effective_item_count >= r_threshold then
|
if -effective_item_count >= r_threshold then
|
||||||
if r_stations_all[item_name] == nil then
|
local item_network_name = item_name + ":" + station.network_name
|
||||||
r_stations_all[item_name] = {}
|
if r_stations_all[item_network_name] == nil then
|
||||||
p_stations_all[item_name] = {}
|
r_stations_all[item_network_name] = {}
|
||||||
all_items[#all_items + 1] = item_name
|
p_stations_all[item_network_name] = {}
|
||||||
all_items[#all_items + 1] = v.signal.type
|
all_names[#all_names + 1] = item_network_name
|
||||||
|
all_names[#all_names + 1] = v.signal
|
||||||
end
|
end
|
||||||
table.insert(r_stations_all[item_name], station_id)
|
table.insert(r_stations_all[item_name], station_id)
|
||||||
elseif effective_item_count >= p_threshold then
|
elseif effective_item_count >= p_threshold then
|
||||||
if r_stations_all[item_name] == nil then
|
local item_network_name = item_name + ":" + station.network_name
|
||||||
r_stations_all[item_name] = {}
|
if r_stations_all[item_network_name] == nil then
|
||||||
p_stations_all[item_name] = {}
|
r_stations_all[item_network_name] = {}
|
||||||
all_items[#all_items + 1] = item_name
|
p_stations_all[item_network_name] = {}
|
||||||
all_items[#all_items + 1] = v.signal.type
|
all_names[#all_names + 1] = item_network_name
|
||||||
|
all_names[#all_names + 1] = v.signal
|
||||||
end
|
end
|
||||||
table.insert(p_stations_all[item_name], station_id)
|
table.insert(p_stations_all[item_name], station_id)
|
||||||
end
|
end
|
||||||
@@ -402,17 +447,19 @@ function tick(map_data, mod_settings)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local failed_because_missing_trains = {}
|
|
||||||
--we do not dispatch more than one train per station per tick
|
--we do not dispatch more than one train per station per tick
|
||||||
--psuedo-randomize what item (and what station) to check first so if trains available is low they choose orders psuedo-randomly
|
--psuedo-randomize what item (and what station) to check first so if trains available is low they choose orders psuedo-randomly
|
||||||
local start_i = 2*(total_ticks%(#all_items/2)) + 1
|
--NOTE: It may be better for performance to update stations one tick at a time rather than all at once, however this does mean more redundant data will be generated and discarded each tick. Once we have a performance test-bed it will probably be worth checking.
|
||||||
for item_i = 0, #all_items - 1, 2 do
|
local start_i = 2*(total_ticks%(#all_names/2)) + 1
|
||||||
local item_name = all_items[(start_i + item_i - 1)%#all_items + 1]
|
for item_i = 0, #all_names - 1, 2 do
|
||||||
local item_type = all_items[(start_i + item_i)%#all_items + 1]
|
local item_network_name = all_names[(start_i + item_i - 1)%#all_names + 1]
|
||||||
local r_stations = r_stations_all[item_name]
|
local signal = all_names[(start_i + item_i)%#all_names + 1]
|
||||||
local p_stations = p_stations_all[item_name]
|
local item_name = signal.name
|
||||||
|
local item_type = signal.type
|
||||||
|
local r_stations = r_stations_all[item_network_name]
|
||||||
|
local p_stations = p_stations_all[item_network_name]
|
||||||
|
|
||||||
--NOTE: this is an approximation algorithm for solving the assignment problem (bipartite graph weighted matching), the true solution would be to implement the simplex algorithm (and run it twice to compare the locality solution to the round-robin solution) but I strongly believe most factorio players would prefer run-time efficiency over perfect train routing logic
|
--NOTE: this is an approximation algorithm for solving the assignment problem (bipartite graph weighted matching), the true solution would be to implement the simplex algorithm but I strongly believe most factorio players would prefer run-time efficiency over perfect train routing logic
|
||||||
if #r_stations > 0 and #p_stations > 0 then
|
if #r_stations > 0 and #p_stations > 0 then
|
||||||
if #r_stations <= #p_stations then
|
if #r_stations <= #p_stations then
|
||||||
--probably backpressure, prioritize locality
|
--probably backpressure, prioritize locality
|
||||||
@@ -19,6 +19,8 @@ OPERATION_WAGON_MANIFEST = "-"
|
|||||||
|
|
||||||
DELTA = 1/2048
|
DELTA = 1/2048
|
||||||
|
|
||||||
|
DEPOT_PRIORITY_MULT = 2048
|
||||||
|
|
||||||
STATUS_D = 0
|
STATUS_D = 0
|
||||||
STATUS_D_TO_P = 1
|
STATUS_D_TO_P = 1
|
||||||
STATUS_P = 2
|
STATUS_P = 2
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
---@field public to_output {[uint]: LuaEntity}
|
---@field public to_output {[uint]: LuaEntity}
|
||||||
---@field public to_stop {[uint]: LuaEntity}
|
---@field public to_stop {[uint]: LuaEntity}
|
||||||
---@field public stations {[uint]: Station}
|
---@field public stations {[uint]: Station}
|
||||||
---@field public depots {[uint]: LuaEntity}
|
---@field public depots {[uint]: Depot}
|
||||||
---@field public trains {[uint]: Train}
|
---@field public trains {[uint]: Train}
|
||||||
---@field public trains_available {[uint]: boolean}
|
---@field public trains_available {[string]: {[uint]: uint}}
|
||||||
---@field public layouts {[uint]: string}
|
---@field public layouts {[uint]: string}
|
||||||
---@field public layout_train_count {[uint]: int}
|
---@field public layout_train_count {[uint]: int}
|
||||||
---@field public train_classes {[string]: TrainClass}
|
---@field public train_classes {[string]: TrainClass}
|
||||||
@@ -25,10 +25,19 @@
|
|||||||
---@field public entity_comb2 LuaEntity?
|
---@field public entity_comb2 LuaEntity?
|
||||||
---@field public wagon_combs {[int]: LuaEntity}?--NOTE: allowed to be invalid entities or combinators with the wrong operation, these must be checked and lazy deleted when found
|
---@field public wagon_combs {[int]: LuaEntity}?--NOTE: allowed to be invalid entities or combinators with the wrong operation, these must be checked and lazy deleted when found
|
||||||
---@field public deliveries {[string]: int}
|
---@field public deliveries {[string]: int}
|
||||||
|
---@field public network_name string?
|
||||||
|
---@field public network_flag int
|
||||||
---@field public train_class SignalID?
|
---@field public train_class SignalID?
|
||||||
---@field public accepted_layouts TrainClass
|
---@field public accepted_layouts TrainClass
|
||||||
---@field public layout_pattern string?
|
---@field public layout_pattern string?
|
||||||
|
|
||||||
|
---@class Depot
|
||||||
|
---@field public priority int
|
||||||
|
---@field public entity_stop LuaEntity
|
||||||
|
---@field public entity_comb LuaEntity
|
||||||
|
---@field public network_name string?
|
||||||
|
---@field public network_flag int
|
||||||
|
|
||||||
---@class Train
|
---@class Train
|
||||||
---@field public entity LuaTrain
|
---@field public entity LuaTrain
|
||||||
---@field public layout_id uint
|
---@field public layout_id uint
|
||||||
@@ -47,7 +56,6 @@
|
|||||||
---@class Economy
|
---@class Economy
|
||||||
---@field public r_stations_all {[string]: uint[]}
|
---@field public r_stations_all {[string]: uint[]}
|
||||||
---@field public p_stations_all {[string]: uint[]}
|
---@field public p_stations_all {[string]: uint[]}
|
||||||
---@field public all_items string[]
|
|
||||||
---@field public total_ticks uint
|
---@field public total_ticks uint
|
||||||
|
|
||||||
--TODO: only init once
|
--TODO: only init once
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ function gui_opened(comb, player)
|
|||||||
}},
|
}},
|
||||||
---choose-elem-button
|
---choose-elem-button
|
||||||
{type="line", style_mods={top_padding=10}},
|
{type="line", style_mods={top_padding=10}},
|
||||||
{type="label", style="heading_3_label", caption={"cybersyn-gui.operation"}, style_mods={top_padding=7}},
|
{type="label", style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=7}},
|
||||||
{type="choose-elem-button", ref={"network"}, elem_type="signal", signal=control.first_signal, style_mods={bottom_margin=2}, actions={
|
{type="choose-elem-button", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", signal=control.first_signal, style_mods={bottom_margin=2}, actions={
|
||||||
on_elem_changed={"choose-elem-button", comb.unit_number}
|
on_elem_changed={"choose-elem-button", comb.unit_number}
|
||||||
}},
|
}},
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ local function on_train_arrives_depot(map_data, train_entity)
|
|||||||
train.manifest = nil
|
train.manifest = nil
|
||||||
train.depot_name = train_entity.station.backer_name
|
train.depot_name = train_entity.station.backer_name
|
||||||
train.status = STATUS_D
|
train.status = STATUS_D
|
||||||
map_data.trains_available[train_entity.id] = true
|
map_data.trains_available[][train_entity.id] = true
|
||||||
else
|
else
|
||||||
if train.manifest then
|
if train.manifest then
|
||||||
on_failed_delivery(map_data, train)
|
on_failed_delivery(map_data, train)
|
||||||
|
|||||||
Reference in New Issue
Block a user