コード例 #1
0
        /// <summary>
        /// Estimates the time it takes to travel from one waypoint to another.
        /// </summary>
        /// <param name="from">The start node.</param>
        /// <param name="to">The destination node.</param>
        /// <returns>The estimated time it takes to travel between the nodes.</returns>
        private double EstimateTravelTime(TimeWaypoint from, Waypoint to)
        {
            // Init, if not done yet
            if (_timeGraph == null)
            {
                _timeGraph = new TimeGraph(_instance);
            }
            // --> Calculate time needed for driving the given distance
            double travelTime = Distances.CalculateEuclid(from.OriginalWaypoint, to, _instance.WrongTierPenaltyDistance) / _timeGraph.Speed;

            // Check whether we reached the goal already, thus, eliminating the need for turning
            if (from.OriginalWaypoint == to)
            {
                // No turning necessary - only consider time for driving
                return(travelTime);
            }
            else
            {
                // In addition to the drive time also consider the effort for turning
                return
                    // --> Calculate time needed for turning towards new orientation
                    (Math.Abs(Circle.GetOrientationDifference(from.Orientation,
                                                              Circle.GetOrientation(from.OriginalWaypoint.X, from.OriginalWaypoint.Y, to.X, to.Y))) / TimeGraph.PI2 * _timeGraph.TurnSpeed +
                     // --> Add time for driving
                     travelTime);
            }
        }
コード例 #2
0
        /// <summary>
        /// This is called to decide about potentially pending orders.
        /// This method is being timed for statistical purposes and is also ONLY called when <code>SituationInvestigated</code> is <code>false</code>.
        /// Hence, set the field accordingly to react on events not tracked by this outer skeleton.
        /// </summary>
        protected override void DecideAboutPendingOrders()
        {
            foreach (var order in _pendingOrders.Where(o => o.Positions.All(p => Instance.StockInfo.GetActualStock(p.Key) >= p.Value)).ToArray())
            {
                // Check all pods for the maximum number of picks that can be done with them
                List <Pod> bestPods     = new List <Pod>();
                int        bestPodPicks = -1;
                foreach (var pod in Instance.Pods.Where(p => !p.InUse))
                {
                    // Calculate picks that can potentially be done with the pod
                    int picks = order.Positions.Sum(pos => Math.Min(pod.CountAvailable(pos.Key), pos.Value));
                    // Check whether we found even more possible picks with this pod
                    if (bestPodPicks < picks)
                    {
                        bestPods.Clear();
                        bestPods.Add(pod);
                        bestPodPicks = picks;
                    }
                    else
                    {
                        // Check whether the current pod belongs into the winner group
                        if (bestPodPicks == picks)
                        {
                            bestPods.Add(pod);
                        }
                    }
                }
                // Choose station nearest to one of the best pods
                OutputStation chosenStation    = null;
                double        shortestDistance = double.PositiveInfinity;
                foreach (var station in Instance.OutputStations.Where(s => s.Active && s.FitsForReservation(order)))
                {
                    foreach (var pod in bestPods)
                    {
                        double distance;
                        switch (_config.DistanceRule)
                        {
                        case NearBestPodOrderBatchingDistanceRule.Euclid: distance = Distances.CalculateEuclid(pod, station, Instance.WrongTierPenaltyDistance); break;

                        case NearBestPodOrderBatchingDistanceRule.Manhattan: distance = Distances.CalculateManhattan(pod, station, Instance.WrongTierPenaltyDistance); break;

                        case NearBestPodOrderBatchingDistanceRule.ShortestPath: distance = Distances.CalculateShortestPathPodSafe(pod.Waypoint, station.Waypoint, Instance); break;

                        default: throw new ArgumentException("Unknown distance rule: " + _config.DistanceRule);
                        }
                        if (distance < shortestDistance)
                        {
                            shortestDistance = distance;
                            chosenStation    = station;
                        }
                    }
                }
                // If we found a station, assign the bundle to it
                if (chosenStation != null)
                {
                    AllocateOrder(order, chosenStation);
                }
            }
        }
コード例 #3
0
 /// <summary>
 /// Calculates the time to move from one time-waypoint to another.
 /// </summary>
 /// <param name="from">The start node.</param>
 /// <param name="to">The destination node.</param>
 /// <returns>The time for traveling from one node to the other.</returns>
 public double GetTimeNeededToTravel(TimeWaypoint from, TimeWaypoint to)
 {
     return
         // --> Calculate time needed for turning towards new orientation
         (Math.Abs(Circle.GetOrientationDifference(from.Orientation, to.Orientation)) / PI2 * TurnSpeed +
          // --> Calculate time needed for driving the given distance
          Distances.CalculateEuclid(from.OriginalWaypoint, to.OriginalWaypoint, Instance.WrongTierPenaltyDistance) / Speed);
 }
コード例 #4
0
        /// <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());
        }
コード例 #5
0
 /// <summary>
 /// Estimates the time for traveling using the manhattan metric.
 /// </summary>
 /// <param name="from">The from part of the trip.</param>
 /// <param name="to">The to part of the trip.</param>
 /// <param name="instance">The instance.</param>
 /// <returns>The time needed to for conducting the trip according to the manhattan metric.</returns>
 public double EstimateShortestPathEuclid(Circle from, Circle to, Instance instance)
 {
     // Init, if not done yet
     if (_timeGraph == null)
     {
         _timeGraph = new TimeGraph(_instance);
     }
     return
         // Use full manhattan distance to estimate travel time
         (Distances.CalculateEuclid(from, to, instance.WrongTierPenaltyDistance) / _timeGraph.Speed);
 }
