/// <summary> /// Initializes this manager. /// </summary> private void Init() { // --> Init shared component or ensure consistency Instance.SharedControlElements.StoragePartitioner.CreateOrEnsureZones(_config.ZoningConfiguration); // --> Store information about the station to which a cache storage location belongs _cacheStations = new VolatileIDDictionary <Waypoint, OutputStation>( Instance.SharedControlElements.StoragePartitioner.CachePartitions.SelectMany(kvp => kvp.Value.Select(wp => new VolatileKeyValuePair <Waypoint, OutputStation>(wp, kvp.Key))).ToList()); // --> Init scoring List <Func <double> > scorers = new List <Func <double> >(); // First select cache or not depending on decision scorers.Add(() => { // Check whether the current location belongs to the current station's cache and the pod does make the cut according to the cache score if (_currentCacheable && _currentStorageLocation.InfoTagCache == ZoneType.Cache && _cacheStations[_currentStorageLocation] == _currentStation) { // It fits, reward it return(-1); } // Check whether the pod shouldn't be cached and the storage location does not belong to a cache else if (!_currentCacheable && _currentStorageLocation.InfoTagCache != ZoneType.Cache) { // It fits, reward it return(-1); } // Check whether the storage location belongs to a foreign station's cache else if (_currentStorageLocation.InfoTagCache == ZoneType.Cache) { // Heavily penalize using foreign cache storage locations return(1); } else { // It does not fit, penalize it return(0); } }); // Then select the nearest one scorers.Add(() => { switch (_config.PodDisposeRule) { case CacheStorageLocationSelectionRule.Euclid: return(Distances.CalculateEuclid(_currentPodLocation, _currentStorageLocation, Instance.WrongTierPenaltyDistance)); case CacheStorageLocationSelectionRule.Manhattan: return(Distances.CalculateManhattan(_currentPodLocation, _currentStorageLocation, Instance.WrongTierPenaltyDistance)); case CacheStorageLocationSelectionRule.ShortestPath: return(Distances.CalculateShortestPathPodSafe(_currentPodLocation, _currentStorageLocation, Instance)); case CacheStorageLocationSelectionRule.ShortestTime: return(Distances.CalculateShortestTimePathPodSafe(_currentPodLocation, _currentStorageLocation, Instance)); default: throw new ArgumentException("Unknown pod dispose rule: " + _config.PodDisposeRule); } }); // Instantiate best candidate assessment _bestCandidateSelector = new BestCandidateSelector(false, scorers.ToArray()); }
/// <summary> /// Decides the waypoint to use when storing a pod. This call is measured by the timing done. /// </summary> /// <param name="pod">The pod to store.</param> /// <returns>The waypoint to use.</returns> protected override Waypoint GetStorageLocationForPod(Pod pod) { double minDistance = double.PositiveInfinity; Waypoint bestStorageLocation = null; foreach (var storageLocation in Instance.ResourceManager.UnusedPodStorageLocations) { // Calculate the distance double distance; switch (_config.PodDisposeRule) { case StationBasedPodStorageLocationDisposeRule.Euclid: distance = (_config.OutputStationMode ? Instance.OutputStations.Cast <Circle>() : Instance.InputStations.Cast <Circle>()) .Min(station => Distances.CalculateEuclid(station, storageLocation, Instance.WrongTierPenaltyDistance)); break; case StationBasedPodStorageLocationDisposeRule.Manhattan: distance = (_config.OutputStationMode ? Instance.OutputStations.Cast <Circle>() : Instance.InputStations.Cast <Circle>()) .Min(station => Distances.CalculateManhattan(station, storageLocation, Instance.WrongTierPenaltyDistance)); break; case StationBasedPodStorageLocationDisposeRule.ShortestPath: distance = (_config.OutputStationMode ? Instance.OutputStations.Select(s => s.Waypoint) : Instance.InputStations.Select(s => s.Waypoint)) .Min(station => Distances.CalculateShortestPathPodSafe(storageLocation, station, Instance)); break; case StationBasedPodStorageLocationDisposeRule.ShortestTime: distance = (_config.OutputStationMode ? Instance.OutputStations.Select(s => s.Waypoint) : Instance.InputStations.Select(s => s.Waypoint)) .Min(station => Distances.CalculateShortestTimePathPodSafe(storageLocation, station, Instance)); break; default: throw new ArgumentException("Unknown pod dispose rule: " + _config.PodDisposeRule); } // Update minimum if (distance < minDistance) { minDistance = distance; bestStorageLocation = storageLocation; } } // Check success if (bestStorageLocation == null) { throw new InvalidOperationException("There was no suitable storage location for the pod: " + pod.ToString()); } // Return it return(bestStorageLocation); }
/// <summary> /// Gets the storage location most suitable for the given pod. /// </summary> /// <param name="pod">The pod to get a storage location for.</param> /// <param name="tripStart">The current location of the pod / where the trip starts (to improve the distance traveled).</param> /// <returns>The storage location to use for the given pod.</returns> public Waypoint GetStorageLocation(Pod pod, Waypoint tripStart) { // Search for a storage location that is available around the best rank int storageLocationRank = DetermineRank(pod); int ranksAssessed = 0; Waypoint bestStorageLocation = null; double bestTripTime = double.PositiveInfinity; bool goBetter = false; int betterRank = storageLocationRank; int worseRank = storageLocationRank; while ( // keep searching as long as not enough ranks have been assessed and ... ranksAssessed < _config.RankCorridor || // no storage location was found at all bestStorageLocation == null) { // Change search direction goBetter = !goBetter; // Get current rank int currentRank = goBetter ? betterRank : worseRank; // Update index for next iteration if (goBetter) { betterRank--; } else { worseRank++; } // Check all unused storage locations of the rank foreach (var storageLocation in _instance.ElementMetaInfoTracker.GetStorageLocationsOfRank(currentRank).Where(sl => !_instance.ResourceManager.IsStorageLocationClaimed(sl))) { // Check whether storage location is a new best double tripTime = Distances.CalculateShortestTimePathPodSafe(tripStart, storageLocation, _instance); if (tripTime < bestTripTime) { bestTripTime = tripTime; bestStorageLocation = storageLocation; } } // Keep track of assessed locations ranksAssessed++; } // Return it return(bestStorageLocation); }
/// <summary> /// Returns a suitable storage location for the given pod. /// </summary> /// <param name="pod">The pod to fetch a storage location for.</param> /// <returns>The storage location to use.</returns> protected override Waypoint GetStorageLocationForPod(Pod pod) { double minDistance = double.PositiveInfinity; Waypoint bestStorageLocation = null; Waypoint podLocation = // Get current waypoint of pod, if we want to estimate a path _config.PodDisposeRule == NearestPodStorageLocationDisposeRule.ShortestPath || _config.PodDisposeRule == NearestPodStorageLocationDisposeRule.ShortestTime ? Instance.WaypointGraph.GetClosestWaypoint(pod.Tier, pod.X, pod.Y) : null; foreach (var storageLocation in Instance.ResourceManager.UnusedPodStorageLocations) { // Calculate the distance double distance; switch (_config.PodDisposeRule) { case NearestPodStorageLocationDisposeRule.Euclid: distance = Distances.CalculateEuclid(pod, storageLocation, Instance.WrongTierPenaltyDistance); break; case NearestPodStorageLocationDisposeRule.Manhattan: distance = Distances.CalculateManhattan(pod, storageLocation, Instance.WrongTierPenaltyDistance); break; case NearestPodStorageLocationDisposeRule.ShortestPath: distance = Distances.CalculateShortestPathPodSafe(podLocation, storageLocation, Instance); break; case NearestPodStorageLocationDisposeRule.ShortestTime: distance = Distances.CalculateShortestTimePathPodSafe(podLocation, storageLocation, Instance); break; default: throw new ArgumentException("Unknown pod dispose rule: " + _config.PodDisposeRule); } // Update minimum if (distance < minDistance) { minDistance = distance; bestStorageLocation = storageLocation; } } // Check success if (bestStorageLocation == null) { throw new InvalidOperationException("There was no suitable storage location for the pod: " + pod.ToString()); } // Return it return(bestStorageLocation); }
/// <summary> /// Reallocates the pods to different classes. /// </summary> private void ReallocatePods() { // Sort pods by their current average frequency _podsOrdered = Instance.Pods .OrderBy(p => p.ItemDescriptionsContained.Any() ? p.ItemDescriptionsContained.Average(item => item.OrderCount) : 0) .ThenBy(p => Instance.OutputStations.Min(o => { double value = 0; switch (_storageLocationClassRule) { case TurnoverPodStorageLocationClassRule.OutputStationDistanceEuclidean: value = Distances.CalculateEuclid(p, o, Instance.WrongTierPenaltyDistance); break; case TurnoverPodStorageLocationClassRule.OutputStationDistanceManhattan: value = Distances.CalculateManhattan(p, o, Instance.WrongTierPenaltyDistance); break; case TurnoverPodStorageLocationClassRule.OutputStationDistanceShortestPath: value = Distances.CalculateShortestPathPodSafe(Instance.WaypointGraph.GetClosestWaypoint(p.Tier, p.X, p.Y), o.Waypoint, Instance); break; case TurnoverPodStorageLocationClassRule.OutputStationDistanceShortestTime: value = Distances.CalculateShortestTimePathPodSafe(Instance.WaypointGraph.GetClosestWaypoint(p.Tier, p.X, p.Y), o.Waypoint, Instance); break; default: throw new ArgumentException("Unknown storage location rule: " + _storageLocationClassRule); } return(value); })) .ToList(); // Determine the shares of the capacity of the storage location classes double overallCapacity = _classStorageLocations.Sum(l => l.Count); Dictionary <int, double> classStorageCapacityShares = new Dictionary <int, double>(); for (int i = 0; i < _classCount; i++) { classStorageCapacityShares[i] = _classStorageLocations[i].Count / overallCapacity; } // Group pods to classes _podClasses = new Dictionary <Pod, int>(); _classStoragePods = Enumerable.Range(0, _classCount).Select(i => new List <Pod>()).ToArray(); int currentClass = 0; int podCount = 0; double aggregatedRelativeCapacity = classStorageCapacityShares[0]; foreach (var pod in _podsOrdered) { podCount++; // See whether the pod still fits in the current storage location area if ((double)podCount / _podsOrdered.Count > aggregatedRelativeCapacity) { // Update virtual capacity currentClass++; if (currentClass < _classCount) { aggregatedRelativeCapacity += classStorageCapacityShares[currentClass]; } } // Assign the pod to the class _podClasses[pod] = currentClass; _classStoragePods[currentClass].Add(pod); // Mark the pods pod.InfoTagPodStorageType = (_classCount - currentClass - 1.0) / (_classCount - 1.0); pod.InfoTagPodStorageInfo = "Class" + currentClass; } // Log this reallocation Instance.LogInfo("Reallocated pods - classes: " + string.Join(";", Enumerable.Range(0, _classCount).Select(c => c + ":" + _classStoragePods[c].Count))); // Mark this re-allocation _lastReallocation = Instance.Controller.CurrentTime; }
/// <summary> /// Initializes this manager. /// </summary> private void Init() { // Remember initialization _initialized = true; // Fetch additional settings if (Instance.Controller.PodStorageManager is TurnoverPodStorageManager) { _storageLocationClassRule = (Instance.ControllerConfig.PodStorageConfig as TurnoverPodStorageConfiguration).StorageLocationClassRule; } // Initialize ordered storage location list _storageLocationsOrdered = Instance.Waypoints .Where(wp => wp.PodStorageLocation) .OrderBy(wp => Instance.OutputStations.Min(o => { double value = 0; switch (_storageLocationClassRule) { case TurnoverPodStorageLocationClassRule.OutputStationDistanceEuclidean: value = Distances.CalculateEuclid(wp, o, Instance.WrongTierPenaltyDistance); break; case TurnoverPodStorageLocationClassRule.OutputStationDistanceManhattan: value = Distances.CalculateManhattan(wp, o, Instance.WrongTierPenaltyDistance); break; case TurnoverPodStorageLocationClassRule.OutputStationDistanceShortestPath: value = Distances.CalculateShortestPathPodSafe(wp, o.Waypoint, Instance); break; case TurnoverPodStorageLocationClassRule.OutputStationDistanceShortestTime: value = Distances.CalculateShortestTimePathPodSafe(wp, o.Waypoint, Instance); break; default: throw new ArgumentException("Unknown storage location rule: " + _storageLocationClassRule); } return(value); })) .ToList(); // Allocate storage locations to classes _classStorageLocations = new List <Waypoint> [_classCount]; List <Waypoint> tempPodsOrdered = _storageLocationsOrdered.ToList(); for (int i = 0; i < _classCount; i++) { if (i < _classCount - 1) { _classStorageLocations[i] = tempPodsOrdered.Take((int)(_classBorders[i] * _storageLocationsOrdered.Count)).ToList(); tempPodsOrdered.RemoveRange(0, _classStorageLocations[i].Count); } else { _classStorageLocations[i] = tempPodsOrdered; } } // Log this reallocation Instance.LogInfo("Allocated storage locations - classes: " + string.Join(";", Enumerable.Range(0, _classCount).Select(c => c + ":" + _classStorageLocations[c].Count))); // Reallocate items and pods for the first time ReallocatePods(); // Reallocate items and pods for the first time ReallocateItems(); }
private void Init() { // --> Init shared component or ensure consistency Instance.SharedControlElements.StoragePartitioner.CreateOrEnsureZones(_config.ZoningConfiguration); // --> Init meta info _cacheTargetCounts = new VolatileIDDictionary <OutputStation, int>( Instance.OutputStations.Select(s => new VolatileKeyValuePair <OutputStation, int>(s, (int)(_config.TargetFillCache * Instance.SharedControlElements.StoragePartitioner.CachePartitions[s].Count))).ToList()); _cachePodCounts = new VolatileIDDictionary <OutputStation, int>( Instance.OutputStations.Select(s => new VolatileKeyValuePair <OutputStation, int>(s, 0)).ToList()); _storageLocationStations = new VolatileIDDictionary <Waypoint, OutputStation>( Instance.Waypoints.Select(w => new VolatileKeyValuePair <Waypoint, OutputStation>(w, null)).ToList()); foreach (var station in Instance.OutputStations) { foreach (var wp in Instance.SharedControlElements.StoragePartitioner.CachePartitions[station]) { _storageLocationStations[wp] = station; } } _cacheableInfoPerStation = new VolatileIDDictionary <OutputStation, bool>( Instance.OutputStations.Select(s => new VolatileKeyValuePair <OutputStation, bool>(s, false)).ToList()); // --> Init move scoring List <Func <double> > scorers = new List <Func <double> >(); // First try to keep the move on the same tier, if desired (this heavily influences the possible moves) if (_config.PreferSameTierMoves) { scorers.Add(() => { return(_currentPod.Tier == _currentBot.Tier && _currentStorageLocation.Tier == _currentBot.Tier ? 1 : 0); }); } // Then decide about move type scorers.Add(() => { if (_currentPod.Waypoint.InfoTagCache == ZoneType.Dropoff && _currentStorageLocation.InfoTagCache == ZoneType.None) { // Drop-off -> Normal return(2); } else if (_currentPod.Waypoint.InfoTagCache == ZoneType.Dropoff && _currentStorageLocation.InfoTagCache == ZoneType.Cache) { // Drop-off -> Cache return(2); } else if (_currentPod.Waypoint.InfoTagCache == ZoneType.Cache && _currentStorageLocation.InfoTagCache == ZoneType.None) { // Cache -> Normal return(1); } else if (_currentPod.Waypoint.InfoTagCache == ZoneType.None && _currentStorageLocation.InfoTagCache == ZoneType.Cache) { // Normal -> Cache return(1); } else { throw new InvalidOperationException("Forbidden move: " + _currentPod.Waypoint.InfoTagCache + " -> " + _currentStorageLocation.InfoTagCache); } }); // Then prefer moves keeping cache at targeted level scorers.Add(() => { // Move types should now be separated, i.e. if there is a drop-off clearing move there is no 'normal' move, respectively, if there is no drop-off clearing move there are only 'normal' moves if (_currentPod.Waypoint.InfoTagCache == ZoneType.Cache && _currentStorageLocation.InfoTagCache == ZoneType.None) { // Cache -> Normal return // Current cache level minus ((double)_cachePodCounts[_storageLocationStations[_currentPod.Waypoint]] / Instance.SharedControlElements.StoragePartitioner.CachePartitions[_storageLocationStations[_currentPod.Waypoint]].Count - // targeted cache level _config.TargetFillCache); } else if (_currentPod.Waypoint.InfoTagCache == ZoneType.None && _currentStorageLocation.InfoTagCache == ZoneType.Cache) { // Normal -> Cache return // Targeted cache level minus (_config.TargetFillCache - // current cache level (double)_cachePodCounts[_storageLocationStations[_currentStorageLocation]] / Instance.SharedControlElements.StoragePartitioner.CachePartitions[_storageLocationStations[_currentStorageLocation]].Count); } else if (_currentPod.Waypoint.InfoTagCache == ZoneType.Dropoff && _currentStorageLocation.InfoTagCache == ZoneType.None) { // Drop-off -> Normal return (_cacheableAtAll ? 0 : 1); } else if (_currentPod.Waypoint.InfoTagCache == ZoneType.Dropoff && _currentStorageLocation.InfoTagCache == ZoneType.Cache) { // Drop-off -> Cache return (_cacheableInfoPerStation[_storageLocationStations[_currentStorageLocation]] ? 1 : 0); } else { throw new InvalidOperationException("Forbidden move: " + _currentPod.Waypoint.InfoTagCache + " -> " + _currentStorageLocation.InfoTagCache); } }); // Then prefer moves that make the greatest contribution in keeping the cache hot (depending on settings) scorers.Add(() => { if (_currentPod.Waypoint.InfoTagCache == ZoneType.Cache) // Cache clearing move { return(1 - Instance.ElementMetaInfoTracker.GetPodCombinedScore(_currentPod, _config.WeightSpeed, _config.WeightUtility)); } else if (_currentStorageLocation.InfoTagCache == ZoneType.Cache) // Cache filling move { return(Instance.ElementMetaInfoTracker.GetPodCombinedScore(_currentPod, _config.WeightSpeed, _config.WeightUtility)); } else { return(0); // Drop-off clearing -> highest priority } }); // Then prefer the shortest moves scorers.Add(() => { switch (_config.PodDisposeRule) { case CacheStorageLocationSelectionRule.Euclid: return(-Distances.CalculateEuclid(_currentPod.Waypoint, _currentStorageLocation, Instance.WrongTierPenaltyDistance)); case CacheStorageLocationSelectionRule.Manhattan: return(-Distances.CalculateManhattan(_currentPod.Waypoint, _currentStorageLocation, Instance.WrongTierPenaltyDistance)); case CacheStorageLocationSelectionRule.ShortestPath: return(-Distances.CalculateShortestPathPodSafe(_currentPod.Waypoint, _currentStorageLocation, Instance)); case CacheStorageLocationSelectionRule.ShortestTime: return(-Distances.CalculateShortestTimePathPodSafe(_currentPod.Waypoint, _currentStorageLocation, Instance)); default: throw new ArgumentException("Unknown pod dispose rule: " + _config.PodDisposeRule); } }); // Then use randomness scorers.Add(() => { return(Instance.Randomizer.NextDouble()); }); // Instantiate best candidate assessment _bestCandidateSelector = new BestCandidateSelector(true, scorers.ToArray()); }
/// <summary> /// Creates all zones or ensures their consistency between different managers using those. /// </summary> /// <param name="config">The configuration to use for creating the zones.</param> internal void CreateOrEnsureZones(CacheConfiguration config) { // Check whether this was already done if (_config != null) { // --> Ensure compatibility if (!_config.Match(config)) { throw new ArgumentException("Incompatible cache configurations: " + _config.ToString() + " vs. " + config.ToString()); } } else { // Init _config = config; List <ZoneType> zoneTypes = new List <ZoneType>() { ZoneType.Cache, ZoneType.Dropoff }; UnzonedStorageLocations = Instance.Waypoints.Where(w => w.PodStorageLocation).ToHashSet(); CachePartitions = new VolatileIDDictionary <OutputStation, HashSet <Waypoint> >(Instance.OutputStations.Select(s => new VolatileKeyValuePair <OutputStation, HashSet <Waypoint> >(s, new HashSet <Waypoint>())).ToList()); int cacheWPCountPerStation = (int)Math.Ceiling(UnzonedStorageLocations.Count * _config.CacheFraction / Instance.OutputStations.Count); DropoffPartitions = new VolatileIDDictionary <OutputStation, HashSet <Waypoint> >(Instance.OutputStations.Select(s => new VolatileKeyValuePair <OutputStation, HashSet <Waypoint> >(s, new HashSet <Waypoint>())).ToList()); int dropoffWPCountPerStation = _config.DropoffCount; // Init selector BestCandidateSelector scorer = new BestCandidateSelector(false, // First adhere to preference between zone types () => { switch (_config.ZonePriority) { case ZonePriority.CacheFirst: return(_currentZoneType == ZoneType.Cache ? 0 : 1); case ZonePriority.DropoffFirst: return(_currentZoneType == ZoneType.Dropoff ? 0 : 1); case ZonePriority.CacheDropoffEqual: return(0); default: throw new ArgumentException("Unknown priority: " + _config.ZonePriority); } }, // Then assess the main distance metric () => { switch (_currentZoneType) { case ZoneType.Cache: return(Distances.CalculateShortestTimePathPodSafe(_currentStorageLocation, _currentStation.Waypoint, Instance)); case ZoneType.Dropoff: return(Distances.CalculateShortestTimePathPodSafe(_currentStation.Waypoint, _currentStorageLocation, Instance)); default: throw new ArgumentException("Unknown zone type for partitioning: " + _currentZoneType.ToString()); } }, // Break ties based on the value for the other zone () => { switch (_currentZoneType) { case ZoneType.Cache: return(-Distances.CalculateShortestTimePathPodSafe(_currentStation.Waypoint, _currentStorageLocation, Instance)); case ZoneType.Dropoff: return(-Distances.CalculateShortestTimePathPodSafe(_currentStorageLocation, _currentStation.Waypoint, Instance)); default: throw new ArgumentException("Unknown zone type for partitioning: " + _currentZoneType.ToString()); } }); // --> Create partitions // Assign storage locations to different zones while ( // Check for any remaining assignments CachePartitions.Values.Any(p => p.Count < cacheWPCountPerStation) || DropoffPartitions.Values.Any(p => p.Count < dropoffWPCountPerStation)) { // Search for next assignment scorer.Recycle(); OutputStation bestStation = null; Waypoint bestStorageLocation = null; ZoneType bestZoneType = ZoneType.None; // Check all unzoned storage locations foreach (var storageLocation in UnzonedStorageLocations) { _currentStorageLocation = storageLocation; // Check all stations foreach (var station in Instance.OutputStations) { _currentStation = station; // Check all types foreach (var zoneType in zoneTypes) { _currentZoneType = zoneType; // Skip invalid assignments if (zoneType == ZoneType.Cache && CachePartitions[station].Count >= cacheWPCountPerStation) { continue; } if (zoneType == ZoneType.Dropoff && DropoffPartitions[station].Count >= dropoffWPCountPerStation) { continue; } // Determine score and update assignment if (scorer.Reassess()) { bestStation = _currentStation; bestStorageLocation = _currentStorageLocation; bestZoneType = _currentZoneType; } } } } // Sanity check if (bestStation == null) { throw new InvalidOperationException("Ran out of available assignments while partitioning the caches - partitions so far: " + "Cache: " + string.Join(",", CachePartitions.Select(p => p.Key.ToString() + "(" + p.Value.Count + ")")) + "Dropoff: " + string.Join(",", DropoffPartitions.Select(p => p.Key.ToString() + "(" + p.Value.Count + ")"))); } // Set assignment switch (bestZoneType) { case ZoneType.Cache: CachePartitions[bestStation].Add(bestStorageLocation); bestStorageLocation.InfoTagCache = ZoneType.Cache; break; case ZoneType.Dropoff: DropoffPartitions[bestStation].Add(bestStorageLocation); bestStorageLocation.InfoTagCache = ZoneType.Dropoff; break; default: throw new InvalidOperationException("Invalid zone determined: " + bestZoneType); } UnzonedStorageLocations.Remove(bestStorageLocation); } } }
/// <summary> /// Initializes this tracker. /// </summary> private void EnsureInit() { if (_podsContainingItems == null) { // --> Init storage location info _storageLocationProminence = new VolatileIDDictionary <Waypoint, double>(_instance.Waypoints .Where(w => w.PodStorageLocation) // Determine pod prominence score .Select(w => new VolatileKeyValuePair <Waypoint, double>(w, _instance.OutputStations.Min(s => { return(Distances.CalculateShortestTimePathPodSafe(w, s.Waypoint, _instance)); }))) .ToList()); StorageLocationsOrdered = _storageLocationProminence // Order storage locations by their prominence .OrderBy(kvp => kvp.Value) // Break ties randomly .ThenBy(kvp => _instance.Randomizer.NextDouble()) // Select the actual locations and build a list .Select(kvp => kvp.Key).ToList(); // Store prominence index _storageLocationIndeces = new VolatileIDDictionary <Waypoint, int>(StorageLocationsOrdered.Select(w => new VolatileKeyValuePair <Waypoint, int>(w, 0)).ToList()); for (int i = 0; i < StorageLocationsOrdered.Count; i++) { _storageLocationIndeces[StorageLocationsOrdered[i]] = i; } // Determine prominence ranks _storageLocationRanks = new VolatileIDDictionary <Waypoint, int>(StorageLocationsOrdered.Select(w => new VolatileKeyValuePair <Waypoint, int>(w, 0)).ToList()); int currentRank = 1; double currentProminenceValue = _storageLocationProminence[StorageLocationsOrdered.First()]; foreach (var storageLocation in StorageLocationsOrdered) { // Update rank, if required if (_storageLocationProminence[storageLocation] > currentProminenceValue) { currentRank++; currentProminenceValue = _storageLocationProminence[storageLocation]; } // Set rank of storage location _storageLocationRanks[storageLocation] = currentRank; } _storageLocationsPerRank = _storageLocationRanks.GroupBy(kvp => kvp.Value).ToDictionary(k => k.Key, v => v.Select(kvp => kvp.Key).OrderBy(kvp => _instance.Randomizer.NextDouble()).ToList()); StorageLocationRankMax = _storageLocationRanks.Values.Max(); // Store prominence ranks for statistics tracking foreach (var w in StorageLocationsOrdered) { w.InfoTagProminence = 1.0 - ((_storageLocationRanks[w] - 1.0) / (StorageLocationRankMax - 1.0)); } // --> Init pod score values _podsContainingItems = new VolatileIDDictionary <ItemDescription, HashSet <Pod> >( _instance.ItemDescriptions.Select(i => new VolatileKeyValuePair <ItemDescription, HashSet <Pod> >(i, _instance.Pods.Where(p => p.IsContained(i)).ToHashSet())).ToList()); _podsAvailableItems = new VolatileIDDictionary <ItemDescription, HashSet <Pod> >( _instance.ItemDescriptions.Select(i => new VolatileKeyValuePair <ItemDescription, HashSet <Pod> >(i, _instance.Pods.Where(p => p.IsAvailable(i)).ToHashSet())).ToList()); // Determine initial pod utility _podUtility = new VolatileIDDictionary <Pod, double>( _instance.Pods.Select(p => new VolatileKeyValuePair <Pod, double>(p, p.ItemDescriptionsContained.Sum(i => Math.Min(p.CountContained(i), _instance.StockInfo.GetCurrentDemand(i))))) .ToList()); VolatileKeyValuePair <Pod, double> bestUtility = _podUtility.ArgMax(pu => pu.Value); _podUtilityMaxPod = bestUtility.Key; PodUtilityMax = bestUtility.Value; // Determine initial pod speed _podSpeed = new VolatileIDDictionary <Pod, double>( _instance.Pods.Select(p => new VolatileKeyValuePair <Pod, double>(p, p.ItemDescriptionsContained.Sum(i => p.CountContained(i) * _instance.FrequencyTracker.GetStaticFrequency(i)))) .ToList()); VolatileKeyValuePair <Pod, double> bestSpeed = _podSpeed.ArgMax(ps => ps.Value); _podSpeedMaxPod = bestSpeed.Key; PodSpeedMax = bestSpeed.Value; } }
/// <summary> /// Chooses the storage location to use for the given pod. /// </summary> /// <param name="pod">The pod to store.</param> /// <returns>The storage location to use for the pod.</returns> private Waypoint ChooseStorageLocation(Pod pod) { // Get the storage class the pod should end up in int desiredStorageClass = _classManager.DetermineStorageClass(pod); // Try to allocate the pod to its storage class - if not possible try neighboring classes int currentClassTriedLow = desiredStorageClass; int currentClassTriedHigh = desiredStorageClass; Waypoint chosenStorageLocation = null; while (true) { // Try the less frequent class first if (currentClassTriedLow < _classManager.ClassCount) { chosenStorageLocation = _classManager.GetClassStorageLocations(currentClassTriedLow) .Where(wp => !Instance.ResourceManager.IsStorageLocationClaimed(wp)) // Only use not occupied ones .OrderBy(wp => { switch (_config.PodDisposeRule) { case TurnoverPodStorageLocationDisposeRule.NearestEuclid: return(Distances.CalculateEuclid(wp, pod, Instance.WrongTierPenaltyDistance)); case TurnoverPodStorageLocationDisposeRule.NearestManhattan: return(Distances.CalculateManhattan(wp, pod, Instance.WrongTierPenaltyDistance)); case TurnoverPodStorageLocationDisposeRule.NearestShortestPath: return(Distances.CalculateShortestPathPodSafe(Instance.WaypointGraph.GetClosestWaypoint(pod.Tier, pod.X, pod.Y), wp, Instance)); case TurnoverPodStorageLocationDisposeRule.NearestShortestTime: return(Distances.CalculateShortestTimePathPodSafe(Instance.WaypointGraph.GetClosestWaypoint(pod.Tier, pod.X, pod.Y), wp, Instance)); case TurnoverPodStorageLocationDisposeRule.OStationNearestEuclid: return(Instance.OutputStations.Min(s => Distances.CalculateEuclid(wp, s, Instance.WrongTierPenaltyDistance))); case TurnoverPodStorageLocationDisposeRule.OStationNearestManhattan: return(Instance.OutputStations.Min(s => Distances.CalculateManhattan(wp, s, Instance.WrongTierPenaltyDistance))); case TurnoverPodStorageLocationDisposeRule.OStationNearestShortestPath: return(Instance.OutputStations.Min(s => Distances.CalculateShortestPathPodSafe(wp, s.Waypoint, Instance))); case TurnoverPodStorageLocationDisposeRule.OStationNearestShortestTime: return(Instance.OutputStations.Min(s => Distances.CalculateShortestTimePathPodSafe(wp, s.Waypoint, Instance))); case TurnoverPodStorageLocationDisposeRule.Random: return(wp.Instance.Randomizer.NextDouble()); default: throw new ArgumentException("Unknown pod dispose rule: " + _config.PodDisposeRule); } }) // Order the remaining ones by the given rule .FirstOrDefault(); // Use the first one } // Check whether we found a suitable pod of this class if (chosenStorageLocation != null) { break; } // Try the higher frequent class next if (currentClassTriedHigh >= 0 && currentClassTriedHigh != currentClassTriedLow) { chosenStorageLocation = _classManager.GetClassStorageLocations(currentClassTriedHigh) .Where(wp => !Instance.ResourceManager.IsStorageLocationClaimed(wp)) // Only use not occupied ones .OrderBy(wp => { switch (_config.PodDisposeRule) { case TurnoverPodStorageLocationDisposeRule.NearestEuclid: return(Distances.CalculateEuclid(wp, pod, Instance.WrongTierPenaltyDistance)); case TurnoverPodStorageLocationDisposeRule.NearestManhattan: return(Distances.CalculateManhattan(wp, pod, Instance.WrongTierPenaltyDistance)); case TurnoverPodStorageLocationDisposeRule.NearestShortestPath: return(Distances.CalculateShortestPathPodSafe(Instance.WaypointGraph.GetClosestWaypoint(pod.Tier, pod.X, pod.Y), wp, Instance)); case TurnoverPodStorageLocationDisposeRule.NearestShortestTime: return(Distances.CalculateShortestTimePathPodSafe(Instance.WaypointGraph.GetClosestWaypoint(pod.Tier, pod.X, pod.Y), wp, Instance)); case TurnoverPodStorageLocationDisposeRule.OStationNearestEuclid: return(Instance.OutputStations.Min(s => Distances.CalculateEuclid(wp, s, Instance.WrongTierPenaltyDistance))); case TurnoverPodStorageLocationDisposeRule.OStationNearestManhattan: return(Instance.OutputStations.Min(s => Distances.CalculateManhattan(wp, s, Instance.WrongTierPenaltyDistance))); case TurnoverPodStorageLocationDisposeRule.OStationNearestShortestPath: return(Instance.OutputStations.Min(s => Distances.CalculateShortestPathPodSafe(wp, s.Waypoint, Instance))); case TurnoverPodStorageLocationDisposeRule.OStationNearestShortestTime: return(Instance.OutputStations.Min(s => Distances.CalculateShortestTimePathPodSafe(wp, s.Waypoint, Instance))); case TurnoverPodStorageLocationDisposeRule.Random: return(wp.Instance.Randomizer.NextDouble()); default: throw new ArgumentException("Unknown pod dispose rule: " + _config.PodDisposeRule); } }) // Order the remaining ones by the given rule .FirstOrDefault(); // Use the first one } // Check whether we found a suitable pod of this class if (chosenStorageLocation != null) { break; } // Update the class indeces to check next currentClassTriedLow++; currentClassTriedHigh--; // Check index correctness if (currentClassTriedHigh < 0 && currentClassTriedLow >= _classManager.ClassCount) { throw new InvalidOperationException("There was no storage location available!"); } } // Return the chosen one return(chosenStorageLocation); }