added events

This commit is contained in:
Monica Moniot
2022-10-08 11:39:25 -04:00
parent cf8c6d650c
commit 0355639ba5
4 changed files with 275 additions and 185 deletions

View File

@@ -1,4 +1,5 @@
--require("scripts.controller")
--require("scripts.main")
require("scripts.global")
require("scripts.controller")
require("scripts.main")

View File

@@ -22,36 +22,6 @@ local function icpairs(a, start_i)
end
end
--[[
station: {
deliveries_total: int
train_limit: int
priority: int
last_delivery_tick: int
r_threshold: int >= 0
p_threshold: int >= 0
entity: FactorioStop
train_layout: [ [ {
[car_type]: true|nil
} ] ]
accepted_layouts: {
[layout_id]: true|nil
}
}
train: {
layout_id: int
depot_id: int
depot_name: string
item_slot_capacity: int
fluid_capacity: int
}
available_trains: [{
layout_id: int
capacity: int
all: [train]
}]
]]
local function create_loading_order(stop, manifest)
local condition = {}
for _, item in ipairs(manifest) do
@@ -111,7 +81,8 @@ local function get_valid_train(stations, r_station_id, p_station_id, available_t
local valid_train_exists = false
local is_fluid = item_type == "fluid"
for k, train in pairs(available_trains.all) do
for train_id, _ in pairs(available_trains) do
local train = map_data.trains[train_id]
--check cargo capabilities
--check layout validity for both stations
if
@@ -139,7 +110,7 @@ local function get_valid_train(stations, r_station_id, p_station_id, available_t
end
end
local function send_train_between(stations, r_station_id, p_station_id, train, primary_item_name, economy)
local function send_train_between(stations, r_station_id, p_station_id, train, available_trains, primary_item_name, economy)
local r_station = stations[r_station_id]
local p_station = stations[p_station_id]
@@ -152,7 +123,7 @@ local function send_train_between(stations, r_station_id, p_station_id, train, p
local item_count = v.count
local item_type = v.signal.type
if item_name and item_type and item_type ~= "virtual" then
local effective_item_count = item_count + r_station.delivery_amount[item_name]
local effective_item_count = item_count + r_station.deliveries[item_name]
if -effective_item_count >= r_station.r_threshold then
requests[item_name] = -effective_item_count
end
@@ -165,7 +136,7 @@ local function send_train_between(stations, r_station_id, p_station_id, train, p
local item_count = v.count
local item_type = v.signal.type
if item_name and item_type and item_type ~= "virtual" then
local effective_item_count = item_count + p_station.delivery_amount[item_name]
local effective_item_count = item_count + p_station.deliveries[item_name]
if effective_item_count >= p_station.p_threshold then
local r = requests[item_name]
if r then
@@ -222,8 +193,8 @@ local function send_train_between(stations, r_station_id, p_station_id, train, p
for _, item in ipairs(manifest) do
assert(item.count > 0, "main.lua error, transfer amount was not positive")
r_station.delivery_amount[item.name] = r_station.delivery_amount[item.name] + item.count
p_station.delivery_amount[item.name] = p_station.delivery_amount[item.name] - item.count
r_station.deliveries[item.name] = r_station.deliveries[item.name] + item.count
p_station.deliveries[item.name] = p_station.deliveries[item.name] - item.count
local r_stations = economy.r_stations_all[item.name]
local p_stations = economy.p_stations_all[item.name]
@@ -241,6 +212,12 @@ local function send_train_between(stations, r_station_id, p_station_id, train, p
end
end
available_trains[train.entity.id] = nil
train.status = STATUS_D_TO_P
train.p_station_id = p_station_id
train.r_station_id = r_station_id
train.manifest = manifest
do
local records = {}
records[#records + 1] = create_inactivity_order(train.depot_name)
@@ -297,7 +274,7 @@ function tick(stations, available_trains, ticks_total)
for k, v in pairs(signals) do
local item_name = v.signal.name
local item_count = v.count
local effective_item_count = item_count + station.delivery_amount[item_name]
local effective_item_count = item_count + station.deliveries[item_name]
if -effective_item_count >= station.r_threshold then
if r_stations_all[item_name] == nil then
@@ -353,7 +330,7 @@ function tick(stations, available_trains, ticks_total)
end
end
if best > 0 then
send_train_between(stations, r_station_id, p_stations[best], best_train, item_name, economy)
send_train_between(stations, r_station_id, p_stations[best], best_train, available_trains, item_name, economy)
elseif could_have_been_serviced then
failed_because_missing_trains_total = failed_because_missing_trains_total + 1
end
@@ -385,7 +362,7 @@ function tick(stations, available_trains, ticks_total)
end
end
if best > 0 then
send_train_between(stations, r_stations[best], p_station_id, best_train, item_name, economy)
send_train_between(stations, r_stations[best], p_station_id, best_train, available_trains, item_name, economy)
elseif could_have_been_serviced then
failed_because_missing_trains_total = failed_because_missing_trains_total + 1
end

56
mtc/scripts/global.lua Normal file
View File

@@ -0,0 +1,56 @@
--By Monica Moniot
--[[
global: {
total_ticks: int
stations: {[stop_id]: Station}
trains: {[train_id]: Train}
trains_available: {[train_id]: bool}
}
Station: {
deliveries_total: int
train_limit: int
priority: int
last_delivery_tick: int
r_threshold: int >= 0
p_threshold: int >= 0
entity: LuaEntity
deliveries: {
[item_name]: int
}
train_layout: [ [ {
[car_type]: bool
} ] ]
accepted_layouts: {
[layout_id]: bool
}
}
Train: {
entity: LuaEntity
layout_id: int
item_slot_capacity: int
fluid_capacity: int
depot_id: int
depot_name: string
status: int
p_station_id: stop_id
r_station_id: stop_id
manifest: [{
name: string
type: string
count: int
}]
}
]]
global.total_ticks = 0
global.stations = {}
global.trains = {}
global.trains_available = {}
STATUS_D = 0
STATUS_D_TO_P = 1
STATUS_P = 2
STATUS_P_TO_R = 3
STATUS_R = 4
STATUS_R_TO_D = 5

View File

@@ -1,163 +1,219 @@
local function get_item_amount(station, item_id)
return 0
end
local function get_valid_train(stations, r_station_i, p_station_i, available_trains)
return {}
end
local function get_distance(stations, r_station_i, p_station_i)
return 0
end
local function send_train_between(stations, r_station_i, p_station_i, train)
local r_station = stations[r_station_i]
local p_station = stations[p_station_i]
r_station.last_p_station_i = p_station_i
p_station.last_r_station_i = r_station_i
r_station.deliveries_total = r_station.deliveries_total + 1
p_station.deliveries_total = p_station.deliveries_total + 1
local r_amount = get_item_amount(r_station, item_id) + r_station.delivery_amount[item_id]
local p_amount = get_item_amount(p_station, item_id) + p_station.delivery_amount[item_id]
local transfer_amount = math.min(train.capacity, -r_amount, p_amount)
assert(transfer_amount > 0, "main.lua error, transfer amount was not positive")
r_station.delivery_amount[item_id] = r_station.delivery_amount[item_id] + transfer_amount
p_station.delivery_amount[item_id] = p_station.delivery_amount[item_id] - transfer_amount
end
--[[
station: {
deliveries_total: int
train_limit: int
requester_limit: int > 0
provider_limit: int > 0
priority: int
last_delivery_tick: int
train_layout: [ [ {
[car_type]: true|nil
} ] ]
accepted_layouts: {
[layout_id]: true|nil
--By Monica Moniot
local function on_station_built(map_data, stop)
local station = {
deliveries_total = 0,
train_limit = 100,
priority = 0,
last_delivery_tick = 0,
r_threshold = 0,
p_threshold = 0,
entity = stop,
--train_layout: [ [ {
-- [car_type]: true|nil
--} ] ]
accepted_layouts = {
--[layout_id]: true|nil
}
}
available_trains: [{
layout_id: int
capacity: int
}]
]]
map_data.stations[stop.unit_number] = station
end
local function on_station_broken(map_data, stop)
end
local function icpairs(a, start_i)
start_i = start_i%#a + 1
local i = start_i - 1
local flag = true
return function()
i = i%#a + 1
if i ~= start_i or flag then
flag = false
local v = a[i]
if v then
return i, v
local function find_and_add_all_stations(map_data)
for _, surface in pairs(game.surfaces) do
local stops = surface.find_entities_filtered({type="train-stop"})
if stops then
for k, stop in pairs(stops) do
if stop.name == BUFFER_STATION_NAME then
local station = map_data.stations[stop.unit_number]
if not station then
on_station_built(map_data, stop)
end
end
end
end
end
end
local function tick(stations, all_items, available_trains, ticks_total)
if #all_items == 0 then
return
end
local failed_because_missing_trains_total = 0
--psuedo-randomize what item (and what station) to check first so if trains available is low they choose orders psuedo-randomly
for _, item_id in icpairs(all_items, ticks_total) do
local r_stations = {}
local p_stations = {}
for station_i, station in pairs(stations) do
if station.deliveries_total < station.train_limit then
local item_amount = get_item_amount(station, item_id) + station.delivery_amount[item_id]
if -item_amount >= station.requester_limit then
table.insert(r_stations, station_i)
elseif item_amount >= station.provider_limit then
table.insert(p_stations, station_i)
local function on_failed_delivery(map_data, train)
if train.status == STATUS_D or train.status == STATUS_D_TO_P or train.status == STATUS_P then
local station = map_data.stations[train.p_station_id]
for i, item in ipairs(train.manifest) do
station.deliveries[item.name] = station.deliveries[item.name] + item.count
end
end
if train.status ~= STATUS_R_TO_D then
local station = map_data.stations[train.r_station_id]
for i, item in ipairs(train.manifest) do
station.deliveries[item.name] = station.deliveries[item.name] - item.count
end
end
--TODO: change circuit outputs
train.r_station_id = 0
train.p_station_id = 0
train.manifest = nil
--NOTE: must change train status after call or remove it from tracked trains
end
--we do not dispatch more than one train per station per tick
if #r_stations > 0 and #p_stations > 0 then
if #r_stations <= #p_stations then
--backpressure, prioritize locality
for i, r_station_i in icpairs(r_stations, ticks_total) do
local best = 0
local best_train = nil
local best_dist = math.huge
local highest_prior = -math.huge
local could_have_been_serviced = false
for j, p_station_i in ipairs(p_stations) do
local d = get_distance(stations, r_station_i, p_station_i)
local prior = stations[p_station_i].priority
if prior > highest_prior or (prior == highest_prior and d < best_dist) then
local train, is_possible = get_valid_train(stations, r_station_i, p_station_i, available_trains)
local function on_train_arrives_depot(map_data, train_entity)
local train = map_data.trains[train_entity.id]
if train then
best = j
best_dist = d
best_train = train
highest_prior = prior
elseif is_possible then
could_have_been_serviced = true
if train.manifest then
if train.status == STATUS_R_TO_D then
--succeeded delivery
train.p_station_id = 0
train.r_station_id = 0
train.manifest = nil
else
on_failed_delivery(map_data, train)
end
end
train.depot_id = train_entity.station.unit_number
train.depot_name = train_entity.station.backer_name
train.status = STATUS_D
map_data.trains_available[train_entity.id] = true
else
map_data.trains[train_entity.id] = {
depot_id = train_entity.station.unit_number,
depot_name = train_entity.station.backer_name,
status = STATUS_D,
entity = train_entity,
layout_id = 0,
item_slot_capacity = 0,
fluid_capacity = 0,
p_station = 0,
r_station = 0,
manifest = nil,
}
end
if best > 0 then
send_train_between(stations, r_station_i, p_stations[best], best_train)
table.remove(p_stations, best)
elseif could_have_been_serviced then
failed_because_missing_trains_total = failed_because_missing_trains_total + 1
map_data.trains_available[train_entity.id] = true
end
local function on_train_arrives_buffer(map_data, station_id, train)
if train.manifest then
if train.status == STATUS_D_TO_P then
if train.p_station_id == station_id then
train.status = STATUS_P
--TODO: change circuit outputs
end
elseif train.status == STATUS_P_TO_R then
if train.r_station_id == station_id then
train.status = STATUS_R
--TODO: change circuit outputs
end
else
--prioritize round robin
for j, p_station_i in icpairs(p_stations, ticks_total) do
on_failed_delivery(map_data, train)
map_data.trains[train.entity.id] = nil
end
else
--train is lost somehow, probably from player intervention
map_data.trains[train.entity.id] = nil
end
end
local best = 0
local best_train = nil
local lowest_tick = math.huge
local highest_prior = -math.huge
local could_have_been_serviced = false
for i, r_station_i in ipairs(r_stations) do
local r_station = stations[r_station_i]
local prior = r_station.priority
if prior > highest_prior or (prior == highest_prior and r_station.last_delivery_tick < lowest_tick) then
local train, is_possible = get_valid_train(stations, r_station_i, p_station_i, available_trains)
local function on_train_leaves_buffer(map_data, train)
if train.manifest then
if train.status == STATUS_P then
train.status = STATUS_P_TO_R
local station = map_data.stations[train.p_station_id]
for i, item in ipairs(train.manifest) do
station.deliveries[item.name] = station.deliveries[item.name] + item.count
end
--TODO: change circuit outputs
elseif train.status == STATUS_R then
train.status = STATUS_R_TO_D
local station = map_data.stations[train.r_station_id]
for i, item in ipairs(train.manifest) do
station.deliveries[item.name] = station.deliveries[item.name] - item.count
end
--TODO: change circuit outputs
end
end
end
local function on_train_broken(map_data, train)
if train.manifest then
on_failed_delivery(map_data, train)
map_data.trains[train.entity.id] = nil
end
end
local function on_tick(event)
tick(global.stations, global.trains_available, global.total_ticks)
global.total_ticks = global.total_ticks + 1
end
local function on_built(event)
local entity = event.entity or event.created_entity or event.destination
if not entity or not entity.valid or entity.name ~= BUFFER_STATION_NAME then return end
on_station_built(global, entity)
end
local function on_broken(event)
local entity = event.entity
if not entity or not entity.valid then return end
if entity.train then
local train = global.trains[entity.id]
if train then
best = i
best_train = train
lowest_tick = r_station.last_delivery_tick
highest_prior = prior
elseif is_possible then
could_have_been_serviced = true
on_train_broken(global, entity.train)
end
elseif entity.name == BUFFER_STATION_NAME then
on_station_broken(entity.unit_number)
end
end
end
if best > 0 then
send_train_between(stations, r_stations[best], p_station_i, best_train)
table.remove(r_stations, best)
elseif could_have_been_serviced then
failed_because_missing_trains_total = failed_because_missing_trains_total + 1
end
local function on_train_changed(event)
local train_e = event.train
local train = global.trains[train_e.id]
if train_e.state == defines.train_state.wait_station and train_e.station ~= nil then
if train_e.station.name == DEPOT_STATION_NAME then
on_train_arrives_depot(global, train_e)
elseif train_e.station.name == BUFFER_STATION_NAME then
if train then
on_train_arrives_buffer(global, train_e.station.unit_number, train)
end
end
elseif event.old_state == defines.train_state.wait_station then
if train and train.is_at_buffer then
on_train_leaves_buffer(global, train)
end
end
end
local filter_built = {{filter = "type", type = "train-stop"}}
local filter_broken = {{filter = "type", type = "train-stop"}, {filter = "rolling-stock"}}
local function register_events()
tick()
script.on_event(defines.events.on_built_entity, on_built, filter_built)
script.on_event(defines.events.on_robot_built_entity, on_built, filter_built)
script.on_event({defines.events.script_raised_built, defines.events.script_raised_revive, defines.events.on_entity_cloned}, on_built)
script.on_event(defines.events.on_pre_player_mined_item, on_broken, filter_broken)
script.on_event(defines.events.on_robot_pre_mined, on_broken, filter_broken)
script.on_event(defines.events.on_entity_died, on_broken, filter_broken)
script.on_event(defines.events.script_raised_destroy, on_broken)
script.on_event({defines.events.on_pre_surface_deleted, defines.events.on_pre_surface_cleared}, on_surface_removed)
-- script.on_nth_tick(nil)
script.on_nth_tick(controller_nth_tick, on_tick)
script.on_event(defines.events.on_train_created, on_train_built)
script.on_event(defines.events.on_train_changed_state, on_train_changed)
end
script.on_load(function()
register_events()
end)
script.on_init(function()
register_events()
end)
script.on_configuration_changed(function(data)
register_events()
end)