コード例 #6
0
        /// <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);
        }
コード例 #7
0
 /// <summary>
 /// This is called to decide about potentially pending bundles.
 /// This method is being timed for statistical purposes and is also ONLY called when <code>SituationInvestigated</code> is <code>false</code>.
 /// Hence, set the field accordingly to react on events not tracked by this outer skeleton.
 /// </summary>
 protected override void DecideAboutPendingBundles()
 {
     foreach (var bundle in _bundleToStation.Keys.ToArray())
     {
         // Find a pod
         Pod chosenPod = Instance.Pods
                         .Where(b => b.FitsForReservation(bundle))
                         .OrderBy(b =>
                                  b.InUse ?
                                  Instance.WrongTierPenaltyDistance + Distances.CalculateEuclid(_bundleToStation[bundle], b, Instance.WrongTierPenaltyDistance) :
                                  Distances.CalculateEuclid(_bundleToStation[bundle], b, Instance.WrongTierPenaltyDistance))
                         .FirstOrDefault();
         // If we found a pod, assign the bundle to it
         if (chosenPod != null)
         {
             AddToReadyList(bundle, chosenPod);
             _bundleToStation.Remove(bundle);
         }
     }
 }
コード例 #8
0
        /// <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);
        }
コード例 #9
0
        /// <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)
        {
            // Ensure init
            if (_bestCandidateSelector == null)
            {
                Init();
            }
            // Get output station this pod is coming from (it has to be the nearest one)
            _currentStation = Instance.OutputStations.ArgMin(s => Distances.CalculateEuclid(s, pod, Instance.WrongTierPenaltyDistance));
            // Get current pod location (pod has to be at station's position right now)
            _currentPodLocation = _currentStation.Waypoint;
            // Determine whether the pod should be put in the cache
            double cacheUtilityValue =
                Instance.SharedControlElements.StoragePartitioner.GetCacheValue(pod, _currentStation, _config.WeightSpeed, _config.WeightUtility);
            double cacheFill =
                (double)Instance.SharedControlElements.StoragePartitioner.CachePartitions[_currentStation].Count(c => c.Pod != null) /
                Instance.SharedControlElements.StoragePartitioner.CachePartitions[_currentStation].Count;

            _currentCacheable = ((1 - cacheFill) * _config.WeightCacheFill + cacheUtilityValue * _config.WeightCacheUtility) / 2.0 > _config.PodCacheableThreshold;
            // Get best storage location
            _bestCandidateSelector.Recycle();
            Waypoint bestStorageLocation = null;

            foreach (var storageLocation in Instance.ResourceManager.UnusedPodStorageLocations)
            {
                // Update current candidate
                _currentStorageLocation = storageLocation;
                // Assess new candidate
                if (_bestCandidateSelector.Reassess())
                {
                    bestStorageLocation = _currentStorageLocation;
                }
            }
            // Return it
            return(bestStorageLocation);
        }
コード例 #10
0
        /// <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();
        }
コード例 #11
0
        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());
        }
