mirror of
https://github.com/Xevion/project-cybersyn.git
synced 2025-12-10 18:08:13 -06:00
improved algorithm
This commit is contained in:
@@ -7,6 +7,7 @@ local ceil = math.ceil
|
|||||||
local INF = math.huge
|
local INF = math.huge
|
||||||
local btest = bit32.btest
|
local btest = bit32.btest
|
||||||
local band = bit32.band
|
local band = bit32.band
|
||||||
|
local table_remove = table.remove
|
||||||
|
|
||||||
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
|
||||||
@@ -295,26 +296,28 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p
|
|||||||
r_station.deliveries_total = r_station.deliveries_total + 1
|
r_station.deliveries_total = r_station.deliveries_total + 1
|
||||||
p_station.deliveries_total = p_station.deliveries_total + 1
|
p_station.deliveries_total = p_station.deliveries_total + 1
|
||||||
|
|
||||||
for _, item in ipairs(manifest) do
|
assert(manifest[1].name == primary_item_name)
|
||||||
|
for item_i, item in ipairs(manifest) do
|
||||||
assert(item.count > 0, "main.lua error, transfer amount was not positive")
|
assert(item.count > 0, "main.lua error, transfer amount was not positive")
|
||||||
|
|
||||||
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 item_network_name = network_name..":"..item.name
|
if item_i > 1 then
|
||||||
local r_stations = economy.all_r_stations[item_network_name]
|
local item_network_name = network_name..":"..item.name
|
||||||
local p_stations = economy.all_p_stations[item_network_name]
|
local r_stations = economy.all_r_stations[item_network_name]
|
||||||
--NOTE: one of these will be redundant
|
local p_stations = economy.all_p_stations[item_network_name]
|
||||||
for i, id in ipairs(r_stations) do
|
for j, 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, j)
|
||||||
break
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
for j, id in ipairs(p_stations) do
|
||||||
for i, id in ipairs(p_stations) do
|
if id == p_station_id then
|
||||||
if id == p_station_id then
|
table_remove(p_stations, j)
|
||||||
table.remove(p_stations, i)
|
break
|
||||||
break
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -339,7 +342,7 @@ local function tick_poll_depot(map_data)
|
|||||||
local tick_data = map_data.tick_data
|
local tick_data = map_data.tick_data
|
||||||
while true do
|
while true do
|
||||||
if tick_data.network == nil then
|
if tick_data.network == nil then
|
||||||
tick_data.network_name, tick_data.network = next(map_data.trains_available)
|
tick_data.network_name, tick_data.network = next(map_data.trains_available, tick_data.network_name)
|
||||||
if tick_data.network == nil then
|
if tick_data.network == nil then
|
||||||
tick_data.train_id = nil
|
tick_data.train_id = nil
|
||||||
map_data.tick_state = STATE_POLL_STATIONS
|
map_data.tick_state = STATE_POLL_STATIONS
|
||||||
@@ -478,13 +481,18 @@ local function tick_dispatch(map_data, mod_settings)
|
|||||||
local stations = map_data.stations
|
local stations = map_data.stations
|
||||||
|
|
||||||
local size = #all_names
|
local size = #all_names
|
||||||
if tick_data.start_i == nil and size > 0 then
|
if size > 0 then
|
||||||
--semi-randomized starting item
|
if tick_data.start_i == nil then
|
||||||
tick_data.start_i = 2*(map_data.total_ticks%(size/2)) + 1
|
--semi-randomized starting item
|
||||||
tick_data.offset_i = 0
|
tick_data.start_i = 2*(map_data.total_ticks%(size/2)) + 1
|
||||||
elseif size == 0 or tick_data.offset_i >= size then
|
tick_data.offset_i = 0
|
||||||
tick_data.start_i = nil
|
elseif tick_data.offset_i >= size then
|
||||||
tick_data.offset_i = nil
|
tick_data.start_i = nil
|
||||||
|
tick_data.offset_i = nil
|
||||||
|
map_data.tick_state = STATE_INIT
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
else
|
||||||
map_data.tick_state = STATE_INIT
|
map_data.tick_state = STATE_INIT
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -500,72 +508,44 @@ local function tick_dispatch(map_data, mod_settings)
|
|||||||
|
|
||||||
--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
|
--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 p_stations and #r_stations > 0 and #p_stations > 0 then
|
if p_stations and #r_stations > 0 and #p_stations > 0 then
|
||||||
if #r_stations <= #p_stations then
|
table.sort(r_stations, function(a_id, b_id)
|
||||||
--probably backpressure, prioritize locality
|
local a = stations[a_id]
|
||||||
repeat
|
local b = stations[b_id]
|
||||||
local i = map_data.total_ticks%#r_stations + 1
|
if a.priority ~= b.priority then
|
||||||
local r_station_id = table.remove(r_stations, i)
|
return a.priority < b.priority
|
||||||
|
else
|
||||||
|
return a.last_delivery_tick > b.last_delivery_tick
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
repeat
|
||||||
|
local r_station_id = table_remove(r_stations)
|
||||||
|
|
||||||
local best = 0
|
local best = 0
|
||||||
local best_depot = nil
|
local best_depot = nil
|
||||||
local best_dist = INF
|
local best_dist = INF
|
||||||
local highest_prior = -INF
|
local highest_prior = -INF
|
||||||
local could_have_been_serviced = false
|
local could_have_been_serviced = false
|
||||||
for j, p_station_id in ipairs(p_stations) do
|
for j, p_station_id in ipairs(p_stations) do
|
||||||
local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type)
|
local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type)
|
||||||
local prior = stations[p_station_id].priority
|
local prior = stations[p_station_id].priority
|
||||||
if prior > highest_prior or (prior == highest_prior and d < best_dist) then
|
if prior > highest_prior or (prior == highest_prior and d < best_dist) then
|
||||||
if depot then
|
if depot then
|
||||||
best = j
|
best = j
|
||||||
best_dist = d
|
best_dist = d
|
||||||
best_depot = depot
|
best_depot = depot
|
||||||
highest_prior = prior
|
highest_prior = prior
|
||||||
elseif d < INF then
|
elseif d < INF then
|
||||||
could_have_been_serviced = true
|
could_have_been_serviced = true
|
||||||
best = j
|
best = j
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if best_depot then
|
end
|
||||||
send_train_between(map_data, r_station_id, p_stations[best], best_depot, item_name)
|
if best_depot then
|
||||||
elseif could_have_been_serviced then
|
send_train_between(map_data, r_station_id, table_remove(p_stations, best), best_depot, item_name)
|
||||||
send_missing_train_alert_for_stops(stations[r_station_id].entity_stop, stations[p_stations[best]].entity_stop)
|
elseif could_have_been_serviced then
|
||||||
end
|
send_missing_train_alert_for_stops(stations[r_station_id].entity_stop, stations[p_stations[best]].entity_stop)
|
||||||
until #r_stations == 0
|
end
|
||||||
else
|
until #r_stations == 0
|
||||||
--prioritize round robin
|
|
||||||
repeat
|
|
||||||
local j = map_data.total_ticks%#p_stations + 1
|
|
||||||
local p_station_id = table.remove(p_stations, j)
|
|
||||||
|
|
||||||
local best = 0
|
|
||||||
local best_depot = nil
|
|
||||||
local lowest_tick = INF
|
|
||||||
local highest_prior = -INF
|
|
||||||
local could_have_been_serviced = false
|
|
||||||
for i, r_station_id in ipairs(r_stations) do
|
|
||||||
local r_station = stations[r_station_id]
|
|
||||||
local prior = r_station.priority
|
|
||||||
if prior > highest_prior or (prior == highest_prior and r_station.last_delivery_tick < lowest_tick) then
|
|
||||||
local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type)
|
|
||||||
if depot then
|
|
||||||
best = i
|
|
||||||
best_depot = depot
|
|
||||||
lowest_tick = r_station.last_delivery_tick
|
|
||||||
highest_prior = prior
|
|
||||||
elseif d < INF then
|
|
||||||
could_have_been_serviced = true
|
|
||||||
best = i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if best_depot then
|
|
||||||
send_train_between(map_data, r_stations[best], p_station_id, best_depot, item_name)
|
|
||||||
elseif could_have_been_serviced then
|
|
||||||
send_missing_train_alert_for_stops(stations[r_stations[best]].entity_stop, stations[p_station_id].entity_stop)
|
|
||||||
end
|
|
||||||
until #p_stations == 0
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user