예제 #1
0
파일: Pathfinder.cs 프로젝트: ekolis/FrEee
 public PathfinderNode(T location, int cost, PathfinderNode <T> previousNode, int minRemaining = 0)
 {
     Location             = location;
     Cost                 = cost;
     PreviousNode         = previousNode;
     MinimumCostRemaining = minRemaining;
 }
예제 #2
0
파일: Pathfinder.cs 프로젝트: ekolis/FrEee
        public static IEnumerable <PathfinderNode <T> > CreateDijkstraMap <T>(T start, T end, ConnectivityGraph <T> graph)
        {
            // pathfind!
            // step 1: empty priority queue with cost to reach each node
            var queue = new List <PathfinderNode <T> >();

            // step 2: empty set of previously visited nodes, along with costs and previous-node references
            var visited = new SafeDictionary <T, PathfinderNode <T> >();

            // step 3: add start node and cost
            queue.Add(new PathfinderNode <T>(start, 0, null));

            // step 4: quit if there are no nodes (all paths exhausted without finding goal)
            bool success = false;

            while (queue.Any() && !success)
            {
                // step 5: take lowest cost node out of queue
                // also prefer straight line movement to diagonal
                var minCost = queue.Min(n => n.Cost);
                var node    = queue.Where(n => n.Cost == minCost).First();
                queue.Remove(node);

                // step 6: if node is the goal, stop - success!
                if (node.Location.Equals(end))
                {
                    success = true;
                }

                // step 7: check possible moves
                var moves = graph.GetExits(node.Location);

                // step 7a: remove blocked points (aka calculate cost)
                // nothing to do here

                // step 7b: update priority queue
                foreach (var move in moves)
                {
                    if (!visited.ContainsKey(move))
                    {
                        // didn't visit yet
                        var newnode = new PathfinderNode <T>(move, node.Cost + 1, node);
                        queue.Add(newnode);
                        visited.Add(move, newnode);
                    }
                    else
                    {
                        // already visited - but is it by a longer path?
                        var items = queue.Where(n => n.Location.Equals(move) && n.Cost > node.Cost + 1);
                        if (items.Any())
                        {
                            foreach (var old in items.ToArray())
                            {
                                queue.Remove(old);
                            }
                            var newnode = new PathfinderNode <T>(move, node.Cost + 1, node);
                            queue.Add(newnode);
                            visited.Add(move, newnode);
                        }
                    }
                }
            }

            return(visited.Values);
        }
예제 #3
0
파일: Pathfinder.cs 프로젝트: ekolis/FrEee
        public static IDictionary <PathfinderNode <Sector>, ISet <PathfinderNode <Sector> > > CreateDijkstraMap(IMobileSpaceObject me, Sector start, Sector end, bool avoidEnemies, bool avoidDamagingSectors)
        {
            // step 2a (do it here so we can return map if start or end is null): empty map with nodes, their costs, and previous-node references
            var map = new Dictionary <PathfinderNode <Sector>, ISet <PathfinderNode <Sector> > >();

            // if we are nowhere or we're going nowhere, that's impossible!
            if (start == null || end == null)
            {
                return(map);
            }

            var startSys = start.StarSystem;

            // pathfind!
            // step 1: empty priority queue with cost to reach each node
            var queue = new Dictionary <int, ISet <PathfinderNode <Sector> > >();

            // step 2b: empty set of previously visited nodes
            var visited = new HashSet <Sector>();

            // step 3: add start node and cost
            queue.Add(0, new HashSet <PathfinderNode <Sector> >());
            queue[0].Add(new PathfinderNode <Sector>(start, 0, null, EstimateDistance(start, end, me == null ? null : me.Owner)));

            // step 4: quit if there are no nodes (all paths exhausted without finding goal)
            bool success = false;

            while (queue.SelectMany(kvp => kvp.Value).Any() && !success)
            {
                // step 5: take lowest cost node out of queue
                // TODO - also prefer straight line movement to diagonal?
                var minCost = queue.Keys.Min();
                while (!queue[minCost].Any())
                {
                    queue.Remove(minCost);
                    minCost = queue.Keys.Min();
                }
                var node = queue[minCost].First();
                queue[minCost].Remove(node);
                map.Add(node, new HashSet <PathfinderNode <Sector> >());

                // step 6: if node is the goal, stop after it's done - success!
                if (node.Location == end)
                {
                    success = true;
                }

                // step 7: check possible moves
                var moves = GetPossibleMoves(node.Location, me == null ? true : me.CanWarp, me == null ? null : me.Owner);

                // step 7a: remove blocked points (aka calculate cost)
                if (avoidEnemies)
                {
                    // avoid enemies, except at the destination
                    moves = moves.Where(m => m == null || m == end || !m.SpaceObjects.Where(sobj => sobj.CheckVisibility(me.Owner) >= Visibility.Visible || sobj.FindMemory(me.Owner)?.Sector == m).OfType <ICombatant>().Any(sobj => sobj.IsHostileTo(me == null ? null : me.Owner)));
                }
                if (avoidDamagingSectors)
                {
                    // don't avoid the destination, even if it is a damaging sector
                    moves = moves.Where(m => m == end || m == null || !m.SpaceObjects.Where(sobj => sobj.CheckVisibility(me.Owner) >= Visibility.Visible || sobj.FindMemory(me.Owner)?.Sector == m).Any(sobj => sobj.GetAbilityValue("Sector - Damage").ToInt() > 0));
                }

                // step 7b: update priority queue
                Action <Sector> f = move =>
                {
                    // When we lock the queue, we do so because it is being checked and modified by other threads,
                    // and we don't want them stepping on each other's toes.
                    // e.g. thread 1 is checking for the existence of an item in the queue
                    // while thread 2 is busy adding that same item!
                    if (!visited.Contains(move))
                    {
                        // didn't visit yet
                        var newnode = new PathfinderNode <Sector>(move, node.Cost + 1, node, EstimateDistance(move, end, me == null ? null : me.Owner));
                        lock (queue)
                        {
                            if (!queue.ContainsKey(newnode.Cost))
                            {
                                queue.Add(newnode.Cost, new HashSet <PathfinderNode <Sector> >());
                            }
                            queue[newnode.Cost].Add(newnode);
                        }
                        if (!map.ContainsKey(node))                         // don't need to lock map, we're only adding a node which should only get added once per run
                        {
                            map.Add(node, new HashSet <PathfinderNode <Sector> >());
                        }
                        map[node].Add(newnode);
                        visited.Add(move);
                    }
                    else
                    {
                        // already visited - but is it by a longer path?
                        var moreCost = queue.Where(kvp => kvp.Key > node.Cost + 1).SelectMany(kvp => kvp.Value);
                        var items    = moreCost.Where(n => n.Location == move && n.Cost > node.Cost + 1);
                        if (items.Any())
                        {
                            PathfinderNode <Sector> newnode;

                            foreach (var old in items.ToArray())
                            {
                                lock (queue[old.Cost]) queue[old.Cost].Remove(old);
                                map.Remove(old);
                            }
                            newnode = new PathfinderNode <Sector>(move, node.Cost + 1, node);
                            lock (queue[newnode.Cost]) queue[newnode.Cost].Add(newnode);
                            if (!map.ContainsKey(node))                              // don't need to lock map, we're only adding a node which should only get added once per run
                            {
                                map.Add(node, new HashSet <PathfinderNode <Sector> >());
                            }
                            map[node].Add(newnode);
                        }
                    }
                };
                moves.SafeForeach(f);
            }

            return(map);
        }