コード例 #12
0
        /// <summary>
        /// Updates the allocation of robots to station, if stations became active or inactive in the meantime.
        /// </summary>
        public void UpdateRobotAllocation(bool firstAllocation)
        {
            // --> Allocate bots
            int pickBots          = (int)(Instance.Bots.Count * _config.PickBotRatio);
            int replenishmentBots = Instance.Bots.Count - pickBots;
            // Check active stations
            List <InputStation>  activeInputStations  = Instance.InputStations.Where(s => firstAllocation || s.Active).ToList();
            List <OutputStation> activeOutputStations = Instance.OutputStations.Where(s => firstAllocation || s.Active).ToList();
            // Check whether something changed since last time
            IEnumerable <Circle> currentlyActiveStations = activeInputStations.Cast <Circle>().Concat(activeOutputStations);

            // If it's the first allocation, remember the 'last' active stations for next time
            if (_lastActiveStations == null)
            {
                _lastActiveStations = new HashSet <Circle>(activeInputStations.Cast <Circle>().Concat(activeOutputStations));
            }
            // If nothing changed, don't do anything
            else if (currentlyActiveStations.All(s => _lastActiveStations.Contains(s)) && currentlyActiveStations.Count() == _lastActiveStations.Count)
            {
                return;
            }
            // If there is no active station at all, ignore the update
            else if (!activeInputStations.Any(s => s.Active) && !activeOutputStations.Any(s => s.Active))
            {
                return;
            }
            // Update the new 'last' active stations for next time
            else
            {
                _lastActiveStations = new HashSet <Circle>(activeInputStations.Cast <Circle>().Concat(activeOutputStations));
            }
            // If there is a 'fractional' bot, add it to the lower number
            if (replenishmentBots + pickBots < Instance.Bots.Count)
            {
                if (replenishmentBots < pickBots)
                {
                    replenishmentBots += Instance.Bots.Count - replenishmentBots - pickBots;
                }
                else
                {
                    pickBots += Instance.Bots.Count - replenishmentBots - pickBots;
                }
            }
            // Order active stations by their preference for assigning bots
            activeInputStations =
                // Order active input stations by their distance to the middle of their tier (prefer stations more far away, because others are more likely to share bots)
                activeInputStations.OrderByDescending(s => s.GetDistance(s.Tier.Length / 2.0, s.Tier.Width / 2.0)).ToList();
            activeOutputStations =
                // Order active output stations by their distance to the middle of their tier (prefer stations more far away, because others are more likely to share bots)
                activeOutputStations.OrderByDescending(s => s.GetDistance(s.Tier.Length / 2.0, s.Tier.Width / 2.0)).ToList();
            // Obtain goal numbers per station
            Dictionary <Circle, int> stationBotGoals = _stations.ToDictionary(k => k, v => 0);

            for (int i = 0; replenishmentBots > 0; i = (i + 1) % activeInputStations.Count)
            {
                if (stationBotGoals.ContainsKey(activeInputStations[i]))
                {
                    stationBotGoals[activeInputStations[i]]++;
                }
                else
                {
                    stationBotGoals[activeInputStations[i]] = 1;
                }
                replenishmentBots--;
            }
            for (int i = 0; pickBots > 0; i = (i + 1) % activeOutputStations.Count)
            {
                if (stationBotGoals.ContainsKey(activeOutputStations[i]))
                {
                    stationBotGoals[activeOutputStations[i]]++;
                }
                else
                {
                    stationBotGoals[activeOutputStations[i]] = 1;
                }
                pickBots--;
            }
            // Keep on reassigning until all stations meet their goal
            List <Tuple <Bot, Circle, Circle> > reassignments      = null;
            Dictionary <Circle, int>            previousAssignment = null;

            while (_stations.Any(s => stationBotGoals[s] != _stationBots[s].Count))
            {
                // Remember current assignment counts
                if (previousAssignment == null)
                {
                    previousAssignment = _stationBots.ToDictionary(k => k.Key, v => v.Value.Count);
                }
                // Get next switching partners
                Bot bestBot = null; Circle receivingStation = null; double bestValue = double.PositiveInfinity; bool unassignedBot = true;
                // Check all station not yet at their goal
                foreach (var station in _stations.Where(s => _stationBots[s].Count < stationBotGoals[s]))
                {
                    // Check all bots that are assigned to a station with an overflow of bots
                    foreach (var bot in _unassignedBots.Concat(_stationBots.Where(s => _stationBots[s.Key].Count > stationBotGoals[s.Key]).SelectMany(s => s.Value)))
                    {
                        // See whether the bot is nearer to the station than the previous ones
                        double distance = (bot.TargetWaypoint != null ? bot.TargetWaypoint.GetDistance(station) : Distances.CalculateEuclid(bot, station, Instance.WrongTierPenaltyDistance));
                        if (distance < bestValue)
                        {
                            // Set new best pair
                            bestValue        = distance;
                            bestBot          = bot;
                            receivingStation = station;
                            unassignedBot    = _unassignedBots.Contains(bot);
                        }
                    }
                }
                // Store the switch
                if (reassignments != null)
                {
                    reassignments.Add(new Tuple <Bot, Circle, Circle>(bestBot, unassignedBot ? null : _botStations[bestBot], receivingStation));
                }
                else
                {
                    reassignments = new List <Tuple <Bot, Circle, Circle> >()
                    {
                        new Tuple <Bot, Circle, Circle>(bestBot, unassignedBot ? null : _botStations[bestBot], receivingStation)
                    }
                };
                // Perform the switch
                if (unassignedBot)
                {
                    _unassignedBots.Remove(bestBot);
                }
                else
                {
                    _stationBots[_botStations[bestBot]].Remove(bestBot);
                }
                _botStations[bestBot] = receivingStation;
                _stationBots[receivingStation].Add(bestBot);
            }
            // Log reassignments
            if (reassignments != null)
            {
                Instance.LogVerbose("Reassigning " + reassignments.Count + " bots:");
                Instance.LogVerbose("ID:   " + string.Join(",", Instance.InputStations.Select(s => "I" + s.ID).Concat(Instance.OutputStations.Select(s => "O" + s.ID)).Select(s => s.PadLeft(3))));
                Instance.LogVerbose("Work: " + string.Join(",",
                                                           Instance.InputStations.Select(s => (s.ItemBundles.Any() ? s.ItemBundles.Count().ToString() : " ")).Concat(
                                                               Instance.OutputStations.Select(s => (s.AssignedOrders.Any() ? s.AssignedOrders.Count().ToString() : " "))).Select(s => s.PadLeft(3))));
                Instance.LogVerbose("Bots: " + string.Join(",",
                                                           Instance.InputStations.Select(s => _stationBots[s].Count.ToString()).Concat(
                                                               Instance.OutputStations.Select(s => _stationBots[s].Count.ToString())).Select(s => s.PadLeft(3))));
                Instance.LogVerbose("Was:  " + string.Join(",",
                                                           Instance.InputStations.Select(s => previousAssignment[s].ToString()).Concat(
                                                               Instance.OutputStations.Select(s => previousAssignment[s].ToString())).Select(s => s.PadLeft(3))));
            }
        }
