コード例 #1
0
ファイル: RegionPathfinder.cs プロジェクト: BenCat07/Voxalia
        /// <summary>
        /// Finds a path from the start to the end, if one exists.
        /// Current implementation is A-Star (A*).
        /// Possibly safe for Async usage.
        /// Runs two searches simultaneously (using async) from either end, and returns the shorter path (either one failing = both fail immediately!).
        /// </summary>
        /// <param name="startloc">The starting location.</param>
        /// <param name="endloc">The ending location.</param>
        /// <param name="maxRadius">The maximum radius to search through.</param>
        /// <param name="pfopts">Any pathfinder options.</param>
        /// <returns>The shortest path, as a list of blocks to travel through.</returns>
        public List <Location> FindPathAsyncDouble(Location startloc, Location endloc, double maxRadius, PathfinderOptions pfopts)
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            List <Location>         a   = new List <Location>();
            List <Location>         b   = new List <Location>();
            Task one = TheWorld.Schedule.StartAsyncTask(() =>
            {
                List <Location> f = FindPath(startloc, endloc, maxRadius, pfopts, cts);
                if (f != null)
                {
                    a.AddRange(f);
                }
            }).Created;
            Task two = TheWorld.Schedule.StartAsyncTask(() =>
            {
                List <Location> f = FindPath(endloc, startloc, maxRadius, pfopts, cts);
                if (f != null)
                {
                    b.AddRange(f);
                }
            }).Created;

            one.Wait();
            two.Wait();
            if (a.Count > 0 && b.Count > 0)
            {
                if (a.Count < b.Count)
                {
                    return(a);
                }
                else
                {
                    b.Reverse();
                    return(b);
                }
            }
            return(null);
        }
