diff --git a/FS25_ADSmartPickup/adPool.lua b/FS25_ADSmartPickup/adPool.lua index 8c0abf9..75c7b0a 100644 --- a/FS25_ADSmartPickup/adPool.lua +++ b/FS25_ADSmartPickup/adPool.lua @@ -70,7 +70,7 @@ end local function nearest(items, x, z, keep) local best, bestDistance = nil, math.huge for _, item in ipairs(items) do - if keep(item) then + if keep == nil or keep(item) then local distance = distanceSq(item.x, item.z, x, z) if distance < bestDistance then best, bestDistance = item, distance end end @@ -80,6 +80,38 @@ end -- Når opgaven slutter, sættes puljeredskabet af — også når AutoDrive ellers selv ville parkere (påfyldte -- sæt): AD parkerer ved redskabets plads med redskabet på. Aldrig ved manuel transport. +-- Bedste pasform: den mindste traktor med hk nok; afstand afgør kun mellem traktorer inden for FIT_BAND af den +-- mindste. Over OVERSIZE_FACTOR x behovet kun når intet andet passer; ukendt traktor-hk efter de kendte. +-- Redskab uden hk-data: nærmeste (intet at matche på). tractors = allerede filtrerede kandidater. +ADPool.FIT_BAND = 1.15 +ADPool.OVERSIZE_FACTOR = 2.5 + +function ADPool.bestFit(tractors, implement, role) + local fitting = {} + for _, tractor in ipairs(tractors or {}) do + if suits(tractor, implement, role) then table.insert(fitting, tractor) end + end + local needed = implement.neededHp + if needed == nil or needed <= 0 then return nearest(fitting, implement.x, implement.z) end + local sized, oversized, unknown = {}, {}, {} + for _, tractor in ipairs(fitting) do + if tractor.hp == nil or tractor.hp <= 0 then table.insert(unknown, tractor) + elseif tractor.hp > needed * ADPool.OVERSIZE_FACTOR then table.insert(oversized, tractor) + else table.insert(sized, tractor) end + end + local pool = #sized > 0 and sized or (#unknown > 0 and unknown or oversized) + if pool ~= unknown then + local smallest = math.huge + for _, tractor in ipairs(pool) do smallest = math.min(smallest, tractor.hp) end + local band = {} + for _, tractor in ipairs(pool) do + if tractor.hp <= smallest * ADPool.FIT_BAND then table.insert(band, tractor) end + end + pool = band + end + return nearest(pool, implement.x, implement.z) +end + function ADPool.shouldReturnHome(assignment) return not assignment.manual end @@ -160,7 +192,7 @@ function ADPool.pairFor(role, fieldX, fieldZ, implements, tractors, blocked, acc end) local weakest = nil for _, implement in ipairs(candidates) do - local tractor = nearest(free, implement.x, implement.z, function(t) return suits(t, implement, role) end) + local tractor = ADPool.bestFit(free, implement, role) if tractor ~= nil then return {implement = implement, tractor = tractor} end if weakest == nil or (implement.neededHp or 0) < (weakest.neededHp or 0) then weakest = implement end end @@ -174,9 +206,11 @@ function ADPool.pairAll(role, implements, tractors, blocked) local used, result = {}, {} for _, implement in ipairs(implements or {}) do if hasRole(implement, role) and not blocked[implement.id] then - local tractor = nearest(tractors or {}, implement.x, implement.z, function(t) - return not used[t.id] and not blocked[t.id] and not t.offNetwork and suits(t, implement, role) - end) + local open = {} + for _, t in ipairs(tractors or {}) do + if not used[t.id] and not blocked[t.id] and not t.offNetwork then table.insert(open, t) end + end + local tractor = ADPool.bestFit(open, implement, role) if tractor ~= nil then used[tractor.id] = true table.insert(result, {implement = implement, tractor = tractor}) diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index 93b7ed0..c73a13d 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.55.1.0 + 1.56.0.0 <en>AD Smart Pickup</en> diff --git a/tests/test_adPool.lua b/tests/test_adPool.lua index a2235b5..eb05ea2 100644 --- a/tests/test_adPool.lua +++ b/tests/test_adPool.lua @@ -164,5 +164,19 @@ check("par-alle: ingen traktorer", #P.pairAll("unloader", wagons, {}, {}), 0) -- flow-vogne: vogn der hører til et kørende flow vises sådan (og er ikke i puljen) check("redskab: flowets vogn", ist({parkName = "P Cow Food vogn 1", hasConnect = true, flowName = "Cow Food"}), "flow|Cow Food") check("redskab: flowets vogn koblet på", ist({parkName = "P Cow Food vogn 1", hasConnect = true, flowName = "Cow Food", attachedTo = "Cow Food 1"}), "attached|Cow Food 1") +-- pasform: mindste traktor med hk nok; afstand kun inden for ±15 %; over 2,5x behovet kun som nødløsning +-- (in-game 29/9: en meget stor traktor blev sendt til et lille job og kunne ikke komme rundt) +local impl120 = {id = "k", x = 0, z = 0, neededHp = 120} +local t150 = {id = "t150", x = 900, z = 0, hp = 150} +local t170 = {id = "t170", x = 300, z = 0, hp = 180} +local t500 = {id = "t500", x = 50, z = 0, hp = 500} +check("pasform: mindste der passer vinder over nærmere", P.bestFit({t500, t170, t150}, impl120, "cultivator").id, "t150") +local t160 = {id = "t160", x = 100, z = 0, hp = 160} +check("pasform: inden for 15 % vinder den nærmeste", P.bestFit({t150, t160}, impl120, "cultivator").id, "t160") +check("pasform: kun for stor -> nødløsning", P.bestFit({t500}, impl120, "cultivator").id, "t500") +check("pasform: for svag -> ingen", P.bestFit({{id = "t90", x = 0, z = 0, hp = 90}}, impl120, "cultivator"), nil) +check("pasform: redskab uden hk-data -> nærmeste", P.bestFit({t150, t500}, {id = "x", x = 0, z = 0}, "cultivator").id, "t500") +check("pasform: ukendt traktor-hk efter kendte", P.bestFit({{id = "u", x = 0, z = 0}, t170}, impl120, "cultivator").id, "t170") +check("pasform: lastbil tæller ikke", P.bestFit({{id = "lb", x = 0, z = 0, hp = 150, category = "TRUCKS"}, t500}, impl120, "cultivator").id, "t500") print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)