private TradeMapSystemCollection GetSystemsInJumpRange(TradeMap map, TradeMapSystem startSystem, TradeMapSystemCollection systemCollection, int jumpRange) { // Traverse outwards from the start system using links. // Build a collection of systems within the specified jump radius from the start system TradeMapSystemCollection seenSystems = new TradeMapSystemCollection(); if (!systemCollection.Contains(startSystem)) { systemCollection.Add(startSystem); } Stack <TradeMapSystem> systemStack = new Stack <TradeMapSystem>(); systemStack.Push(startSystem); Stack <TradeMapSystem> nextSystemStack; int jumpCount = 1; while (jumpCount <= jumpRange && systemStack.Count > 0) { // Start the next stack nextSystemStack = new Stack <TradeMapSystem>(); // Add all of the linked systems while (systemStack.Count > 0) { var thisSystem = systemStack.Pop(); if (seenSystems.Contains(thisSystem)) { continue; } foreach (var link in thisSystem.Links) { if (link.System == null) { continue; } if (link.System == startSystem) { continue; // Ignore it if it's start system } // Good link nextSystemStack.Push(link.System); if (!systemCollection.Contains(link.System)) { systemCollection.Add(link.System); } seenSystems.Add(thisSystem); } } // Done with stack, ready the next stack systemStack = nextSystemStack; jumpCount++; } return(systemCollection); }
public RouteScannerResults Scan(TradeMap map, RouteScannerOptions options, CancellationToken ct) { if (_working) { throw new InvalidOperationException("Already working"); } _working = true; _log.Info("Starting route scan"); _ct = ct; _options = options; var r = new RouteScannerResults(); // Build a list of profitable runs // Loop through systems // Get list of systems within the max jump limit // Go through each system in the list, compare comodity prices // If the price is greater than the desired threshold, add it to the run list TradeMapSystemCollection systemCollection; List <string> validMapBoundSystems = new List <string>(); if (options.MapBounds.Count > 0) { // Gather map bounds systemCollection = new TradeMapSystemCollection(); foreach (var system in map.Systems) { foreach (var mapBound in options.MapBounds) { var mapBoundSystem = mapBound.Key.ToLower(); if (mapBoundSystem == system.Name.ToLower()) { validMapBoundSystems.Add(mapBoundSystem); GetSystemsInJumpRange(map, system, systemCollection, mapBound.Value); continue; } } } // Validate foreach (var mapBound in options.MapBounds) { if (!validMapBoundSystems.Contains(mapBound.Key.ToLower())) { throw new IndexOutOfRangeException($"Map bound system name isn't a known system '{mapBound.Key}'"); } } } else { // Do for all systems systemCollection = map.Systems; } for (var iSS = 0; iSS < systemCollection.Count; iSS++) { ct.ThrowIfCancellationRequested(); var startSystem = systemCollection[iSS]; if (!startSystem.CanTrade) { continue; // Don't care about system's that can't trade. } _log.Debug($"Scanning runs for system: {startSystem.Name}"); DoProgress(new ProgressEventArgs(iSS, map.Systems.Count, ProgressEventStatus.Working, $"Scanning runs for '{startSystem.Name}' ({iSS+1}/{systemCollection.Count})")); // Clear this system's runs, in case startSystem.Runs.Clear(); List <TradeMapSystem> seenSystems = new List <TradeMapSystem>(); List <TradeMapSystem> systemsInRange = new List <TradeMapSystem>(); Stack <TradeMapSystem> systemCheckStack = new Stack <TradeMapSystem>(); Stack <TradeMapSystem> systemCheckStackNext; // Add this system's links to the "to do" stack foreach (var link in startSystem.Links) { ct.ThrowIfCancellationRequested(); if (link.System == null) { continue; } if (link.System == startSystem) { continue; // Ignore it if it's start system } systemsInRange.Add(link.System); systemCheckStack.Push(link.System); } for (int jumpCount = 1; jumpCount <= options.RunMaxJumps; jumpCount++) { ct.ThrowIfCancellationRequested(); systemCheckStackNext = new Stack <TradeMapSystem>(); // Ready to compile the next list of jumps // Find new systems in the check stack while (systemCheckStack.Count > 0) { ct.ThrowIfCancellationRequested(); var destSystem = systemCheckStack.Pop(); // Check for profitable runs to this system if (destSystem.CanTrade) // Avoid systems that have comodity data, but can't actually trade { ScanForRuns(startSystem, destSystem, jumpCount, r.AllRuns, options); } // Check for jumps away from this system // but only if we're under the jump limit if (jumpCount < options.RunMaxJumps) { //_log.Debug($"{jumpCount} under jump limit {options.RunMaxJumps}, looking for more destinations"); foreach (var link in destSystem.Links) { if (link.System == null) { continue; } if (link.System == startSystem) { continue; // Ignore it if it's start system } if (seenSystems.Contains(link.System)) { continue; } // New system //_log.Debug($"Enquing system '{link.System.Name}'"); seenSystems.Add(link.System); systemsInRange.Add(link.System); systemCheckStackNext.Push(link.System); } } else { //_log.Debug($"Hit jump limit, not going further"); } } // Make the next stack the current stack systemCheckStack = systemCheckStackNext; } } // Scan for routes _log.Info("Starting scan for routes"); TradeMapSystemCollection systemsToRoute; if (options.StartSystems.Count == 0) { // No systems specified, do them all systemsToRoute = systemCollection; } else { // Systems are specified. // Resolve them to systems systemsToRoute = new TradeMapSystemCollection(); foreach (var system in map.Systems) { if (options.StartSystems.Contains(system.Name.ToLower())) { systemsToRoute.Add(system); } } } for (var iSS = 0; iSS < systemsToRoute.Count; iSS++) { ct.ThrowIfCancellationRequested(); var startSystem = systemsToRoute[iSS]; DoProgress(new ProgressEventArgs(iSS, systemsToRoute.Count, ProgressEventStatus.Working, $"Scanning routes for '{startSystem.Name}' ({iSS+1}/{systemsToRoute.Count})")); ScanForRoutes(startSystem, r.AllRoutes, options); } //DoProgress(new ProgressEventArgs(ProgressEventStatus.Complete, $"Route scanning complete")); _working = false; return(r); }