コード例 #13
0
 /// <summary>
 /// Creates a new instance of this controller.
 /// </summary>
 /// <param name="instance">The instance this controller belongs to.</param>
 public BalancedBotManager(Instance instance) : base(instance)
 {
     // Save config and instance
     Instance = instance;
     _config  = instance.ControllerConfig.TaskAllocationConfig as BalancedTaskAllocationConfiguration;
     // Build list of bots used for station work
     _bots = instance.Bots
             // Take all bots that shall be used for the dynamic station assignment
             .Take((int)Math.Ceiling((_config.WeightInputStations + _config.WeightOutputStations) / (_config.WeightInputStations + _config.WeightOutputStations + _config.WeightRepositioning) * instance.Bots.Count))
             .ToList();
     // Use all remaining bots for repositioning
     _repositioningBots = instance.Bots.Except(_bots).ToHashSet();
     // Keep expended search radius within limits, if same tier is preferred
     if (_config.ExtendedSearchRadius > instance.WrongTierPenaltyDistance && _config.PreferSameTier)
     {
         _config.ExtendedSearchRadius = instance.WrongTierPenaltyDistance;
     }
     _stations           = instance.InputStations.Cast <Circle>().Concat(instance.OutputStations).ToList();
     _singleStationLists = _stations.ToDictionary(k => k, v => new List <Circle>()
     {
         v
     });
     _allStationsList = _stations.ToDictionary(k => k, v => _stations.OrderBy(s => Distances.CalculateEuclid(v, s, instance.WrongTierPenaltyDistance)).ThenByDescending(s => s.GetDistance(s.Tier.Length / 2.0, s.Tier.Width / 2.0)).ToList());
     _stationBots     = _stations.ToDictionary(k => k, v => new HashSet <Bot>());
     foreach (var bot in _bots)
     {
         _unassignedBots.Add(bot);
     }
     // Perform first allocation
     ReallocateBots(true);
     // Keep track of the stations the bots are currently working for
     _workerStations     = instance.Bots.ToDictionary(k => k, v => _botStations.ContainsKey(v) ? _botStations[v] : null);
     _stationWorkerCount = _stations.ToDictionary(k => k, v => _workerStations.Count(s => s.Value == v));
     // Do not immediately reallocate again
     _nextReallocation = instance.Controller != null ? instance.Controller.CurrentTime + _config.BotReallocationTimeout : _config.BotReallocationTimeout;
 }
