protected double CalculateDifficulty(DifficultyType type) { double actualStrainStep = STRAIN_STEP * TimeRate; // Find the highest strain value within each strain step List <double> highestStrains = new List <double>(); double intervalEndTime = actualStrainStep; double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval OsuHitObjectDifficulty previousHitObject = null; foreach (OsuHitObjectDifficulty 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 { double decay = Math.Pow(OsuHitObjectDifficulty.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 (double strain in highestStrains) { difficulty += weight * strain; weight *= DECAY_WEIGHT; } return(difficulty); }
protected bool CalculateStrainValues() { // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. using (List <OsuHitObjectDifficulty> .Enumerator hitObjectsEnumerator = DifficultyHitObjects.GetEnumerator()) { if (!hitObjectsEnumerator.MoveNext()) { return(false); } OsuHitObjectDifficulty current = hitObjectsEnumerator.Current; // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. while (hitObjectsEnumerator.MoveNext()) { var next = hitObjectsEnumerator.Current; next?.CalculateStrains(current, TimeRate); current = next; } return(true); } }