beginning to add depot logic

This commit is contained in:
Monica Moniot
2022-10-30 12:36:58 -04:00
parent 202ae86c95
commit bb596ff733
8 changed files with 101 additions and 40 deletions

View File

@@ -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

View File

@@ -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")

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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}
}}, }},
}} }}

View File

@@ -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)