private static List <Loc>[] FindMultiPaths(Loc rectStart, Loc rectSize, Loc start, Loc[] ends, LocTest checkBlock, LocTest checkDiagBlock, EvalPaths eval, EvalFallback fallback) { if (ends.Length == 0) { return(new List <Loc> [0]); } PathTile[][] tiles = new PathTile[rectSize.X][]; for (int ii = 0; ii < rectSize.X; ii++) { tiles[ii] = new PathTile[rectSize.Y]; for (int jj = 0; jj < rectSize.Y; jj++) { tiles[ii][jj] = new PathTile(new Loc(rectStart.X + ii, rectStart.Y + jj)); } } Loc offset_start = start - rectStart; StablePriorityQueue <double, PathTile> candidates = new StablePriorityQueue <double, PathTile>(); PathTile start_tile = tiles[offset_start.X][offset_start.Y]; start_tile.UpdateHeuristics(ends); start_tile.Cost = 0; candidates.Enqueue(start_tile.MinHeuristic + start_tile.Cost, start_tile); List <Loc>[] resultPaths = new List <Loc> [ends.Length]; PathTile[] farthestTiles = new PathTile[ends.Length]; for (int ii = 0; ii < farthestTiles.Length; ii++) { farthestTiles[ii] = start_tile; } while (candidates.Count > 0) { PathTile currentTile = candidates.Dequeue(); if (eval(ends, resultPaths, farthestTiles, currentTile)) { return(resultPaths); } currentTile.Traversed = true; foreach (Dir8 dir in DirExt.VALID_DIR8) { Loc newLoc = currentTile.Location - rectStart + dir.GetLoc(); if (Collision.InBounds(rectSize.X, rectSize.Y, newLoc)) { if (!IsDirBlocked(currentTile.Location, dir, checkBlock, checkDiagBlock)) { PathTile tile = tiles[newLoc.X][newLoc.Y]; if (tile.Traversed) { continue; } int newCost = currentTile.Cost + 1; if (tile.Cost == -1 || newCost < tile.Cost) { tile.Cost = newCost; tile.UpdateHeuristics(ends); tile.BackReference = currentTile; candidates.AddOrSetPriority(tile.MinHeuristic + tile.Cost, tile); } } } } } fallback(resultPaths, farthestTiles); return(resultPaths); }
/// <summary> /// Searches for the N fastest paths to any of the endpoints. /// </summary> /// <param name="rectStart"></param> /// <param name="rectSize"></param> /// <param name="start"></param> /// <param name="ends">The list of goal points to path to. Increase in count increases runtime linearly.</param> /// <param name="checkBlock"></param> /// <param name="checkDiagBlock"></param> /// <param name="amt">Top N</param> /// <param name="multiTie">If multiple are tied it returns all involved in the tie.</param> public static List <Loc>[] FindNPaths(Loc rectStart, Loc rectSize, Loc start, Loc[] ends, LocTest checkBlock, LocTest checkDiagBlock, int amt, bool multiTie) { EvalPaths evalPathTied = (Loc[] evalEnds, List <Loc>[] resultPaths, PathTile[] farthestTiles, PathTile currentTile) => { int foundResults = 0; int highestBackRefCount = 0; for (int ii = 0; ii < resultPaths.Length; ii++) { if (resultPaths[ii] != null) { foundResults++; highestBackRefCount = Math.Max(highestBackRefCount, resultPaths[ii].Count); } } bool addedResult = false; for (int ii = 0; ii < evalEnds.Length; ii++) { if (currentTile.Location == evalEnds[ii]) { List <Loc> proposedBackRef = GetBackreference(currentTile); // in multiTie mode, we are waiting for confirmation that we've found all ties. This means having found something that fails a tie. if (multiTie && foundResults >= amt) { // if this new proposed backref takes more steps than the current longest, then it is a failed tie if (proposedBackRef.Count > highestBackRefCount) { return(true); } } resultPaths[ii] = proposedBackRef; } if (currentTile.Heuristic[ii] < farthestTiles[ii].Heuristic[ii]) { farthestTiles[ii] = currentTile; } } // if we're just looking for the amount and not paying attention to tiebreakers, we can call it a day when we reach the amount. if (!multiTie && addedResult) { if (foundResults + 1 >= amt) { return(true); } } return(false); }; EvalFallback evalFallbackTied = (List <Loc>[] resultPaths, PathTile[] farthestTiles) => { int foundResults = 0; int highestBackRefCount = 0; StablePriorityQueue <int, int> lowestStepsQueue = new StablePriorityQueue <int, int>(); List <Loc>[] proposedPaths = new List <Loc> [resultPaths.Length]; for (int ii = 0; ii < resultPaths.Length; ii++) { if (resultPaths[ii] != null) { foundResults++; highestBackRefCount = Math.Max(highestBackRefCount, resultPaths[ii].Count); } else { proposedPaths[ii] = GetBackreference(farthestTiles[ii]); lowestStepsQueue.AddOrSetPriority(proposedPaths[ii].Count, ii); } } while (lowestStepsQueue.Count > 0) { int newIdx = lowestStepsQueue.Dequeue(); if (multiTie && foundResults >= amt) { // we've run out of ties. stop adding paths if (proposedPaths[newIdx].Count > highestBackRefCount) { break; } } resultPaths[newIdx] = proposedPaths[newIdx]; highestBackRefCount = proposedPaths[newIdx].Count; foundResults++; if (!multiTie) { if (foundResults >= amt) { break; } } } }; return(FindMultiPaths(rectStart, rectSize, start, ends, checkBlock, checkDiagBlock, evalPathTied, evalFallbackTied)); }