From facbae6955e1185579097a9692160d72ecd5ca78 Mon Sep 17 00:00:00 2001 From: Pavel Solodovnikov Date: Wed, 13 Dec 2023 01:08:47 +0300 Subject: [PATCH] order.cpp: partial fix for `RSComparator:s` distance check `RSComparator` does not conform to strict weak ordering requirements, which are required by standard containers. More specifically, `a < b && b < a` can happen in the following scenario: 1. Player finishes an offworld mission with some damaged units and a mobile repair turret in the pack. 2. Upon returning to the base map, some units may be clumped, so that some of them may occupy exactly the same spot. 3. In this case, `RSComparator` will see two entries with equal distances, so distance-based tie breaking will fail. The patch does not fix the problem in a fundamental way, but tries to significantly reduce the probability for `a < b && b < a` to happen by introducing yet another fallback strategy: if the distance check failed, then try to compare damage levels and prefer the unit which is more damaged. Signed-off-by: Pavel Solodovnikov --- src/order.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/order.cpp b/src/order.cpp index 912029b351b..5b6ea6932b9 100644 --- a/src/order.cpp +++ b/src/order.cpp @@ -198,13 +198,20 @@ struct RSComparator if (lhighest && !rhighest) return true; if (!lhighest && rhighest) return false; - if (lhighest && rhighest) + + const auto distanceCheck = [this](const DROID* l, const DROID* r) { - // break the tie with distance check const auto ldist = (l->pos.x - selfPosX) * (l->pos.x - selfPosX) + (l->pos.y - selfPosY) * (l->pos.y - selfPosY); const auto rdist = (r->pos.x - selfPosX) * (r->pos.x - selfPosX) + (r->pos.y - selfPosY) * (r->pos.y - selfPosY); // debug(LOG_REPAIRS, "comparator called %i %i: (ldist %i >= %i rdist) %i", l->id, r->id, ldist, rdist, ldist >= rdist); - return (ldist >= rdist); + // If distances are the same, fallback to damage level check: prefer the one which is more damaged. + return ldist != rdist ? ldist > rdist : l->body > r->body; + }; + + if (lhighest && rhighest) + { + // break the tie with distance check + return distanceCheck(l, r); } // Second highest priority: @@ -215,12 +222,10 @@ struct RSComparator if (!lsecond && rsecond) return false; // at this point we don't really have a preference - // just repair closest (maybe should repair the most damaged?..) - const auto ldist = (l->pos.x - selfPosX) * (l->pos.x - selfPosX) + (l->pos.y - selfPosY) * (l->pos.y - selfPosY); - const auto rdist = (r->pos.x - selfPosX) * (r->pos.x - selfPosX) + (r->pos.y - selfPosY) * (r->pos.y - selfPosY); - // debug(LOG_REPAIRS, "comparator called %i %i: (ldist %i >= %i rdist) %i", l->id, r->id, ldist, rdist, ldist >= rdist); - return ldist >= rdist; - + // just repair closest. + // In case they have the same distance, + // fallback to the damage level check: prefer the most damaged. + return distanceCheck(l, r); } private: const int selfPosX;