コード例 #1
0
        public static List <Loc> FindAPath(Loc rectStart, Loc rectSize, Loc start, Loc[] ends, LocTest checkBlock, LocTest checkDiagBlock)
        {
            // searches for one specific path (ends[0]), but doesn't mind hitting the other ones
            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.Heuristic = Math.Sqrt((ends[0] - start).DistSquared());
            start_tile.Cost      = 0;
            candidates.Enqueue(start_tile.Heuristic + start_tile.Cost, start_tile);

            PathTile farthest_tile = start_tile;

            while (candidates.Count > 0)
            {
                PathTile currentTile = candidates.Dequeue();
                for (int ii = 0; ii < ends.Length; ii++)
                {
                    if (currentTile.Location == ends[ii])
                    {
                        return(GetBackreference(currentTile));
                    }
                }

                if (currentTile.Heuristic < farthest_tile.Heuristic)
                {
                    farthest_tile = currentTile;
                }

                currentTile.Traversed = true;

                foreach (Dir8 dir in DirExt.VALID_DIR8)
                {
                    if (!IsDirBlocked(currentTile.Location, dir, checkBlock, checkDiagBlock))
                    {
                        Loc newLoc = currentTile.Location - rectStart + dir.GetLoc();
                        if (Collision.InBounds(rectSize.X, rectSize.Y, newLoc))
                        {
                            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.Heuristic     = Math.Sqrt((ends[0] - tile.Location).DistSquared());
                                tile.BackReference = currentTile;
                                candidates.AddOrSetPriority(tile.Heuristic + tile.Cost, tile);
                            }
                        }
                    }
                }
            }

            return(GetBackreference(farthest_tile));
        }
コード例 #2
0
 public static Loc?FindClosestConnectedTile(Loc rectStart, Loc rectSize, LocTest checkFree, LocTest checkBlock, LocTest checkDiagBlock, Loc loc)
 {
     foreach (Loc returnLoc in FindClosestConnectedTiles(rectStart, rectSize, checkFree, checkBlock, checkDiagBlock, loc, 1))
     {
         return(returnLoc);
     }
     return(null);
 }
コード例 #3
0
        public static List <Loc> FindConnectedTiles(Loc rectStart, Loc rectSize, LocTest checkOp, LocTest checkBlock, LocTest checkDiagBlock, Loc loc)
        {
            if (checkOp == null)
            {
                throw new ArgumentNullException(nameof(checkOp));
            }

            // use efficient fill method
            List <Loc> locList = new List <Loc>();

            void Action(Loc actLoc)
            {
                if (checkOp(actLoc))
                {
                    locList.Add(actLoc);
                }
            }

            AffectConnectedTiles(rectStart, rectSize, Action, checkBlock, checkDiagBlock, loc);

            return(locList);
        }
コード例 #4
0
        public static void AffectConnectedTiles(Loc rectStart, Loc rectSize, LocAction action, LocTest checkBlock, LocTest checkDiagBlock, Loc loc)
        {
            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }
            if (checkBlock == null)
            {
                throw new ArgumentNullException(nameof(checkBlock));
            }
            if (checkDiagBlock == null)
            {
                throw new ArgumentNullException(nameof(checkDiagBlock));
            }

            // create an array to cache which tiles were already traversed
            bool[][] fillArray = new bool[rectSize.X][];
            for (int ii = 0; ii < rectSize.X; ii++)
            {
                fillArray[ii] = new bool[rectSize.Y];
            }

            FloodFill(
                new Rect(rectStart, rectSize),
                (Loc testLoc) => (checkBlock(testLoc) || fillArray[testLoc.X - rectStart.X][testLoc.Y - rectStart.Y]),
                (Loc testLoc) => (checkDiagBlock(testLoc) || fillArray[testLoc.X - rectStart.X][testLoc.Y - rectStart.Y]),
                (Loc actLoc) =>
            {
                action(actLoc);
                fillArray[actLoc.X - rectStart.X][actLoc.Y - rectStart.Y] = true;
            },
                loc);
        }
コード例 #5
0
        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);
        }
