189 lines
12 KiB
Lua
189 lines
12 KiB
Lua
-- Kør: luajit tests/test_adOutboundPlanner.lua (fra repo-roden)
|
|
-- Ren turvurdering for udkørsel: prisregel, tærskler, politikker, rangering, opsamlingsrunde.
|
|
|
|
dofile("FS25_ADSmartPickup/adOutboundPlanner.lua")
|
|
local P = ADOutboundPlanner
|
|
|
|
local failures = 0
|
|
local function check(name, actual, expected)
|
|
if actual == expected then
|
|
print("OK " .. name)
|
|
else
|
|
failures = failures + 1
|
|
print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual)))
|
|
end
|
|
end
|
|
local function join(list) return table.concat(list or {}, ",") end
|
|
|
|
local SUGAR, FORAGE, BASKET, CRATE, BROOM = 5, 6, 7, 8, 9
|
|
local function input(products, overrides)
|
|
local result = {capacity = 20000, hasAutoload = false, sellAtPercent = 90, storeAbovePercent = 80, collectRound = false, products = products}
|
|
for key, value in pairs(overrides or {}) do result[key] = value end
|
|
return result
|
|
end
|
|
local function product(name, fillType, policy, sources, quote, store, isPallet)
|
|
return {fillType = fillType, name = name, policy = policy, isPallet = isPallet == true, sources = sources, quote = quote, store = store}
|
|
end
|
|
local quoteS = {percent = 92, price = 480, wayPointId = 900, stationName = "Grain Elevator"}
|
|
local storeSilo = {wayPointId = 400, name = "silo 1", free = 500000}
|
|
|
|
-- P1: salg ved pris over tærskel, uanset mængde
|
|
local trip = P.plan(input({product("SUGAR", SUGAR, "sell", {{wayPointId = 1, available = 5000, level = 5000, capacity = 180000}}, quoteS, nil)}))
|
|
check("P1 salg", trip.kind, "sell")
|
|
check("P1 mål", trip.targetWayPointId, 900)
|
|
check("P1 målnavn", trip.targetName, "Grain Elevator")
|
|
check("P1 kilde", join(trip.sourceIds), "1")
|
|
check("P1 vare", join(trip.fillTypes), "5")
|
|
check("P1 procent", trip.percent, 92)
|
|
|
|
-- P2: pris under tærskel, lager lavt -> ingen tur med begrundelse
|
|
local low = {percent = 78, price = 400, wayPointId = 900, stationName = "Grain Elevator"}
|
|
local trip2, reasons2 = P.plan(input({product("SUGAR", SUGAR, "sell", {{wayPointId = 1, available = 74000, level = 74000, capacity = 180000}}, low, nil)}))
|
|
check("P2 ingen tur", trip2, nil)
|
|
check("P2 begrundelse", reasons2[1], "SUGAR 78 % af top (kræver 90), lager 41 %")
|
|
|
|
-- P3: sellElseStore, pris lav, lager 85 % -> lager
|
|
local trip3 = P.plan(input({product("SUGAR", SUGAR, "sellElseStore", {{wayPointId = 1, available = 153000, level = 153000, capacity = 180000}}, low, storeSilo)}))
|
|
check("P3 lager", trip3.kind, "store")
|
|
check("P3 silo", trip3.targetWayPointId, 400)
|
|
|
|
-- P4: store med fuldt læs, lager 20 %
|
|
local trip4 = P.plan(input({product("FORAGE", FORAGE, "store", {{wayPointId = 2, available = 700000, level = 700000, capacity = 3500000}}, nil, storeSilo)}))
|
|
check("P4 fuldt læs -> lager", trip4.kind, "store")
|
|
local trip4b = P.plan(input({product("FORAGE", FORAGE, "store", {{wayPointId = 2, available = 15000, level = 15000, capacity = 3500000}}, nil, storeSilo)}))
|
|
check("P4 for lidt og lager lavt -> nil", trip4b, nil)
|
|
|
|
-- P5: rangering: højeste pris-% først
|
|
local trip5 = P.plan(input({
|
|
product("SUGAR", SUGAR, "sell", {{wayPointId = 1, available = 1000, level = 1000, capacity = 180000}}, {percent = 91, price = 1, wayPointId = 900, stationName = "A"}, nil),
|
|
product("FORAGE", FORAGE, "sell", {{wayPointId = 2, available = 1000, level = 1000, capacity = 3500000}}, {percent = 95, price = 1, wayPointId = 901, stationName = "B"}, nil),
|
|
}))
|
|
check("P5 højeste procent vinder", join(trip5.fillTypes), "6")
|
|
check("P5 uden runde kun én vare", #trip5.fillTypes, 1)
|
|
|
|
-- P6: intet nåeligt salgssted
|
|
local trip6, reasons6 = P.plan(input({product("SUGAR", SUGAR, "sell", {{wayPointId = 1, available = 170000, level = 170000, capacity = 180000}}, nil, nil)}))
|
|
check("P6 ingen tur", trip6, nil)
|
|
check("P6 begrundelse", reasons6[1], "SUGAR: intet nåeligt salgssted")
|
|
|
|
-- P7: ingen silo med plads ved lagerbehov
|
|
local trip7, reasons7 = P.plan(input({product("FORAGE", FORAGE, "store", {{wayPointId = 2, available = 3000000, level = 3000000, capacity = 3500000}}, nil, nil)}))
|
|
check("P7 ingen tur", trip7, nil)
|
|
check("P7 begrundelse", reasons7[1], "FORAGE: intet lager med plads (silo eller egen fabrik der bruger varen)")
|
|
|
|
-- P8: runde, bulk: samme vare fra flere kilder, mest først
|
|
local trip8 = P.plan(input({product("SUGAR", SUGAR, "sell", {{wayPointId = 1, available = 3000, level = 3000, capacity = 180000}, {wayPointId = 3, available = 9000, level = 9000, capacity = 180000}}, quoteS, nil)}, {collectRound = true, capacity = 10000}))
|
|
check("P8 kilder mest først", join(trip8.sourceIds), "3,1")
|
|
check("P8 én vare", #trip8.fillTypes, 1)
|
|
|
|
-- P9: runde, UAL: alle pallevarer med samme mål
|
|
local sellS = function(pct) return {percent = pct, price = 1, wayPointId = 950, stationName = "Halm-salg"} end
|
|
local sellT = {percent = 96, price = 1, wayPointId = 951, stationName = "Andet"}
|
|
local pallets = {
|
|
product("STRAW_BASKET", BASKET, "sell", {{wayPointId = 5, available = 8000, level = 8, capacity = 4000}}, sellS(95), nil, true),
|
|
product("STRAW_CRATE", CRATE, "sell", {{wayPointId = 5, available = 2000, level = 2, capacity = 4000}}, sellS(93), nil, true),
|
|
product("STRAW_BROOM", BROOM, "sell", {{wayPointId = 6, available = 1000, level = 1, capacity = 4000}}, sellT, nil, true),
|
|
}
|
|
local trip9 = P.plan(input(pallets, {collectRound = true, hasAutoload = true}))
|
|
check("P9 højeste procent = kost alene (andet mål)", join(trip9.fillTypes), "9")
|
|
local trip9b = P.plan(input({pallets[1], pallets[2]}, {collectRound = true, hasAutoload = true}))
|
|
check("P9 kurve+kasser samme mål", join(trip9b.fillTypes), "7,8")
|
|
check("P9 navne", join(trip9b.names), "STRAW_BASKET,STRAW_CRATE")
|
|
check("P9 kilder unikke", join(trip9b.sourceIds), "5")
|
|
local trip9c = P.plan(input({pallets[1], pallets[2]}, {collectRound = true, hasAutoload = false}))
|
|
check("P9 uden UAL kun én pallevare", #trip9c.fillTypes, 1)
|
|
|
|
-- P10: uden runde kun én kilde (den med mest)
|
|
local trip10 = P.plan(input({product("SUGAR", SUGAR, "sell", {{wayPointId = 1, available = 3000, level = 3000, capacity = 180000}, {wayPointId = 3, available = 9000, level = 9000, capacity = 180000}}, quoteS, nil)}))
|
|
check("P10 én kilde, den fyldeste", join(trip10.sourceIds), "3")
|
|
|
|
-- P11: intet på lager
|
|
local trip11, reasons11 = P.plan(input({product("SUGAR", SUGAR, "sell", {}, quoteS, nil)}))
|
|
check("P11 ingen tur", trip11, nil)
|
|
check("P11 begrundelse", reasons11[1], "SUGAR: intet på lager")
|
|
|
|
-- P12: evaluate
|
|
local e = P.evaluate(product("SUGAR", SUGAR, "sell", {{wayPointId = 1, available = 150000, level = 150000, capacity = 180000}}, quoteS, nil), input({}))
|
|
check("P12 storagePercent", math.floor(e.storagePercent + 0.5), 83)
|
|
check("P12 storageHigh", e.storageHigh, true)
|
|
check("P12 fullLoad", e.fullLoad, true)
|
|
check("P12 priceOk", e.priceOk, true)
|
|
|
|
-- P13 (reserve): det gårdens fabrikker/stalde skal bruge, sælges ikke
|
|
local BEET = 10
|
|
local withReserve = product("SUGARBEET", BEET, "sell", {{wayPointId = 3, available = 268000, level = 268000, capacity = 12000000}}, quoteS, nil)
|
|
withReserve.reserve, withReserve.reservedFor = 196000, {"TMR-fabrik"}
|
|
local trip13 = P.plan(input({withReserve}))
|
|
check("P13 fuldt læs uden om reserven -> salg", trip13 ~= nil and trip13.kind, "sell")
|
|
withReserve.reserve = 260000 -- kun 8.000 til overs: mindre end et læs
|
|
local trip13b, reasons13b = P.plan(input({withReserve}))
|
|
check("P13 reserven ville blive rørt -> ingen tur", trip13b, nil)
|
|
check("P13 begrundelse", reasons13b[1], "SUGARBEET: 260.0k reserveret til TMR-fabrik")
|
|
withReserve.reserve = 300000 -- alt reserveret
|
|
check("P13 alt reserveret -> ingen tur", P.plan(input({withReserve})), nil)
|
|
withReserve.reserve = 0
|
|
check("P13 reserve 0 -> salg uanset mængde", P.plan(input({withReserve})).kind, "sell")
|
|
-- lager-politik ignorerer reserven (varen bliver på gården)
|
|
local storeReserve = product("SUGARBEET", BEET, "store", {{wayPointId = 3, available = 268000, level = 11000000, capacity = 12000000}}, nil, storeSilo)
|
|
storeReserve.reserve = 300000
|
|
check("P13 lager rører ikke reserven", P.plan(input({storeReserve})).kind, "store")
|
|
local e13 = P.evaluate(withReserve, input({}, {capacity = 20000}))
|
|
check("P13 evaluate sellable", e13.sellable, 268000)
|
|
|
|
-- P14 (paller): kapacitet ukendt for en autoload-bil -> "fuldt læs" = mindst PALLET_FULL_LOAD_COUNT paller klar
|
|
local function palletInput(products)
|
|
local inp = input(products, {hasAutoload = true})
|
|
inp.capacity = nil
|
|
return inp
|
|
end
|
|
local palletProduct = product("STRAW_BASKET", BASKET, "store", {{wayPointId = 5, available = 3000, level = 1600, capacity = 8000, palletCount = 3}}, nil, storeSilo, true)
|
|
check("P14 3 paller: ikke fuldt læs", P.plan(palletInput({palletProduct})), nil)
|
|
palletProduct.sources[1].palletCount = 4
|
|
check("P14 4 paller: lager-tur", P.plan(palletInput({palletProduct})).kind, "store")
|
|
local twoSources = product("STRAW_BASKET", BASKET, "store", {{wayPointId = 5, available = 2000, level = 1600, capacity = 8000, palletCount = 2}, {wayPointId = 3, available = 2000, level = 2, capacity = 4000, palletCount = 2}}, nil, storeSilo, true)
|
|
check("P14 paller summeres over kilder", P.plan(palletInput({twoSources})).kind, "store")
|
|
check("P14 evaluate palletCount", P.evaluate(twoSources, palletInput({})).palletCount, 4)
|
|
|
|
-- P15: kilder der deler lager (mælketanke = udvidelser inden for storageRadius) regnes som ét stop;
|
|
-- den første i forløbets rækkefølge beholdes, rækkefølgen bevares
|
|
local storA, storB, storC = {}, {}, {}
|
|
local pools = {[1] = {storA, storB}, [2] = {storB}, [3] = {storC}, [4] = {storA}, [5] = nil}
|
|
local srcs = {{wayPointId = 1}, {wayPointId = 2}, {wayPointId = 3}, {wayPointId = 4}, {wayPointId = 5}}
|
|
local kept, merged = P.dedupeSharedSources(srcs, function(source) return pools[source.wayPointId] end)
|
|
check("P15 behold 1, 3, 5", join((function() local ids = {} for _, s in ipairs(kept) do table.insert(ids, s.wayPointId) end return ids end)()), "1,3,5")
|
|
check("P15 slået sammen: 2 og 4 ind i 1", join(merged[1]), "2,4")
|
|
check("P15 kilde uden lagre beholdes", kept[3].wayPointId, 5)
|
|
local none = P.dedupeSharedSources({}, function() return nil end)
|
|
check("P15 tom liste", #none, 0)
|
|
|
|
-- P16: fodring ("Lager til" valgt = feedMode): kør så snart kilden har FEED_MIN_LITERS — ikke fuldt læs / 80 %
|
|
local feedStore = {wayPointId = 50, name = "Slurry Drop 1", free = 100000}
|
|
local feedProduct = product("LIQUIDMANURE", FORAGE, "store", {{wayPointId = 2, available = 20000, level = 20000, capacity = 500000}}, nil, feedStore)
|
|
local tripNoFeed = P.plan(input({feedProduct}, {capacity = 100000}))
|
|
check("P16 uden fodring: 4 % og intet fuldt læs -> ingen tur", tripNoFeed, nil)
|
|
local tripFeed = P.plan(input({feedProduct}, {capacity = 100000, feedMode = true}))
|
|
check("P16 fodring: 20k >= minimum -> lagertur", tripFeed and tripFeed.kind, "store")
|
|
check("P16 fodring: mål = valgt drop", tripFeed and tripFeed.targetWayPointId, 50)
|
|
local tinyProduct = product("LIQUIDMANURE", FORAGE, "store", {{wayPointId = 2, available = 3000, level = 3000, capacity = 500000}}, nil, feedStore)
|
|
check("P16 fodring: under minimum -> ingen tur", P.plan(input({tinyProduct}, {capacity = 100000, feedMode = true})), nil)
|
|
|
|
-- P17: valgt lager ("Lager til") kan ikke bruges -> begrundelsen er den præcise grund, ikke "intet lager med plads"
|
|
local blocked = product("EGG", FORAGE, "store", {{wayPointId = 2, available = 3000000, level = 3000000, capacity = 3500000}}, nil, nil)
|
|
blocked.storeReason = "Bale & Pallet Storage: ingen vej til 'Chicken Storage Unload'"
|
|
local _, reasons17 = P.plan(input({blocked}))
|
|
check("P17 præcis grund i status", reasons17[1], "EGG: Bale & Pallet Storage: ingen vej til 'Chicken Storage Unload'")
|
|
|
|
-- SA: "sælg altid over X %" — kilden over grænsen sælges selvom prisen er lav (gylle ved BGA'en, 26/9)
|
|
local lowQuote = {percent = 40, price = 0.5, top = 1.25, wayPointId = 77, stationName = "Liquid Buy"}
|
|
local fullSlurry = {{wayPointId = 5, available = 900000, level = 900000, capacity = 1000000}}
|
|
local lowSlurry = {{wayPointId = 5, available = 300000, level = 300000, capacity = 1000000}}
|
|
local saOff = P.plan(input({product("LIQUIDMANURE", 40, "sell", fullSlurry, lowQuote, nil)}))
|
|
check("SA fra: lav pris -> ikke salg", saOff ~= nil and saOff.kind or "intet", "intet")
|
|
local saOn = P.plan(input({product("LIQUIDMANURE", 40, "sell", fullSlurry, lowQuote, nil)}, {sellAbovePercent = 80}))
|
|
check("SA 90 % fyldt over 80 %: sælg trods lav pris", saOn ~= nil and saOn.kind, "sell")
|
|
local saLow = P.plan(input({product("LIQUIDMANURE", 40, "sell", lowSlurry, lowQuote, nil)}, {sellAbovePercent = 80}))
|
|
check("SA 30 % fyldt: ikke salg", saLow ~= nil and saLow.kind or "intet", "intet")
|
|
|
|
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
|
os.exit(failures == 0 and 0 or 1)
|