Files
fs25-adsmartpickup/FS25_ADSmartPickup/adFieldSiloPlanner.lua
T

115 lines
4.9 KiB
Lua

-- AD Field Silo Planner
-- Ren planlægning for marksilo-flowet: hvilke (silo, vare) skal hjem, hvor mange lånte vogne sendes, og til
-- hvilket gårdlager. Ingen spil-kald; input muteres ikke.
ADFieldSiloPlanner = {}
function ADFieldSiloPlanner.key(siloId, fillType)
return tostring(siloId) .. ":" .. tostring(fillType)
end
local key = ADFieldSiloPlanner.key
local function dist2(a, b)
return ((a.x or 0) - (b.x or 0)) ^ 2 + ((a.z or 0) - (b.z or 0)) ^ 2
end
local function freeIn(store, fillType, reserved)
return ((store.free or {})[fillType] or 0) - (reserved[store.id .. ":" .. tostring(fillType)] or 0)
end
local function anyStoreTakes(stores, fillType, minLiters, reserved)
for _, store in ipairs(stores or {}) do
if store.markerId ~= nil and freeIn(store, fillType, reserved) >= minLiters then return true end
end
return false
end
-- Nærmeste lager med plads til hele turen; ellers det med mest plads (over tærsklen).
local function chooseStore(stores, silo, fillType, liters, minLiters, reserved)
local best, bestD, most, mostFree = nil, nil, nil, nil
for _, store in ipairs(stores or {}) do
local free = freeIn(store, fillType, reserved)
if store.markerId ~= nil and free >= minLiters then
local d = dist2(store, silo)
if free >= liters and (bestD == nil or d < bestD) then best, bestD = store, d end
if mostFree == nil or free > mostFree then most, mostFree = store, free end
end
end
return best or most
end
local function canCarry(vehicle, fillType)
if (vehicle.capacity or 0) <= 0 then return false end
if vehicle.cargo ~= nil and vehicle.cargo ~= fillType then return false end
return (vehicle.carries or {})[fillType] == true
end
local function nearestVehicle(vehicles, used, silo, fillType)
local best, bestD = nil, nil
for _, vehicle in ipairs(vehicles or {}) do
if not used[vehicle.id] and canCarry(vehicle, fillType) then
local d = dist2(vehicle, silo)
if bestD == nil or d < bestD then best, bestD = vehicle, d end
end
end
return best
end
function ADFieldSiloPlanner.plan(input)
local minLiters = input.minLiters or 10000
local reserved, onWay = {}, {}
for _, loan in ipairs(input.loans or {}) do
local k = key(loan.siloId, loan.fillType)
onWay[k] = (onWay[k] or 0) + (loan.liters or 0)
end
local status, tasks = {}, {}
for _, silo in ipairs(input.silos or {}) do
for fillType, liters in pairs(silo.stock or {}) do
if (liters or 0) >= minLiters then
local k = key(silo.id, fillType)
local entry = {siloId = silo.id, siloName = silo.name, fillType = fillType, liters = liters, onWay = onWay[k] or 0}
if silo.markerId == nil then
entry.state = "noMarker"
elseif not anyStoreTakes(input.stores, fillType, minLiters, reserved) then
entry.state = "noFarmStore"
elseif (input.paused or {})[k] then
entry.state = "paused"
else
entry.state = "ok"
local rest = liters - entry.onWay
if rest >= minLiters then table.insert(tasks, {silo = silo, fillType = fillType, rest = rest}) end
end
table.insert(status, entry)
end
end
end
table.sort(tasks, function(a, b)
if a.rest ~= b.rest then return a.rest > b.rest end
if tostring(a.silo.id) ~= tostring(b.silo.id) then return tostring(a.silo.id) < tostring(b.silo.id) end
return a.fillType < b.fillType
end)
table.sort(status, function(a, b)
if tostring(a.siloId) ~= tostring(b.siloId) then return tostring(a.siloId) < tostring(b.siloId) end
return a.fillType < b.fillType
end)
local freeSlots = math.max(0, (input.maxVehicles or 2) - #(input.loans or {}))
local used, dispatch = {}, {}
for _, task in ipairs(tasks) do
while freeSlots > 0 and task.rest >= minLiters do
local vehicle = nearestVehicle(input.vehicles, used, task.silo, task.fillType)
if vehicle == nil then break end
local liters = math.min(vehicle.capacity, task.rest)
local store = chooseStore(input.stores, task.silo, task.fillType, liters, minLiters, reserved)
if store == nil then break end
used[vehicle.id] = true
local reserveKey = store.id .. ":" .. tostring(task.fillType)
reserved[reserveKey] = (reserved[reserveKey] or 0) + liters
table.insert(dispatch, {vehicleId = vehicle.id, siloId = task.silo.id, siloMarkerId = task.silo.markerId,
fillType = task.fillType, storeId = store.id, storeMarkerId = store.markerId, liters = liters})
task.rest = task.rest - liters
freeSlots = freeSlots - 1
end
end
return {dispatch = dispatch, status = status, freeSlots = freeSlots}
end