コード例 #14
0
        /// <summary>
        /// Generates a complete configuration for the simple item generator.
        /// </summary>
        /// <param name="preConfig">The pre-configuration defining characteristics of the actual configuration.</param>
        /// <returns>The complete configuration.</returns>
        public static SimpleItemGeneratorConfiguration GenerateSimpleItemConfiguration(SimpleItemGeneratorPreConfiguration preConfig)
        {
            // Init
            SimpleItemGeneratorConfiguration config = new SimpleItemGeneratorConfiguration()
            {
                DefaultWeight     = preConfig.DefaultWeight,
                DefaultCoWeight   = preConfig.DefaultCoWeight,
                ProbToUseCoWeight = preConfig.ProbToUseCoWeight
            };
            RandomizerSimple             randomizer       = new RandomizerSimple(0);
            List <SimpleItemDescription> itemDescriptions = new List <SimpleItemDescription>();
            List <Tuple <SimpleItemDescription, double> > itemDescriptionWeights = new List <Tuple <SimpleItemDescription, double> >();
            List <Tuple <SimpleItemDescription, SimpleItemDescription, double> > itemDescriptionCoWeights = new List <Tuple <SimpleItemDescription, SimpleItemDescription, double> >();

            // Add comment
            config.Description = string.Join(",", typeof(SimpleItemGeneratorPreConfiguration).GetFields().Select(f =>
            {
                string fieldValue;
                if (f.GetValue(preConfig) is double)
                {
                    fieldValue = ((double)f.GetValue(preConfig)).ToString(IOConstants.FORMATTER);
                }
                else
                {
                    fieldValue = f.GetValue(preConfig).ToString();
                }
                return(f.Name + "=" + fieldValue);
            }));

            // Generate a set of item-descriptions
            for (int i = 0; i < preConfig.ItemDescriptionCount; i++)
            {
                // Generate next item
                SimpleItemDescription description = new SimpleItemDescription(null)
                {
                    ID = i
                };
                // Randomly weight the item
                double itemDescriptionWeight = 0;
                switch (preConfig.WeightDistributionType)
                {
                case ItemDescriptionWeightDistributionType.Normal:
                    itemDescriptionWeight = randomizer.NextNormalDouble(preConfig.ItemWeightMu, preConfig.ItemWeightSigma, preConfig.ItemWeightLB, preConfig.ItemWeightUB);
                    break;

                case ItemDescriptionWeightDistributionType.Uniform:
                    itemDescriptionWeight = randomizer.NextDouble(preConfig.ItemWeightLB, preConfig.ItemWeightUB);
                    break;

                default: throw new ArgumentException("Unknown distribution: " + preConfig.WeightDistributionType);
                }
                description.Weight = itemDescriptionWeight;
                // Randomly determine bundle size of the item
                if (preConfig.SupplyBundleSize)
                {
                    int itemDescriptionBundleSize = 0;
                    switch (preConfig.BundleSizeDistributionType)
                    {
                    case ItemDescriptionBundleSizeDistributionType.Normal:
                        itemDescriptionBundleSize = randomizer.NextNormalInt(preConfig.BundleSizeMu, preConfig.BundleSizeSigma, preConfig.BundleSizeLB, preConfig.BundleSizeUB);
                        break;

                    case ItemDescriptionBundleSizeDistributionType.Uniform:
                        itemDescriptionBundleSize = randomizer.NextInt(preConfig.BundleSizeLB, preConfig.BundleSizeUB + 1);
                        break;

                    default: throw new ArgumentException("Unknown distribution: " + preConfig.BundleSizeDistributionType);
                    }
                    description.BundleSize = itemDescriptionBundleSize;
                }
                // Add a random hue value to distinguish the item from others
                description.Hue = randomizer.NextDouble(360);
                // Add it
                itemDescriptions.Add(description);
                // Set a weight for the probability of the item
                double weight = 0;
                switch (preConfig.ProbWeightDistributionType)
                {
                case ItemDescriptionProbabilityWeightDistributionType.Constant:
                    weight = preConfig.ProbabilityWeightConstant;
                    break;

                case ItemDescriptionProbabilityWeightDistributionType.Uniform:
                    weight = randomizer.NextDouble(preConfig.ProbabilityWeightUniformMin, preConfig.ProbabilityWeightUniformMax);
                    break;

                case ItemDescriptionProbabilityWeightDistributionType.Normal:
                    weight = randomizer.NextNormalDouble(preConfig.ProbabilityWeightNormalMu, preConfig.ProbabilityWeightNormalSigma, preConfig.ProbabilityWeightLB, preConfig.ProbabilityWeightUB);
                    break;

                case ItemDescriptionProbabilityWeightDistributionType.Exponential:
                    weight = randomizer.NextExponentialDouble(preConfig.ProbabilityWeightExpLambda, preConfig.ProbabilityWeightLB, preConfig.ProbabilityWeightUB);
                    break;

                case ItemDescriptionProbabilityWeightDistributionType.Gamma:
                    weight = randomizer.NextGammaDouble(preConfig.ProbabilityWeightGammaK, preConfig.ProbabilityWeightGammaTheta, preConfig.ProbabilityWeightLB, preConfig.ProbabilityWeightUB);
                    break;

                default: throw new ArgumentException("Unknown distribution: " + preConfig.ProbWeightDistributionType);
                }
                itemDescriptionWeights.Add(new Tuple <SimpleItemDescription, double>(description, weight));
            }

            // Equally distribute items over two-dimensional space
            Dictionary <SimpleItemDescription, Tuple <double, double> > itemDescriptionPosition = new Dictionary <SimpleItemDescription, Tuple <double, double> >();

            foreach (var description in itemDescriptions)
            {
                itemDescriptionPosition[description] = new Tuple <double, double>(randomizer.NextDouble(), randomizer.NextDouble());
            }

            // Plot the distribution for reference
            GnuPlotter.Plot2DPoints(
                "itemdistribution",
                new List <Tuple <string, IEnumerable <Tuple <double, double> > > >()
            {
                new Tuple <string, IEnumerable <Tuple <double, double> > >("Item locations in 2D", itemDescriptionPosition.Values)
            },
                "item distribution for co-probability emulation");

            // Set conditional weights
            double maxDistance = Distances.CalculateEuclid(0, 0, 1, 1);

            foreach (var description in itemDescriptions.OrderBy(d => randomizer.NextDouble()).Take((int)(itemDescriptions.Count * preConfig.GivenCoWeights)))
            {
                foreach (var otherDescription in itemDescriptions.OrderBy(d => randomizer.NextDouble()).Take((int)(itemDescriptions.Count * preConfig.GivenCoWeights)))
                {
                    itemDescriptionCoWeights.Add(new Tuple <SimpleItemDescription, SimpleItemDescription, double>(
                                                     description,
                                                     otherDescription,
                                                     maxDistance - Distances.CalculateEuclid(
                                                         itemDescriptionPosition[description].Item1, itemDescriptionPosition[description].Item2,
                                                         itemDescriptionPosition[otherDescription].Item1, itemDescriptionPosition[otherDescription].Item2)));
                }
            }

            // Submit all
            config.ItemDescriptions = itemDescriptions.Select(d => new Skvp <int, double>()
            {
                Key = d.ID, Value = d.Hue
            }).ToList();
            config.ItemDescriptionWeights = itemDescriptions.Select(d => new Skvp <int, double>()
            {
                Key = d.ID, Value = d.Weight
            }).ToList();
            if (preConfig.SupplyBundleSize)
            {
                config.ItemDescriptionBundleSizes = itemDescriptions.Select(d => new Skvp <int, int>()
                {
                    Key = d.ID, Value = d.BundleSize
                }).ToList();
            }
            config.ItemWeights = itemDescriptionWeights.Select(d => new Skvp <int, double>()
            {
                Key = d.Item1.ID, Value = d.Item2
            }).ToList();
            config.ItemCoWeights = itemDescriptionCoWeights.Select(d => new Skkvt <int, int, double>()
            {
                Key1 = d.Item1.ID, Key2 = d.Item2.ID, Value = d.Item3
            }).ToList();

            // Name it
            config.Name = config.GetMetaInfoBasedName();

            // Return it
            return(config);
        }
