Пример #1
0
        /// <summary>
        /// Find all Systems within range of the specified system.
        /// </summary>
        /// <param name="origin">The reference system.</param>
        /// <param name="range">The range to include.</param>
        /// <returns>A list of all Systems that are within range of the origin.</returns>
        public IEnumerable <KeyValuePair <string, EDSystem> > FindInRange(EDSystem origin, double range)
        {
            // We only need to consider systems within the same box as the origin, and neighbouring boxes up to the jump range away.
            // One possible optimisation here is to calculate the boxes within the spherical range, but I suspect that for all but the
            // longest routes, the constant circle-maths would cost more than it gains. Something to try out sometime.

            // Find out how many boxes away from the origin we need to inspect.
            var boxRange = ((int)range / BoxSize) + 1;

            // Build up a list of boxes that might contain reachable systems. These are the boxes we'll be searching later, so the smaller
            // we can make this list the better, and the fewer the number of systems in the boxes the better too.
            var boxes = new List <BoxKey>();

            for (int x = origin.box.X - boxRange; x <= origin.box.X + boxRange; x++)
            {
                for (int z = origin.box.Z - boxRange; z <= origin.box.Z + boxRange; z++)
                {
                    var key = new BoxKey(x, z);
                    if (_BoxedList.ContainsKey(key))
                    {
                        boxes.Add(key);
                    }
                }
            }

            // Spin through every box in our search-space, and add any systems that are actually within range to the result list.
            // This can be done concurrently, because each system is independent.
            var systems = new ConcurrentDictionary <string, EDSystem>();

            Parallel.ForEach(boxes, (box) =>
            {
                // We're doing a faster box-based range check first, so we don't have to do an expensive √((x2-x1)² + (y2-y1)² + (z2-z1)²) calculation.
                foreach (var system in _BoxedList[box].Where(x => x.Value != origin && Math.Abs(x.Value.x - origin.x) <= range && Math.Abs(x.Value.y - origin.y) <= range && Math.Abs(x.Value.z - origin.z) <= range).ToList())
                {
                    // Now we have a list of systems that have their X/Y/Z coordinates all within ±jump of the origin, find out which really are in range.
                    // (Because the corners of the cube will be further from the centre than 1 jump-range)
                    if (Astrogation.Distance(origin, system.Value) <= range)
                    {
                        systems[system.Key] = system.Value;
                    }
                }
            });

            // We now should have a simple list of Systems within the specified range of the provided origin system.
            return(systems);
        }
Пример #2
0
        public List <EDSystem> Route(EDSystem start, EDSystem end)
        {
            if (JumpRange == 0)
            {
                throw new InvalidOperationException("Jump range has not been set.");
            }

            var key = new CacheKey()
            {
                JumpRange = JumpRange, Start = start.key, End = end.key
            };

            if (_routeCache.ContainsKey(key))
            {
                Console.WriteLine($"Found a cached route from {start.name} to {end.name} in {JumpRange:n2} Ly jumps.");
                LastResultWasFromCache = true;
                return(_routeCache[key]);
            }
            else
            {
                LastResultWasFromCache = false;
            }


            var swTotal      = new Stopwatch();
            var swSort       = new Stopwatch();
            var swPriority   = new Stopwatch();
            var swNeighbours = new Stopwatch();

            swTotal.Start();

            var edsm = EDSystemManager.Instance;

            _frontier  = new SimplePriorityQueue <EDSystem>();
            _cameFrom  = new Dictionary <string, RouteNode>();
            _costSoFar = new Dictionary <string, RouteNode>();

            _frontier.Enqueue(start, 0);
            _costSoFar.Add(start.key, new RouteNode()
            {
                System = start, Distance = 0, Priority = 0
            });

            var routeFound = false;

            while (_frontier.Count > 0)
            {
                var current = _frontier.Dequeue();

                if (current == end)
                {
                    routeFound = true;
                    break;
                }

                swNeighbours.Start();
                var neighbours = edsm.FindInRange(current, JumpRange).ToList();
                swNeighbours.Stop();

                // sort neighbours to put those closest to the target first
                swSort.Start();
                neighbours.OrderBy(x => Astrogation.Distance(x.Value, end));
                swSort.Stop();

                foreach (var kvNext in neighbours)
                {
                    var next = kvNext.Value;

                    var new_cost = _costSoFar[current.key].Priority + 1;

                    if (_costSoFar.ContainsKey(next.key) == false || new_cost < _costSoFar[next.key].Priority)
                    {
                        if (_costSoFar.ContainsKey(next.key))
                        {
                            var cost = _costSoFar[next.key];
                            cost.Priority = new_cost;
                        }
                        else
                        {
                            _costSoFar.Add(next.key, new RouteNode()
                            {
                                System = next, Priority = new_cost
                            });
                        }

                        var node = new RouteNode()
                        {
                            System = current, Distance = Astrogation.Distance(current, next)
                        };

                        swPriority.Start();
                        var priority = new_cost + Astrogation.Distance(next, end);
                        swPriority.Stop();

                        _frontier.Enqueue(next, priority);

                        _cameFrom[next.key] = node;
                    }
                }
            }

            if (DebugDumpRouteGraphs)
            {
                DumpSearchSpaceGraph(start, end, _cameFrom, _costSoFar);
            }

            var path = new List <EDSystem>();

            if (routeFound || AcceptPartialRoutes)
            {
                var c = end;
                while (c != start)
                {
                    path.Add(c);
                    if (_cameFrom.ContainsKey(c.key) == false)
                    {
                        // This should only happen if there is no route to the requested destination.
                        break;
                    }
                    c = _cameFrom[c.key].System;
                }
                path.Reverse();
            }

            _routeCache.Add(key, path);

            swTotal.Stop();
            // Console.WriteLine($"total:{swTotal.ElapsedMilliseconds}, sort:{swSort.ElapsedMilliseconds}, priority:{swPriority.ElapsedMilliseconds}, neigh:{swNeighbours.ElapsedMilliseconds}");

            return(path);
        }