コード例 #6
0
 public static List <Loc> FindPath(Loc rectStart, Loc rectSize, Loc start, Loc end, LocTest checkBlock, LocTest checkDiagBlock)
 {
     Loc[] ends = new Loc[1];
     ends[0] = end;
     return(FindAPath(rectStart, rectSize, start, ends, checkBlock, checkDiagBlock));
 }
コード例 #7
0
        private static void ScanFill(
            Rect rect,
            LocTest checkBlock,
            LocTest checkDiagBlock,
            LocAction fillOp,
            int min,
            int max,
            int range_min,
            int range_max,
            int y,
            bool isNext,
            DirV dir,
            Stack <ScanLineTile> stack)
        {
            if (checkBlock == null)
            {
                throw new ArgumentNullException(nameof(checkBlock));
            }
            if (fillOp == null)
            {
                throw new ArgumentNullException(nameof(fillOp));
            }

            // move y down or up
            int new_y = y + dir.GetLoc().Y;

            // for diagonal checking: check slightly further
            int sub = (range_min > rect.Start.X) ? 1 : 0;
            int add = (range_max < rect.Start.X + rect.Size.X - 1) ? 1 : 0;

            int line_start = -1;
            int x          = range_min - sub;

            for (; x <= range_max + add; x++)
            {
                bool unblocked = !checkBlock(new Loc(x, new_y));

                // check diagonal if applicable
                if (x < range_min)
                {
                    unblocked &= !IsDirBlocked(new Loc(range_min, y), DirExt.Combine(DirH.Left, dir), checkBlock, checkDiagBlock);
                }
                else if (x > range_max)
                {
                    unblocked &= !IsDirBlocked(new Loc(range_max, y), DirExt.Combine(DirH.Right, dir), checkBlock, checkDiagBlock);
                }

                // skip testing, if testing previous line within previous range
                bool empty = (isNext || (x <min || x> max)) && unblocked;

                if (line_start == -1 && empty)
                {
                    line_start = x;
                }
                else if (line_start > -1 && !empty)
                {
                    stack.Push(new ScanLineTile(new IntRange(line_start, x - 1), new_y, dir, line_start <= range_min, x > range_max));
                    line_start = -1;
                }

                if (line_start > -1)
                {
                    fillOp(new Loc(x, new_y));
                }

                if (!isNext && x == min)
                {
                    x = max;
                }
            }

            if (line_start > -1)
            {
                stack.Push(new ScanLineTile(new IntRange(line_start, x - 1), new_y, dir, line_start <= range_min, true));
            }
        }
コード例 #8
0
        /// <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));
        }
コード例 #9
0
 public static bool IsDirBlocked(Loc loc, Dir8 dir, LocTest checkBlock, LocTest checkDiagBlock)
 {
     return(IsDirBlocked(loc, dir, checkBlock, checkDiagBlock, 1));
 }
コード例 #10
0
 public static List <Loc>[] FindAllPaths(Loc rectStart, Loc rectSize, Loc start, Loc[] ends, LocTest checkBlock, LocTest checkDiagBlock)
 {
     return(FindMultiPaths(rectStart, rectSize, start, ends, checkBlock, checkDiagBlock, EvalPathAll, EvalFallbackAll));
 }
