fix(forsyning): mindst fyldte kilde først kun blandt kilder med >= 25 % af et læs (ellers fyldeste) — foder-traktorer hentede 2-10k l i en gæringssilo der fyldes løbende; knappen viser Start for et flow der aldrig kom i gang, selv med vogn på (v1.57.0.0)

This commit is contained in:
2026-09-29 15:20:48 +02:00
parent 2454dd17e7
commit 793f792f8b
6 changed files with 53 additions and 13 deletions
+4 -1
View File
@@ -193,7 +193,10 @@ end
function ADFlowWagons.hasActivity(run, vehiclesById) function ADFlowWagons.hasActivity(run, vehiclesById)
for _, vehicleId in ipairs(run.vehicleIds or {}) do for _, vehicleId in ipairs(run.vehicleIds or {}) do
local vehicle = vehiclesById ~= nil and vehiclesById[vehicleId] or nil local vehicle = vehiclesById ~= nil and vehiclesById[vehicleId] or nil
if vehicle ~= nil and (waiting[vehicle] ~= nil or fetching[vehicle] ~= nil or #ADHitch.rearImplementsOf(vehicle) > 0) then -- vogn bagpå tæller kun i et flow der er startet: et flow der aldrig kom i gang (fx 'ingen vogn', og spilleren
-- har selv koblet på) skal vise Start, ikke Stop (in-game 29/9: Milk Store)
local holdsWagon = vehicle ~= nil and ADRunsController.runningRuns[run.id] == true and #ADHitch.rearImplementsOf(vehicle) > 0
if vehicle ~= nil and (waiting[vehicle] ~= nil or fetching[vehicle] ~= nil or holdsWagon) then
return true return true
end end
end end
+14 -6
View File
@@ -372,18 +372,26 @@ end
-- Bedste kilde der har varen: mindst fyldte først for lagervarer, fyldeste først for -- Bedste kilde der har varen: mindst fyldte først for lagervarer, fyldeste først for
-- varer hvis tanke løber fulde (ADSupplyPlanner). Siloer med varen i EGET lager går -- varer hvis tanke løber fulde (ADSupplyPlanner). Siloer med varen i EGET lager går
-- altid forud for dem der kun kan låne fra naboer. Returnerer et valg eller nil. -- altid forud for dem der kun kan låne fra naboer. Returnerer et valg eller nil.
local function findBestSource(markerStations, fillTypes, farmId) local getLoadCapacity -- defineret længere nede (læs-kapacitet til kildevalget)
local function findBestSource(markerStations, fillTypes, farmId, loadCapacity)
local prefersFullest = ADSupplyPlanner.prefersFullestSource(getFillTypeNames(fillTypes)) local prefersFullest = ADSupplyPlanner.prefersFullestSource(getFillTypeNames(fillTypes))
local ample = ADSupplyPlanner.ampleLevel(loadCapacity)
local best = {[true] = {}, [false] = {}} local best = {[true] = {}, [false] = {}}
for _, entry in ipairs(markerStations) do for _, entry in ipairs(markerStations) do
local level, isOwn = getStationFillLevel(entry.station, fillTypes, farmId) local level, isOwn = getStationFillLevel(entry.station, fillTypes, farmId)
if level >= ADSmartPickup.MIN_USEFUL_LITERS and not isProtectedProductionInput(entry.station, fillTypes) if level >= ADSmartPickup.MIN_USEFUL_LITERS and not isProtectedProductionInput(entry.station, fillTypes)
and ADSupplyPlanner.isBetterSource(level, best[isOwn].level, prefersFullest) then and ADSupplyPlanner.isBetterSource(level, best[isOwn].level, prefersFullest, ample) then
best[isOwn] = {level = level, marker = entry.marker, station = entry.station, isOwn = isOwn} best[isOwn] = {level = level, marker = entry.marker, station = entry.station, isOwn = isOwn}
end end
end end
local choice = best[true].marker ~= nil and best[true] or best[false] -- eget lager går forud — men ikke en næsten tom egen tank frem for et rigeligt nabolager
local own, other = best[true], best[false]
local choice = own
if own.marker == nil or (not prefersFullest and other.marker ~= nil and own.level < ample and other.level >= ample) then
choice = other
end
if choice.marker == nil then if choice.marker == nil then
return nil return nil
end end
@@ -412,7 +420,7 @@ function ADSmartPickup.findLeastFilledPickup(adEnv, driver)
return nil return nil
end end
local groupMarkers = adEnv.ADGraphManager:getMapMarkersInGroup(firstMarker.group) local groupMarkers = adEnv.ADGraphManager:getMapMarkersInGroup(firstMarker.group)
return findBestSource(collectMarkerStations(adEnv, groupMarkers, farmId, nil), fillTypes, farmId) return findBestSource(collectMarkerStations(adEnv, groupMarkers, farmId, nil), fillTypes, farmId, getLoadCapacity(adEnv, driver))
end end
-- Lagrene bag aflæsningsstationen; produktionspunkter uden targetStorages læses direkte. -- Lagrene bag aflæsningsstationen; produktionspunkter uden targetStorages læses direkte.
@@ -719,7 +727,7 @@ function ADSmartPickup.getTargetNeedsRanked(station, fillTypes, farmId, pending)
end end
-- Vognens samlede kapacitet (liter); nil hvis AutoDrive ikke kan opgøre den. -- Vognens samlede kapacitet (liter); nil hvis AutoDrive ikke kan opgøre den.
local function getLoadCapacity(adEnv, driver) getLoadCapacity = function(adEnv, driver)
if adEnv.AutoDrive.getAllUnits == nil or adEnv.AutoDrive.getAllFillLevels == nil then if adEnv.AutoDrive.getAllUnits == nil or adEnv.AutoDrive.getAllFillLevels == nil then
return nil return nil
end end
@@ -787,7 +795,7 @@ function ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients, targetWayPoi
-- bedste foder først: lavere foder kun når truget er nødlidende (ADSupplyPlanner.isFoodAllowed) -- bedste foder først: lavere foder kun når truget er nødlidende (ADSupplyPlanner.isFoodAllowed)
local bestFood = ADSupplyPlanner.bestFoodPriority ~= nil and ADSupplyPlanner.bestFoodPriority(ranked) or nil local bestFood = ADSupplyPlanner.bestFoodPriority ~= nil and ADSupplyPlanner.bestFoodPriority(ranked) or nil
for _, need in ipairs(ranked) do for _, need in ipairs(ranked) do
local choice = (ADSupplyPlanner.isFoodAllowed == nil or ADSupplyPlanner.isFoodAllowed(need, bestFood)) and findBestSource(markerStations, {need.fillType}, farmId) or nil local choice = (ADSupplyPlanner.isFoodAllowed == nil or ADSupplyPlanner.isFoodAllowed(need, bestFood)) and findBestSource(markerStations, {need.fillType}, farmId, loadCapacity) or nil
if choice ~= nil then if choice ~= nil then
return { return {
markerIndex = choice.markerIndex, markerIndex = choice.markerIndex,
+18 -1
View File
@@ -29,15 +29,32 @@ function ADSupplyPlanner.prefersFullestSource(fillTypeNames)
return true return true
end end
-- "Rigelig" kilde: kan levere mindst MIN_LOAD_SHARE af et læs (mindst MIN_AMPLE_LITERS).
ADSupplyPlanner.MIN_LOAD_SHARE = 0.25
ADSupplyPlanner.MIN_AMPLE_LITERS = 5000
function ADSupplyPlanner.ampleLevel(loadCapacity)
return math.max(ADSupplyPlanner.MIN_AMPLE_LITERS, (loadCapacity or 0) * ADSupplyPlanner.MIN_LOAD_SHARE)
end
-- Er en kilde med dette niveau bedre end den hidtil bedste (nil = ingen endnu)? -- Er en kilde med dette niveau bedre end den hidtil bedste (nil = ingen endnu)?
function ADSupplyPlanner.isBetterSource(level, bestLevel, prefersFullest) -- Lagervarer: mindst fyldte først, men kun blandt rigelige kilder — en næsten tom tank der fyldes løbende
-- (gæringssilo) ville ellers altid vinde og give et par tusind liter pr. tur (in-game 29/9). Ingen rigelige:
-- den fyldeste. ample = nil: gammel regel.
function ADSupplyPlanner.isBetterSource(level, bestLevel, prefersFullest, ample)
if bestLevel == nil then if bestLevel == nil then
return true return true
end end
if prefersFullest then if prefersFullest then
return level > bestLevel return level > bestLevel
end end
if ample == nil then
return level < bestLevel return level < bestLevel
end
local isAmple, bestIsAmple = level >= ample, bestLevel >= ample
if isAmple ~= bestIsAmple then return isAmple end
if isAmple then return level < bestLevel end
return level > bestLevel
end end
function ADSupplyPlanner.copyList(list) function ADSupplyPlanner.copyList(list)
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?> <?xml version="1.0" encoding="utf-8" standalone="no"?>
<modDesc descVersion="107"> <modDesc descVersion="107">
<author>masterdraco</author> <author>masterdraco</author>
<version>1.56.5.0</version> <version>1.57.0.0</version>
<title> <title>
<en>AD Smart Pickup</en> <en>AD Smart Pickup</en>
</title> </title>
+4 -3
View File
@@ -206,9 +206,10 @@ check("markør uden nær silo ignoreres", manager:getNextPickup(makeDriver(marke
-- 4: flere valgte varer lægges sammen -- 4: flere valgte varer lægges sammen
resetWorld() resetWorld()
addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[SUGARBEET] = 100, [WHEAT] = 5000})) -- summeret er silo 1 rigelig (6000 >= 5000) og vinder; uden summering var den 3000 og taber til silo 2's 4000
addMarker(2, "Silo 2", "Siloer", 500, 0); table.insert(stations, makeStation(102, 500, 15, {[SUGARBEET] = 3000})) addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[SUGARBEET] = 3000, [WHEAT] = 3000}))
check("flere varetyper summeres", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET, WHEAT}), false), 2) addMarker(2, "Silo 2", "Siloer", 500, 0); table.insert(stations, makeStation(102, 500, 15, {[SUGARBEET] = 4000}))
check("flere varetyper summeres", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET, WHEAT}), false), 1)
-- 4b: klynge med delt lager — eget indhold slår nabo-sum, også når nabo-summen er mindre -- 4b: klynge med delt lager — eget indhold slår nabo-sum, også når nabo-summen er mindre
resetWorld() resetWorld()
+11
View File
@@ -251,5 +251,16 @@ check("CF vare uden for run'et -> nil", ADSupplyPlanner.cargoFor(HAY, 5000, {SIL
check("CF tom vogn -> nil", ADSupplyPlanner.cargoFor(SILAGE, 0, {SILAGE}), nil) check("CF tom vogn -> nil", ADSupplyPlanner.cargoFor(SILAGE, 0, {SILAGE}), nil)
check("CF ukendt vare -> nil", ADSupplyPlanner.cargoFor(nil, 5000, {SILAGE}), nil) check("CF ukendt vare -> nil", ADSupplyPlanner.cargoFor(nil, 5000, {SILAGE}), nil)
-- kilde med nok til et rigtigt læs: mindst fyldte først kun blandt kilder med >= 25 % af et læs; ellers fyldeste
-- (in-game 29/9: 4 foder-traktorer hentede 2-10k l i en gæringssilo der løbende fyldes, og kørte "tomme")
local SP = ADSupplyPlanner
check("kilde-grænse: 25 % af læsset", SP.ampleLevel(1000000), 250000)
check("kilde-grænse: ukendt læs -> kun minimum", SP.ampleLevel(nil), SP.MIN_AMPLE_LITERS)
check("kilde-grænse: lille vogn -> mindst minimum", SP.ampleLevel(10000), SP.MIN_AMPLE_LITERS)
check("vælg: næsten tom taber til rigelig", SP.isBetterSource(10000, 12000000, false, 250000), false)
check("vælg: blandt rigelige mindst fyldte", SP.isBetterSource(300000, 12000000, false, 250000), true)
check("vælg: ingen rigelige -> fyldeste", SP.isBetterSource(9000, 2000, false, 250000), true)
check("vælg: løber-fuld-varer uændret (fyldeste)", SP.isBetterSource(12000000, 300000, true, 250000), true)
check("vælg: første kandidat", SP.isBetterSource(10, nil, false, 250000), true)
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1) os.exit(failures == 0 and 0 or 1)