コード例 #15
0
        /// <summary>
        /// This is called to decide about potentially pending bundles.
        /// This method is being timed for statistical purposes and is also ONLY called when <code>SituationInvestigated</code> is <code>false</code>.
        /// Hence, set the field accordingly to react on events not tracked by this outer skeleton.
        /// </summary>
        protected override void DecideAboutPendingBundles()
        {
            if (_config.BreakBatches)
            {
                // Go through list of not assigned bundles
                for (int i = 0; i < _itemBundles.Count; i++)
                {
                    // Check which pod was used to store the bundle
                    ItemBundle bundle       = _itemBundles[i];
                    Pod        podForBundle = _bundleToPod[bundle];
                    // See whether we already have a station in memory for this pod
                    InputStation chosenStation;
                    if (_lastChosenStations.ContainsKey(podForBundle))
                    {
                        // See whether we can assign the new bundle to that station
                        if (_lastChosenStations[podForBundle].FitsForReservation(bundle))
                        {
                            chosenStation = _lastChosenStations[podForBundle];
                        }
                        else
                        {
                            // The bundle won't fit the station - try a station close by
                            chosenStation = Instance.InputStations
                                            .Where(s => s.Active && s.FitsForReservation(bundle))                                                             // There has to be sufficient capacity left at the station and the station needs to be active
                                            .OrderBy(s => Distances.CalculateEuclid(_lastChosenStations[podForBundle], s, Instance.WrongTierPenaltyDistance)) // Start with the nearest station
                                            .FirstOrDefault();                                                                                                // Return the first one or null if none available
                        }
                    }
                    else
                    {
                        // We don't know this pod - select a new station
                        switch (_config.FirstStationRule)
                        {
                        case SamePodFirstStationRule.Emptiest:
                            chosenStation = Instance.InputStations
                                            .Where(s => s.Active && s.FitsForReservation(bundle))              // There has to be sufficient capacity left at the station and the station needs to be active
                                            .OrderBy(s => (s.CapacityInUse + s.CapacityReserved) / s.Capacity) // Pick the emptiest one
                                            .FirstOrDefault();                                                 // Return the first one or null if none available
                            break;

                        case SamePodFirstStationRule.Fullest:
                            chosenStation = Instance.InputStations
                                            .Where(s => s.Active && s.FitsForReservation(bundle))                        // There has to be sufficient capacity left at the station and the station needs to be active
                                            .OrderByDescending(s => (s.CapacityInUse + s.CapacityReserved) / s.Capacity) // Pick the fullest one
                                            .FirstOrDefault();                                                           // Return the first one or null if none available
                            break;

                        case SamePodFirstStationRule.LeastBusy:
                            chosenStation = Instance.InputStations
                                            .Where(s => s.Active && s.FitsForReservation(bundle)) // There has to be sufficient capacity left at the station and the station needs to be active
                                            .OrderBy(s => s.ItemBundles.Count())                  // Pick the one with the fewest bundles
                                            .FirstOrDefault();                                    // Return the first one or null if none available
                            break;

                        case SamePodFirstStationRule.MostBusy:
                            chosenStation = Instance.InputStations
                                            .Where(s => s.Active && s.FitsForReservation(bundle)) // There has to be sufficient capacity left at the station and the station needs to be active
                                            .OrderByDescending(s => s.ItemBundles.Count())        // Pick the one with the most bundles
                                            .FirstOrDefault();                                    // Return the first one or null if none available
                            break;

                        case SamePodFirstStationRule.Random:
                            chosenStation = Instance.InputStations
                                            .Where(s => s.Active && s.FitsForReservation(bundle)) // There has to be sufficient capacity left at the station and the station needs to be active
                                            .OrderBy(s => Instance.Randomizer.NextDouble())       // Pick a random one
                                            .FirstOrDefault();                                    // Return the first one or null if none available
                            break;

                        case SamePodFirstStationRule.DistanceEuclid:
                            chosenStation = Instance.InputStations
                                            .Where(s => s.Active && s.FitsForReservation(bundle))                                        // There has to be sufficient capacity left at the station and the station needs to be active
                                            .OrderBy(s => Distances.CalculateEuclid(podForBundle, s, Instance.WrongTierPenaltyDistance)) // Pick the nearest one
                                            .FirstOrDefault();                                                                           // Return the first one or null if none available
                            break;

                        default: throw new ArgumentException("Unknown first station rule: " + _config.FirstStationRule);
                        }
                    }
                    // If we found a station, assign the bundle to it
                    if (chosenStation != null)
                    {
                        AddToReadyList(bundle, chosenStation);
                        _bundleToPod.Remove(bundle);
                        _itemBundles.RemoveAt(0);
                        i--;
                        _lastChosenStations[podForBundle] = chosenStation;
                    }
                }
            }
            else
            {
                // Assign batches in the order they arrive in
                List <Pod> removedBatches = null;
                foreach (var batch in _podBundles.OrderBy(p => _waitingTime[p.Key]))
                {
                    double batchSize = batch.Value.Sum(b => b.BundleWeight);
                    // Choose a suitable station
                    InputStation chosenStation = Instance.InputStations
                                                 // Only active stations where the complete bundle fits
                                                 .Where(s => s.Active && batchSize <= s.RemainingCapacity)
                                                 // Only stations that are located on the same tier as the pod (if desired)
                                                 .Where(s => s.Tier == batch.Key.Tier)
                                                 // Find the best station according to the chosen rule
                                                 .ArgMin(s =>
                    {
                        switch (_config.FirstStationRule)
                        {
                        case SamePodFirstStationRule.Emptiest: return((s.CapacityInUse + s.CapacityReserved) / s.Capacity);

                        case SamePodFirstStationRule.Fullest: return(1 - ((s.CapacityInUse + s.CapacityReserved) / s.Capacity));

                        case SamePodFirstStationRule.LeastBusy: return(s.ItemBundles.Count());

                        case SamePodFirstStationRule.MostBusy: return(-s.ItemBundles.Count());

                        case SamePodFirstStationRule.Random: return(Instance.Randomizer.NextDouble());

                        case SamePodFirstStationRule.DistanceEuclid: return(Distances.CalculateEuclid(batch.Key, s, Instance.WrongTierPenaltyDistance));

                        default: throw new ArgumentException("Unknown rule: " + _config.FirstStationRule);
                        }
                    });
                    // Check whether there was a suitable station at all
                    if (chosenStation != null)
                    {
                        // Submit the decision
                        foreach (var bundle in batch.Value)
                        {
                            AddToReadyList(bundle, chosenStation);
                        }
                        // Clean up
                        _lastChosenStations[batch.Key] = chosenStation;
                        if (removedBatches == null)
                        {
                            removedBatches = new List <Pod>();
                        }
                        removedBatches.Add(batch.Key);
                    }
                    else
                    {
                        // If FCFS applies, we cannot assign further batches until this one gets assigned
                        if (_config.FCFS)
                        {
                            break;
                        }
                    }
                }
                // Actually remove the batches
                if (removedBatches != null)
                {
                    foreach (var batch in removedBatches)
                    {
                        _podBundles.Remove(batch);
                    }
                }
            }
        }