コード例 #2
0
ファイル: RegionPathfinder.cs プロジェクト: BenCat07/Voxalia
        /// <summary>
        /// Finds a path from the start to the end, if one exists.
        /// Current implementation is A-Star (A*).
        /// Thanks to fullwall for the reference sources this was originally built from.
        /// Possibly safe for Async usage.
        /// </summary>
        /// <param name="startloc">The starting location.</param>
        /// <param name="endloc">The ending location.</param>
        /// <param name="maxRadius">The maximum radius to search through.</param>
        /// <param name="pfopts">Any pathfinder options.</param>
        /// <param name="cts">A cancellation token, if any.</param>
        /// <returns>The shortest path, as a list of blocks to travel through.</returns>
        public List <Location> FindPath(Location startloc, Location endloc, double maxRadius, PathfinderOptions pfopts, CancellationTokenSource cts = null)
        {
            // TODO: Improve async safety!
            startloc = startloc.GetBlockLocation() + new Location(0.5, 0.5, 1.0);
            endloc   = endloc.GetBlockLocation() + new Location(0.5, 0.5, 1.0);
            double mrsq = maxRadius * maxRadius;
            double gosq = pfopts.GoalDist * pfopts.GoalDist;

            if (startloc.DistanceSquared(endloc) > mrsq)
            {
                cts.Cancel();
                return(null);
            }
            PathFindNodeSet                     nodes;
            PriorityQueue <PFEntry>             open;
            Dictionary <Vector3i, Chunk>        map;
            Dictionary <Vector3i, PathFindNode> closed;
            Dictionary <Vector3i, PathFindNode> openset;

            lock (PFNodeSetLock)
            {
                if (PFNodeSet.Count == 0)
                {
                    nodes   = null;
                    open    = null;
                    map     = null;
                    closed  = null;
                    openset = null;
                }
                else
                {
                    nodes   = PFNodeSet.Pop();
                    open    = PFQueueSet.Pop();
                    map     = PFMapSet.Pop();
                    closed  = PFClosedSet.Pop();
                    openset = PFOpenSet.Pop();
                }
            }
            if (nodes == null)
            {
                nodes = new PathFindNodeSet()
                {
                    Nodes = new PathFindNode[8192]
                };
                open    = new PriorityQueue <PFEntry>(8192);
                map     = new Dictionary <Vector3i, Chunk>(1024);
                closed  = new Dictionary <Vector3i, PathFindNode>(1024);
                openset = new Dictionary <Vector3i, PathFindNode>(1024);
            }
            int     nloc  = 0;
            int     start = GetNode(nodes, ref nloc, startloc.ToVec3i(), 0.0, 0.0, -1);
            PFEntry pfet;

            pfet.Nodes = nodes;
            pfet.ID    = start;
            open.Enqueue(ref pfet, 0.0);
            openset[startloc.ToVec3i()] = nodes.Nodes[start];
            while (open.Count > 0)
            {
                if (cts.IsCancellationRequested)
                {
                    return(null);
                }
                int          nextid = open.Dequeue().ID;
                PathFindNode next   = nodes.Nodes[nextid];
                if (openset.TryGetValue(next.Internal, out PathFindNode pano) && pano.F < next.F)
                {
                    continue;
                }
                openset.Remove(next.Internal);
                if (next.Internal.ToLocation().DistanceSquared(endloc) < gosq)
                {
                    open.Clear();
                    map.Clear();
                    closed.Clear();
                    openset.Clear();
                    lock (PFNodeSetLock)
                    {
                        PFNodeSet.Push(nodes);
                        PFQueueSet.Push(open);
                        PFMapSet.Push(map);
                        PFClosedSet.Push(closed);
                        PFOpenSet.Push(openset);
                    }
                    return(Reconstruct(nodes.Nodes, nextid));
                }
                if (closed.TryGetValue(next.Internal, out PathFindNode pfn) && pfn.F < next.F)
                {
                    continue;
                }
                closed[next.Internal] = next;
                foreach (Vector3i neighbor in PathFindNode.Neighbors)
                {
                    Vector3i neighb = next.Internal + neighbor;
                    if (startloc.DistanceSquared(neighb.ToLocation()) > mrsq)
                    {
                        continue;
                    }
                    // Note: Add `&& fbv.F <= next.F)` to enhance precision of results... but it makes invalid searches take forever.
                    if (closed.TryGetValue(neighb, out PathFindNode fbv))
                    {
                        continue;
                    }
                    // Note: Add `&& pfv.F <= next.F)` to enhance precision of results... but it makes invalid searches take forever.
                    if (openset.TryGetValue(neighb, out PathFindNode pfv))
                    {
                        continue;
                    }
                    // TODO: Check solidity from very solid entities too!
                    if (GetBlockMaterial(map, neighb).GetSolidity() != MaterialSolidity.NONSOLID) // TODO: Better solidity check
                    {
                        continue;
                    }
                    if (GetBlockMaterial(map, neighb + new Vector3i(0, 0, -1)).GetSolidity() == MaterialSolidity.NONSOLID &&
                        GetBlockMaterial(map, neighb + new Vector3i(0, 0, -2)).GetSolidity() == MaterialSolidity.NONSOLID &&
                        GetBlockMaterial(map, next.Internal + new Vector3i(0, 0, -1)).GetSolidity() == MaterialSolidity.NONSOLID &&
                        GetBlockMaterial(map, next.Internal + new Vector3i(0, 0, -2)).GetSolidity() == MaterialSolidity.NONSOLID)
                    {
                        // TODO: Implement CanParkour
                        continue;
                    }
                    // TODO: Implement CanSwim
                    int     node = GetNode(nodes, ref nloc, neighb, next.G + 1.0, next.F + 1.0 + neighb.ToLocation().Distance(endloc), nextid);
                    PFEntry tpfet;
                    tpfet.Nodes = nodes;
                    tpfet.ID    = node;
                    open.Enqueue(ref tpfet, nodes.Nodes[node].F);
                    openset[neighb] = nodes.Nodes[node];
                }
            }
            open.Clear();
            map.Clear();
            closed.Clear();
            openset.Clear();
            lock (PFNodeSetLock)
            {
                PFNodeSet.Push(nodes);
                PFQueueSet.Push(open);
                PFMapSet.Push(map);
                PFClosedSet.Push(closed);
                PFOpenSet.Push(openset);
            }
            cts.Cancel();
            return(null);
        }