Ejemplo n.º 1
0
        protected double CalculateDifficulty(DifficultyType type)
        {
            var actualStrainStep = STRAIN_STEP * TimeRate;

            // Find the highest strain value within each strain step
            var    highestStrains  = new List <double>();
            var    intervalEndTime = actualStrainStep;
            double maximumStrain   = 0; // We need to keep track of the maximum strain in the current interval

            RpHitObjectDifficulty previousHitObject = null;

            foreach (var hitObject in DifficultyHitObjects)
            {
                // While we are beyond the current interval push the currently available maximum to our strain list
                while (hitObject.BaseHitObject.StartTime > intervalEndTime)
                {
                    highestStrains.Add(maximumStrain);

                    // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
                    // until the beginning of the next interval.
                    if (previousHitObject == null)
                    {
                        maximumStrain = 0;
                    }
                    else
                    {
                        var decay = Math.Pow(RpHitObjectDifficulty.DECAY_BASE[(int)type], (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
                        maximumStrain = previousHitObject.Strains[(int)type] * decay;
                    }

                    // Go to the next time interval
                    intervalEndTime += actualStrainStep;
                }

                // Obtain maximum strain
                maximumStrain = Math.Max(hitObject.Strains[(int)type], maximumStrain);

                previousHitObject = hitObject;
            }

            // Build the weighted sum over the highest strains for each interval
            double difficulty = 0;
            double weight     = 1;

            highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.

            foreach (var strain in highestStrains)
            {
                difficulty += weight * strain;
                weight     *= DECAY_WEIGHT;
            }

            return(difficulty);
        }
Ejemplo n.º 2
0
        private void calculateSpecificStrain(RpHitObjectDifficulty previousHitObject, RpDifficultyCalculator.DifficultyType type, double timeRate)
        {
            double addition    = 0;
            var    timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
            var    decay       = Math.Pow(DECAY_BASE[(int)type], timeElapsed / 1000);

            if (BaseHitObject.ObjectType == ObjectType.ContainerGroup)
            {
                // Do nothing for spinners
            }
            else if (BaseHitObject.ObjectType == ObjectType.Hold)
            {
                switch (type)
                {
                case RpDifficultyCalculator.DifficultyType.Speed:

                    // For speed strain we treat the whole slider as a single spacing entity, since "Speed" is about how hard it is to click buttons fast.
                    // The spacing weight exists to differentiate between being able to easily alternate or having to single.
                    addition =
                        spacingWeight(previousHitObject.lazySliderLength +
                                      DistanceTo(previousHitObject), type) *
                        spacing_weight_scaling[(int)type];

                    break;

                case RpDifficultyCalculator.DifficultyType.Aim:

                    // For Aim strain we treat each slider segment and the jump after the end of the slider as separate jumps, since movement-wise there is no difference
                    // to multiple jumps.
                    addition =
                        (
                            spacingWeight(previousHitObject.lazySliderLength, type) +
                            spacingWeight(DistanceTo(previousHitObject), type)
                        ) *
                        spacing_weight_scaling[(int)type];

                    break;
                }
            }
            else if (BaseHitObject.ObjectType == ObjectType.Hit)
            {
                addition = spacingWeight(DistanceTo(previousHitObject), type) * spacing_weight_scaling[(int)type];
            }

            // Scale addition by the time, that elapsed. Filter out HitObjects that are too close to be played anyway to avoid crazy values by division through close to zero.
            // You will never find maps that require this amongst ranked maps.
            addition /= Math.Max(timeElapsed, 50);

            Strains[(int)type] = previousHitObject.Strains[(int)type] * decay + addition;
        }
Ejemplo n.º 3
0
 internal double DistanceTo(RpHitObjectDifficulty other)
 {
     // Scale the distance by circle size.
     return((startPosition - other.endPosition).Length * scalingFactor);
 }
Ejemplo n.º 4
0
 internal void CalculateStrains(RpHitObjectDifficulty previousHitObject, double timeRate)
 {
     calculateSpecificStrain(previousHitObject, RpDifficultyCalculator.DifficultyType.Speed, timeRate);
     calculateSpecificStrain(previousHitObject, RpDifficultyCalculator.DifficultyType.Aim, timeRate);
 }