コード例 #16
0
        /// <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;
        }
コード例 #17
0
        /// <summary>
        /// Reallocates the bots between the stations.
        /// </summary>
        /// <param name="overrideActivity">Indicates whether the stations activity  will be ignored.</param>
        private void ReallocateBots(bool overrideActivity)
        {
            // Check active stations
            List <InputStation>  activeInputStations  = Instance.InputStations.Where(s => overrideActivity || s.ItemBundles.Any()).ToList();
            List <OutputStation> activeOutputStations = Instance.OutputStations.Where(s => overrideActivity || s.AssignedOrders.Any()).ToList();
            // Check whether something changed since last time
            IEnumerable <Circle> currentlyActiveStations = activeInputStations.Cast <Circle>().Concat(activeOutputStations);

            if (_lastActiveStations == null)
            {
                _lastActiveStations = new HashSet <Circle>(activeInputStations.Cast <Circle>().Concat(activeOutputStations));
            }
            else if (currentlyActiveStations.All(s => _lastActiveStations.Contains(s)) && currentlyActiveStations.Count() == _lastActiveStations.Count)
            {
                return;
            }
            else
            {
                _lastActiveStations = new HashSet <Circle>(activeInputStations.Cast <Circle>().Concat(activeOutputStations));
            }
            // Defining stations as active when there are available requests is too volatile, but maybe consider the request count somehow in the future?
            //List<InputStation> activeInputStations = Instance.ResourceManager.AvailableStoreRequests.Select(r => r.Station).Distinct().ToList();
            //List<OutputStation> activeOutputStations = Instance.ResourceManager.AvailableExtractRequests.Select(r => r.Station).Distinct().ToList();

            // Get count if input / output bots
            double activeWeightInputStations  = _config.WeightInputStations * ((double)activeInputStations.Count / Instance.InputStations.Count);
            double activeWeightOutputStations = _config.WeightOutputStations * ((double)activeOutputStations.Count / Instance.OutputStations.Count);

            // Handle the extreme case of no active stations
            if (activeWeightInputStations == 0 && activeWeightOutputStations == 0)
            {
                // Set all bots to unassigned and quit
                foreach (var bot in _bots)
                {
                    _botStations[bot] = null;
                    _unassignedBots.Add(bot);
                }
                foreach (var station in _stations)
                {
                    _stationBots[station].Clear();
                }
                return;
            }
            // Divide input and output station bots
            int inputStationBots  = Convert.ToInt32(Math.Floor(_bots.Count * (activeWeightInputStations / (activeWeightInputStations + activeWeightOutputStations))));
            int outputStationBots = Convert.ToInt32(Math.Floor(_bots.Count * (activeWeightOutputStations / (activeWeightInputStations + activeWeightOutputStations))));

            // If there is a 'fractional' bot, add it to the lower number
            if (inputStationBots + outputStationBots < _bots.Count)
            {
                if (inputStationBots < outputStationBots)
                {
                    inputStationBots += _bots.Count - inputStationBots - outputStationBots;
                }
                else
                {
                    outputStationBots += _bots.Count - inputStationBots - outputStationBots;
                }
            }
            // Order active stations by their preference for assigning bots
            activeInputStations =
                // Order active input stations by their distance to the middle of their tier (prefer stations more far away, because others are more likely to share bots)
                activeInputStations.OrderByDescending(s => s.GetDistance(s.Tier.Length / 2.0, s.Tier.Width / 2.0)).ToList();
            activeOutputStations =
                // Order active output stations by their distance to the middle of their tier (prefer stations more far away, because others are more likely to share bots)
                activeOutputStations.OrderByDescending(s => s.GetDistance(s.Tier.Length / 2.0, s.Tier.Width / 2.0)).ToList();
            // Obtain goal numbers per station
            Dictionary <Circle, int> stationBotGoals = _stations.ToDictionary(k => k, v => 0);

            for (int i = 0; inputStationBots > 0; i = (i + 1) % activeInputStations.Count)
            {
                if (stationBotGoals.ContainsKey(activeInputStations[i]))
                {
                    stationBotGoals[activeInputStations[i]]++;
                }
                else
                {
                    stationBotGoals[activeInputStations[i]] = 1;
                }
                inputStationBots--;
            }
            for (int i = 0; outputStationBots > 0; i = (i + 1) % activeOutputStations.Count)
            {
                if (stationBotGoals.ContainsKey(activeOutputStations[i]))
                {
                    stationBotGoals[activeOutputStations[i]]++;
                }
                else
                {
                    stationBotGoals[activeOutputStations[i]] = 1;
                }
                outputStationBots--;
            }
            // Limit bots per station
            int overflowBots = 0;

            foreach (var station in _stations)
            {
                if (stationBotGoals[station] > _config.BotsPerStationLimit)
                {
                    overflowBots            += stationBotGoals[station] - _config.BotsPerStationLimit;
                    stationBotGoals[station] = _config.BotsPerStationLimit;
                }
            }
            // Distribute overflow bots across other stations, if possible
            while (overflowBots > 0 && stationBotGoals.Any(s => s.Value < _config.BotsPerStationLimit))
            {
                // Select a station at the border of its tier (stations nearer to the middle should share bots more easily)
                Circle receivingStation = _stations.Where(s => stationBotGoals[s] < _config.BotsPerStationLimit).ArgMax(s => s.GetDistance(s.Tier.Length / 2.0, s.Tier.Width / 2.0));
                stationBotGoals[receivingStation]++;
                overflowBots--;
            }
            // Keep on reassigning until all stations meet their goal
            List <Tuple <Bot, Circle, Circle> > reassignments      = null;
            Dictionary <Circle, int>            previousAssignment = null;

            while (_stations.Any(s => stationBotGoals[s] != _stationBots[s].Count))
            {
                // Remember current assignment counts
                if (previousAssignment == null)
                {
                    previousAssignment = _stationBots.ToDictionary(k => k.Key, v => v.Value.Count);
                }
                // Get next switching partners
                Bot bestBot = null; Circle receivingStation = null; double bestValue = double.PositiveInfinity; bool unassignedBot = true;
                // Check all station not yet at their goal
                foreach (var station in _stations.Where(s => _stationBots[s].Count < stationBotGoals[s]))
                {
                    // Check all unassigned bots or bots that are assigned to a station with an overflow of bots
                    foreach (var bot in _unassignedBots.Concat(_stationBots.Where(s => _stationBots[s.Key].Count > stationBotGoals[s.Key]).SelectMany(s => s.Value)))
                    {
                        // See whether the bot is nearer to the station than the previous ones
                        double distance = (bot.TargetWaypoint != null ? bot.TargetWaypoint.GetDistance(station) : Distances.CalculateEuclid(bot, station, Instance.WrongTierPenaltyDistance));
                        if (distance < bestValue)
                        {
                            // Set new best pair
                            bestValue        = distance;
                            bestBot          = bot;
                            receivingStation = station;
                            unassignedBot    = _unassignedBots.Contains(bot);
                        }
                    }
                }
                // Store the switch
                if (reassignments != null)
                {
                    reassignments.Add(new Tuple <Bot, Circle, Circle>(bestBot, unassignedBot ? null : _botStations[bestBot], receivingStation));
                }
                else
                {
                    reassignments = new List <Tuple <Bot, Circle, Circle> >()
                    {
                        new Tuple <Bot, Circle, Circle>(bestBot, unassignedBot ? null : _botStations[bestBot], receivingStation)
                    }
                };
                // Perform the switch
                if (unassignedBot)
                {
                    _unassignedBots.Remove(bestBot);
                }
                else
                {
                    _stationBots[_botStations[bestBot]].Remove(bestBot);
                }
                _botStations[bestBot] = receivingStation;
                _stationBots[receivingStation].Add(bestBot);
            }
            // Log reassignments
            if (reassignments != null)
            {
                Instance.LogVerbose("Reassigning " + reassignments.Count + " bots:");
                Instance.LogVerbose("ID:   " + string.Join(",", Instance.InputStations.Select(s => "I" + s.ID).Concat(Instance.OutputStations.Select(s => "O" + s.ID)).Select(s => s.PadLeft(3))));
                Instance.LogVerbose("Work: " + string.Join(",",
                                                           Instance.InputStations.Select(s => (s.ItemBundles.Any() ? s.ItemBundles.Count().ToString() : " ")).Concat(
                                                               Instance.OutputStations.Select(s => (s.AssignedOrders.Any() ? s.AssignedOrders.Count().ToString() : " "))).Select(s => s.PadLeft(3))));
                Instance.LogVerbose("Bots: " + string.Join(",",
                                                           Instance.InputStations.Select(s => _stationBots[s].Count.ToString()).Concat(
                                                               Instance.OutputStations.Select(s => _stationBots[s].Count.ToString())).Select(s => s.PadLeft(3))));
                Instance.LogVerbose("Was:  " + string.Join(",",
                                                           Instance.InputStations.Select(s => previousAssignment[s].ToString()).Concat(
                                                               Instance.OutputStations.Select(s => previousAssignment[s].ToString())).Select(s => s.PadLeft(3))));
            }
        }
コード例 #18
0
        /// <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);
        }