コード例 #11
0
        /// <summary>
        /// Determines if blocking off a specific tile would cause a path leading through it to become inaccessible.
        /// If a tile is a choke point, there are no alternate paths.
        /// </summary>
        /// <param name="rectStart">Top-Left of the rectangle to search for alternate paths.</param>
        /// <param name="rectSize">Dimensions of the rectangle to search for alternate paths.</param>
        /// <param name="point">The tile to block off.</param>
        /// <param name="checkBlock">Determines if a tile is blocked.</param>
        /// <param name="checkDiagBlock">Determines if a tile checked diagonally is blocked.</param>
        /// <returns></returns>
        public static bool IsChokePoint(Loc rectStart, Loc rectSize, Loc point, LocTest checkBlock, LocTest checkDiagBlock)
        {
            if (checkBlock == null)
            {
                throw new ArgumentNullException(nameof(checkBlock));
            }
            if (checkDiagBlock == null)
            {
                throw new ArgumentNullException(nameof(checkDiagBlock));
            }

            List <Dir8> forks = GetForkDirs(point, checkBlock, checkDiagBlock);

            if (forks.Count < 2)
            {
                return(false);
            }

            bool[][] fillArray = new bool[rectSize.X][];
            for (int ii = 0; ii < rectSize.X; ii++)
            {
                fillArray[ii] = new bool[rectSize.Y];
            }

            Loc offset_point = point - rectStart;

            fillArray[offset_point.X][offset_point.Y] = true;

            List <Loc> forkList = new List <Loc>();

            for (int ii = 0; ii < forks.Count; ii++)
            {
                Loc loc = point + forks[ii].GetLoc();
                forkList.Add(loc);
            }

            bool CheckChokeBlock(Loc loc)
            {
                if (fillArray[loc.X - rectStart.X][loc.Y - rectStart.Y])
                {
                    return(true);
                }
                return(checkBlock(loc));
            }

            bool CheckDiagChokeBlock(Loc loc)
            {
                if (fillArray[loc.X - rectStart.X][loc.Y - rectStart.Y])
                {
                    return(true);
                }
                return(checkDiagBlock(loc));
            }

            void Fill(Loc loc)
            {
                fillArray[loc.X - rectStart.X][loc.Y - rectStart.Y] = true;
                if (forkList.Contains(loc))
                {
                    forkList.Remove(loc);
                }
            }

            FloodFill(new Rect(rectStart, rectSize), CheckChokeBlock, CheckDiagChokeBlock, Fill, forkList[0]);

            return(forkList.Count > 0);
        }
コード例 #12
0
        public static IEnumerable <Loc> FindClosestConnectedTiles(Loc rectStart, Loc rectSize, LocTest checkFree, LocTest checkBlock, LocTest checkDiagBlock, Loc loc, int amount)
        {
            if (checkFree == null)
            {
                throw new ArgumentNullException(nameof(checkFree));
            }

            // create an array to cache which tiles were already traversed
            bool[][] fillArray = new bool[rectSize.X][];
            for (int ii = 0; ii < rectSize.X; ii++)
            {
                fillArray[ii] = new bool[rectSize.Y];
            }

            // use typical fill method
            StablePriorityQueue <int, Loc> locList = new StablePriorityQueue <int, Loc>();
            Loc offset_loc = loc - rectStart;

            locList.Enqueue(0, offset_loc);
            fillArray[offset_loc.X][offset_loc.Y] = true;
            int found = 0;

            while (locList.Count > 0)
            {
                Loc candidate = locList.Dequeue();

                if (checkFree(candidate + rectStart))
                {
                    yield return(candidate + rectStart);

                    found++;
                    if (found >= amount)
                    {
                        yield break;
                    }
                }

                foreach (Dir8 dir in DirExt.VALID_DIR8)
                {
                    Loc movedLoc = candidate + dir.GetLoc();
                    if (Collision.InBounds(rectSize.X, rectSize.Y, movedLoc) && !fillArray[movedLoc.X][movedLoc.Y] && !IsDirBlocked(candidate + rectStart, dir, checkBlock, checkDiagBlock))
                    {
                        Loc diff = movedLoc - offset_loc;
                        locList.Enqueue(diff.DistSquared(), movedLoc);
                        fillArray[movedLoc.X][movedLoc.Y] = true;
                    }
                }
            }
        }
コード例 #13
0
        /// <summary>
        /// Searches for the fastest path to any of the endpoints.  If multiple are tied it picks the first one.
        /// </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>
        /// <returns></returns>
        public static List <Loc> FindAPath(Loc rectStart, Loc rectSize, Loc start, Loc[] ends, LocTest checkBlock, LocTest checkDiagBlock)
        {
            List <Loc>[] multiEnds = FindMultiPaths(rectStart, rectSize, start, ends, checkBlock, checkDiagBlock, EvalPathOne, EvalFallbackOne);
            for (int ii = 0; ii < multiEnds.Length; ii++)
            {
                if (multiEnds[ii] != null)
                {
                    return(multiEnds[ii]);
                }
            }

            throw new InvalidOperationException("Could not find a path!");
        }