@@ -390,12 +390,16 @@ static CPos AbstractCellForLocalCells(List<CPos> cells, byte layer)
390390 // Make sure the abstract cell is one of the available local cells.
391391 // This ensures each abstract cell we generate is unique.
392392 // We'll choose an abstract node as close to the middle of the region as possible.
393+ // If there are multiple equally close cells, choose the leftmost/topmost (arbitrary tiebreaker).
394+ // This is important so we always choose the same abstract cell, no matter the ordering of the list.
393395 var abstractCell = desired ;
394396 var distance = int . MaxValue ;
395397 foreach ( var cell in cells )
396398 {
397399 var newDistance = ( cell - desired ) . LengthSquared ;
398- if ( distance > newDistance )
400+ if ( distance > newDistance ||
401+ ( distance == newDistance && abstractCell . X > cell . X ) ||
402+ ( distance == newDistance && abstractCell . X == cell . X && abstractCell . Y > cell . Y ) )
399403 {
400404 distance = newDistance ;
401405 abstractCell = cell ;
@@ -416,6 +420,8 @@ static CPos AbstractCellForLocalCells(List<CPos> cells, byte layer)
416420 using ( var search = GetLocalPathSearch (
417421 null , new [ ] { src } , src , customCost , null , BlockedByActor . None , false , grid , 100 , null , false , null ) )
418422 {
423+ // Determinism: The order of visited cells from ExpandAll can be perturbed by cell cost changes.
424+ // Processing of this list must not take any implicit dependencies on its ordering.
419425 var localCellsInRegion = search . ExpandAll ( ) ;
420426 var abstractCell = AbstractCellForLocalCells ( localCellsInRegion , gridLayer ) ;
421427 accessibleCells . ExceptWith ( localCellsInRegion ) ;
@@ -1164,9 +1170,7 @@ Func<CPos, bool, int> Heuristic(PathSearch abstractSearch, int estimatedSearchSi
11641170 }
11651171
11661172 if ( maybeAbstractCell == null )
1167- throw new Exception (
1168- "The abstract path should never be searched for an unreachable point. " +
1169- $ "Cell { cell } failed lookup for an abstract cell.") ;
1173+ return PathGraph . PathCostForInvalidPath ;
11701174 }
11711175
11721176 var abstractCell = maybeAbstractCell . Value ;
@@ -1177,9 +1181,8 @@ Func<CPos, bool, int> Heuristic(PathSearch abstractSearch, int estimatedSearchSi
11771181 {
11781182 abstractSearch . TargetPredicate = c => c == abstractCell ;
11791183 if ( ! abstractSearch . ExpandToTarget ( ) )
1180- throw new Exception (
1181- "The abstract path should never be searched for an unreachable point. " +
1182- $ "Abstract cell { abstractCell } failed to route to abstract cell.") ;
1184+ return PathGraph . PathCostForInvalidPath ;
1185+
11831186 info = graph [ abstractCell ] ;
11841187 }
11851188
0 commit comments