/// <summary> /// Creates a new instance of this manager. /// </summary> /// <param name="instance">The instance this manager belongs to.</param> public QueueOrderManager(Instance instance) : base(instance) { _config = instance.ControllerConfig.OrderBatchingConfig as QueueOrderBatchingConfiguration; _stationQueues = new VolatileIDDictionary <OutputStation, HashSet <Order> >(instance.OutputStations.Select(s => new VolatileKeyValuePair <OutputStation, HashSet <Order> >(s, new HashSet <Order>())).ToList()); _inboundPodsByDistance = new VolatileIDDictionary <OutputStation, List <Pod> >(instance.OutputStations.Select(s => new VolatileKeyValuePair <OutputStation, List <Pod> >(s, new List <Pod>())).ToList()); _nearestInboundPod = new VolatileIDDictionary <OutputStation, Pod>(instance.OutputStations.Select(s => new VolatileKeyValuePair <OutputStation, Pod>(s, null)).ToList()); }
/// <summary> /// Initializes this controller. /// </summary> private void Initialize() { // Set some values for statistics _statPodMatchingScoreIndex = _config.LateBeforeMatch ? 1 : 0; // --> Setup normal scorers List <Func <double> > normalScorers = new List <Func <double> >(); // Select late orders first if (_config.LateBeforeMatch) { normalScorers.Add(() => { return(_currentOrder.DueTime > Instance.Controller.CurrentTime ? 1 : 0); }); } // Select best by match with inbound pods normalScorers.Add(() => { return(_currentOrder.Positions.Sum(line => Math.Min(_currentStation.InboundPods.Sum(pod => pod.CountAvailable(line.Key)), line.Value))); }); // If we run into ties use the oldest order normalScorers.Add(() => { switch (_config.TieBreaker) { case Shared.OrderSelectionTieBreaker.Random: return(Instance.Randomizer.NextDouble()); case Shared.OrderSelectionTieBreaker.EarliestDueTime: return(-_currentOrder.DueTime); case Shared.OrderSelectionTieBreaker.FCFS: return(-_currentOrder.TimeStamp); default: throw new ArgumentException("Unknown tie breaker: " + _config.FastLaneTieBreaker); } }); // --> Setup fast lane scorers List <Func <double> > fastLaneScorers = new List <Func <double> >(); // If we run into ties use the oldest order fastLaneScorers.Add(() => { switch (_config.FastLaneTieBreaker) { case Shared.FastLaneTieBreaker.Random: return(Instance.Randomizer.NextDouble()); case Shared.FastLaneTieBreaker.EarliestDueTime: return(-_currentOrder.DueTime); case Shared.FastLaneTieBreaker.FCFS: return(-_currentOrder.TimeStamp); default: throw new ArgumentException("Unknown tie breaker: " + _config.FastLaneTieBreaker); } }); // Init selectors _bestCandidateSelectNormal = new BestCandidateSelector(true, normalScorers.ToArray()); _bestCandidateSelectFastLane = new BestCandidateSelector(true, fastLaneScorers.ToArray()); if (_config.FastLane) { _nearestInboundPod = new VolatileIDDictionary <OutputStation, Pod>(Instance.OutputStations.Select(s => new VolatileKeyValuePair <OutputStation, Pod>(s, null)).ToList()); } }
/// <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> /// Initializes this controller. /// </summary> private void Initialize() { // Set some values for statistics _statLinesInCommonScoreIndex = 0; // Setup scorers _bestCandidateSelectNormal = new BestCandidateSelector(true, // First select order with most lines in common () => { return(_currentOrder.Positions.Sum(line => _currentStation.AssignedOrders.Sum(o => o.GetDemandCount(line.Key) > 0 ? 1 : 0))); }, // If we run into ties use the oldest order () => { switch (_config.TieBreaker) { case Shared.OrderSelectionTieBreaker.Random: return(Instance.Randomizer.NextDouble()); case Shared.OrderSelectionTieBreaker.EarliestDueTime: return(-_currentOrder.DueTime); case Shared.OrderSelectionTieBreaker.FCFS: return(-_currentOrder.TimeStamp); default: throw new ArgumentException("Unknown tie breaker: " + _config.FastLaneTieBreaker); } }); _bestCandidateSelectFastLane = new BestCandidateSelector(true, // If we run into ties, break them () => { switch (_config.FastLaneTieBreaker) { case Shared.FastLaneTieBreaker.Random: return(Instance.Randomizer.NextDouble()); case Shared.FastLaneTieBreaker.EarliestDueTime: return(-_currentOrder.DueTime); case Shared.FastLaneTieBreaker.FCFS: return(-_currentOrder.TimeStamp); default: throw new ArgumentException("Unknown tie breaker: " + _config.FastLaneTieBreaker); } }); if (_config.FastLane) { _nearestInboundPod = new VolatileIDDictionary <OutputStation, Pod>(Instance.OutputStations.Select(s => new VolatileKeyValuePair <OutputStation, Pod>(s, null)).ToList()); } }
/// <summary> /// Prepares another assessment run. /// </summary> private void PrepareAssessment() { if (_orderItemDemand == null) { _orderItemDemand = new VolatileIDDictionary <ItemDescription, int>(Instance.ItemDescriptions.Select(i => new VolatileKeyValuePair <ItemDescription, int>(i, 0)).ToList()); } Instance.OutputStations.ForEach(station => _inboundPodsByDistance[station] = station.InboundPods.OrderBy(p => { if (p.Bot != null && p.Bot.CurrentWaypoint != null) { // Use the path distance (this should always be possible) return(Distances.CalculateShortestPathPodSafe(p.Bot.CurrentWaypoint, station.Waypoint, Instance)); } else { // Use manhattan distance as a fallback return(Distances.CalculateManhattan(p, station, Instance.WrongTierPenaltyDistance)); } }).ToList()); Instance.OutputStations.ForEach(station => _nearestInboundPod[station] = _inboundPodsByDistance[station].FirstOrDefault());; }
/// <summary> /// Initializes this manager. /// </summary> public void InitOrEnsureInit(PodUtilityConfiguration config) { // Check whether this has already been done if (_config != null) { // --> Ensure compatibility if (!_config.Match(config)) { throw new ArgumentException("Incompatible pod utility configurations: " + _config.ToString() + " vs. " + config.ToString()); } } else { // Store config _config = config; // Calculate some additional info _storageLocationsToUse = (int)(_instance.ElementMetaInfoTracker.StorageLocationsOrdered.Count * _config.BufferStorageLocations) + _instance.Pods.Count; // Init temp score storage _temporaryPodScores = new VolatileIDDictionary <Pod, double>(_instance.Pods.Select(p => new VolatileKeyValuePair <Pod, double>(p, 0)).ToList()); _temporaryPodScoreIndeces = new VolatileIDDictionary <Pod, int>(_instance.Pods.Select(p => new VolatileKeyValuePair <Pod, int>(p, 0)).ToList()); } }
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> /// Inits demand tracking, if not already done. /// </summary> private void InitDemandTracking() { _backlogDemandStore = new VolatileIDDictionary <ItemDescription, int>(_instance.ItemDescriptions.Select(i => new VolatileKeyValuePair <ItemDescription, int>(i, 0)).ToList()); _queuedDemandStore = new VolatileIDDictionary <ItemDescription, int>(_instance.ItemDescriptions.Select(i => new VolatileKeyValuePair <ItemDescription, int>(i, 0)).ToList()); _assignedDemandStore = new VolatileIDDictionary <ItemDescription, int>(_instance.ItemDescriptions.Select(i => new VolatileKeyValuePair <ItemDescription, int>(i, 0)).ToList()); }
/// <summary> /// Inits this controller. /// </summary> private void InitTimeouts() { _botTimeouts = new VolatileIDDictionary <Bot, double>(Instance.Bots.Select(b => new VolatileKeyValuePair <Bot, double>(b, double.MinValue)).ToList()); }
/// <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 the stock information. /// </summary> private void InitStockInfo() { _currentActualStock = new VolatileIDDictionary <ItemDescription, int>(Instance.ItemDescriptions.Select(i => new VolatileKeyValuePair <ItemDescription, int>(i, 0)).ToList()); _currentAvailableStock = new VolatileIDDictionary <ItemDescription, int>(Instance.ItemDescriptions.Select(i => new VolatileKeyValuePair <ItemDescription, int>(i, 0)).ToList()); _currentOverallDemand = new VolatileIDDictionary <ItemDescription, int>(Instance.ItemDescriptions.Select(i => new VolatileKeyValuePair <ItemDescription, int>(i, 0)).ToList()); }
/// <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> /// Initializes this controller. /// </summary> private void Initialize() { // Setup scorers switch (_config.OrderSelectionRule) { case DefaultOrderSelection.Random: _bestCandidateSelectOrder = new BestCandidateSelector(false, () => { return(Instance.Randomizer.NextDouble()); }); break; case DefaultOrderSelection.FCFS: _bestCandidateSelectOrder = new BestCandidateSelector(false, () => { return(_currentOrder.TimeStamp); }); break; case DefaultOrderSelection.DueTime: _bestCandidateSelectOrder = new BestCandidateSelector(false, () => { return(_currentOrder.DueTime); }); break; case DefaultOrderSelection.FrequencyAge: _bestCandidateSelectOrder = new BestCandidateSelector(false, () => { double orderBirth = 1 - ((_currentOrder.TimeStamp - _oldestOrderTimestamp) / (_newestOrderTimestamp - _oldestOrderTimestamp)); double frequency = _currentOrder.Positions.Average(p => Instance.FrequencyTracker.GetMeasuredFrequency(p.Key)); return(-(orderBirth * frequency)); }); break; default: throw new ArgumentException("Unknown selection rule: " + _config.OrderSelectionRule); } switch (_config.StationSelectionRule) { case DefaultOutputStationSelection.Random: _bestCandidateSelectStation = new BestCandidateSelector(false, () => { return(Instance.Randomizer.NextDouble()); }); break; case DefaultOutputStationSelection.LeastBusy: _bestCandidateSelectStation = new BestCandidateSelector(false, () => { return((_currentStation.CapacityInUse + _currentStation.CapacityReserved) / (double)_currentStation.Capacity); }); break; case DefaultOutputStationSelection.MostBusy: _bestCandidateSelectStation = new BestCandidateSelector(false, () => { return(-((_currentStation.CapacityInUse + _currentStation.CapacityReserved) / (double)_currentStation.Capacity)); }); break; default: throw new ArgumentException("Unknown selection rule: " + _config.OrderSelectionRule); } // Setup fast lane helpers _bestCandidateSelectFastLane = new BestCandidateSelector(true, // If we run into ties use the oldest order () => { switch (_config.FastLaneTieBreaker) { case Shared.FastLaneTieBreaker.Random: return(Instance.Randomizer.NextDouble()); case Shared.FastLaneTieBreaker.EarliestDueTime: return(-_currentOrder.DueTime); case Shared.FastLaneTieBreaker.FCFS: return(-_currentOrder.TimeStamp); default: throw new ArgumentException("Unknown tie breaker: " + _config.FastLaneTieBreaker); } }); if (_config.FastLane) { _nearestInboundPod = new VolatileIDDictionary <OutputStation, Pod>(Instance.OutputStations.Select(s => new VolatileKeyValuePair <OutputStation, Pod>(s, null)).ToList()); } }
/// <summary> /// Decides the next repositioning move to do for the given robot. /// </summary> /// <param name="robot">The robot that is asking to conduct such a move.</param> /// <returns>A repositioning move or <code>null</code> if no such move was available.</returns> protected override RepositioningMove GetRepositioningMove(Bot robot) { // Prepare hot zones if (_chacheStorageLocations == null) { // Ensure valid zones Instance.SharedControlElements.StoragePartitioner.CreateOrEnsureZones(_config.ZoningConfiguration); // Store zones for fast access _chacheStorageLocations = Instance.SharedControlElements.StoragePartitioner.CachePartitions; foreach (var station in _chacheStorageLocations.Keys) { foreach (var storageLocation in _chacheStorageLocations[station]) { _stationsOfStorageLocations[storageLocation] = station; } } _regularStorageLocations = Instance.Waypoints.Except(_chacheStorageLocations.SelectMany(c => c.Value)).ToHashSet(); } // Init if (_bestCandidateSelectorClear == null) { _bestCandidateSelectorClear = new BestCandidateSelector(false, // First try to keep the move on the same tier as the robot () => { return(_currentPod.Tier == robot.Tier && _currentStorageLocation.Tier == robot.Tier ? 0 : 1); }, // Then try to find a pod useless for the station () => { // Check the number of potential picks possible with the pod (given by station orders' demand) int potentialPicks = Instance.ResourceManager.GetExtractRequestsOfStation(_currentStation) .Concat(Instance.ResourceManager.GetQueuedExtractRequestsOfStation(_currentStation)) .GroupBy(r => r.Item).Sum(g => Math.Min(_currentPod.CountContained(g.Key), g.Count())); // Use negative potential picks to mark useless pod return(potentialPicks); }, // Then try to find a pod useless overall () => { // Check the number of potential picks possible with the pod (given by all orders' demand) int potentialPicks = _currentPod.ItemDescriptionsContained.Sum(i => Math.Min(_currentPod.CountContained(i), Instance.ResourceManager.GetDemandAssigned(i) + Instance.ResourceManager.GetDemandQueued(i) + (_config.UselessConsiderBacklog ? Instance.ResourceManager.GetDemandBacklog(i) : 0))); // Use negative potential picks to mark useless pod return(potentialPicks); }, // Then try to use an empty pod () => { return(_currentPod.CapacityInUse); }, // Then try to get a destination location most near to the input-stations (if pod is considered empty) or the shortest move distance (if pod still has sufficient content) () => { return((_currentPod.CapacityInUse / _currentPod.Capacity < _config.PodEmptyThreshold) ? _currentStorageLocation.ShortestPodPathDistanceToNextInputStation : Distances.CalculateShortestPathPodSafe(_currentPod.Waypoint, _currentStorageLocation, Instance)); }, // Then try to make a move with the pod most near to an output-station () => { return(_currentPod.Waypoint.ShortestPodPathDistanceToNextOutputStation); }); } if (_bestCandidateSelectorFill == null) { _bestCandidateSelectorFill = new BestCandidateSelector(false, // First try to keep the move on the same tier as the robot () => { return(_currentPod.Tier == robot.Tier && _currentStorageLocation.Tier == robot.Tier ? 0 : 1); }, // Then try to find a pod useful for the station () => { // Check the number of potential picks possible with the pod (given by station orders' demand) int potentialPicks = Instance.ResourceManager.GetExtractRequestsOfStation(_currentStation) .Concat(Instance.ResourceManager.GetQueuedExtractRequestsOfStation(_currentStation)) .GroupBy(r => r.Item).Sum(g => Math.Min(_currentPod.CountContained(g.Key), g.Count())); // Use negative potential picks to mark useless pod return(-potentialPicks); }, // Then try to find a pod useful overall () => { // Check the number of potential picks possible with the pod (given by all orders' demand) int potentialPicks = _currentPod.ItemDescriptionsContained.Sum(i => Math.Min(_currentPod.CountContained(i), Instance.ResourceManager.GetDemandAssigned(i) + Instance.ResourceManager.GetDemandQueued(i) + (_config.UselessConsiderBacklog ? Instance.ResourceManager.GetDemandBacklog(i) : 0))); // Use negative potential picks to mark useless pod return(-potentialPicks); }, // Then try to use a full pod () => { return(-_currentPod.CapacityInUse); }, // Then try to do a short move () => { return(Distances.CalculateShortestPathPodSafe(_currentPod.Waypoint, _currentStorageLocation, Instance)); }); } // Init Pod bestPod = null; Waypoint bestStorageLocation = null; // Check whether any cache has too many pods if (Instance.OutputStations.Any(s => NeedsClearingRepositioning(s))) { // Clear potential old results _bestCandidateSelectorClear.Recycle(); // Check all stations foreach (var station in Instance.OutputStations.Where(s => NeedsClearingRepositioning(s))) { // Update current candidate to assess _currentStation = station; // Check occupied storage locations of cache of station foreach (var from in _chacheStorageLocations[station].Where(w => w.Pod != null && !Instance.ResourceManager.IsPodClaimed(w.Pod))) { // Check unoccupied storage locations not in a cache foreach (var to in Instance.ResourceManager.UnusedPodStorageLocations.Where(w => _regularStorageLocations.Contains(w))) { // Update current candidate to assess _currentPod = from.Pod; _currentStorageLocation = to; // Check whether the current combination is better if (_bestCandidateSelectorClear.Reassess()) { // Update best candidate bestPod = _currentPod; bestStorageLocation = _currentStorageLocation; } } } } } // Check whether any cache has too few pods if (Instance.OutputStations.Any(s => NeedsFillingRepositioning(s))) { // Clear potential old results _bestCandidateSelectorFill.Recycle(); // Check all stations foreach (var station in Instance.OutputStations.Where(s => NeedsFillingRepositioning(s))) { // Update current candidate to assess _currentStation = station; // Check unused pods foreach (var pod in Instance.ResourceManager.UnusedPods.Where(p => _regularStorageLocations.Contains(p.Waypoint))) { // Check unoccupied storage locations of cache of station foreach (var to in _chacheStorageLocations[station].Where(w => !Instance.ResourceManager.IsStorageLocationClaimed(w))) { // Update current candidate to assess _currentPod = pod; _currentStorageLocation = to; // Check whether the current combination is better if (_bestCandidateSelectorFill.Reassess()) { // Update best candidate bestPod = _currentPod; bestStorageLocation = _currentStorageLocation; } } } } } // Check whether a move was obtained if (bestPod != null) { // Return the move return(new RepositioningMove() { Pod = bestPod, StorageLocation = bestStorageLocation }); } else { // No move available - block calls for a while GlobalTimeout = Instance.Controller.CurrentTime + _config.GlobalTimeout; return(null); } }