diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index 96180c0aa120..6aedc95da6d8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -2,11 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using System.Collections.Generic; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; -using System.Linq; using osu.Framework.Utils; +using osu.Framework.Lists; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -36,22 +37,26 @@ public override double DifficultyValue() Difficulty = 0; double weight = 1; - // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). - // These sections will not contribute to the difficulty. - var peaks = GetCurrentStrainPeaks().Where(p => p > 0); + SortedList strains = GetCurrentStrainsSorted(); - List strains = peaks.OrderDescending().ToList(); + int reducedSectionCount = Math.Min(strains.Count, ReducedSectionCount); + double[] reducedStrains = new double[reducedSectionCount]; // We are reducing the highest strains first to account for extreme difficulty spikes - for (int i = 0; i < Math.Min(strains.Count, ReducedSectionCount); i++) + for (int i = 0; i < reducedSectionCount; i++) { double scale = Math.Log10(Interpolation.Lerp(1, 10, Math.Clamp((float)i / ReducedSectionCount, 0, 1))); - strains[i] *= Interpolation.Lerp(ReducedStrainBaseline, 1.0, scale); + reducedStrains[i] = strains[i] * Interpolation.Lerp(ReducedStrainBaseline, 1.0, scale); } + // Remove reduced strains as they are no longer sorted + strains.RemoveRange(0, reducedSectionCount); + + // Insert them back + strains.AddRange(reducedStrains); + // Difficulty is the weighted sum of the highest strains from every section. - // We're sorting from highest to lowest strain. - foreach (double strain in strains.OrderDescending()) + foreach (double strain in strains) { Difficulty += strain * weight; weight *= DecayWeight; diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index b07e8399c024..7f7496106872 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Lists; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Mods; @@ -102,13 +103,9 @@ public override double DifficultyValue() double difficulty = 0; double weight = 1; - // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). - // These sections will not contribute to the difficulty. - var peaks = GetCurrentStrainPeaks().Where(p => p > 0); - // Difficulty is the weighted sum of the highest strains from every section. // We're sorting from highest to lowest strain. - foreach (double strain in peaks.OrderDescending()) + foreach (double strain in GetCurrentStrainsSorted()) { difficulty += strain * weight; weight *= DecayWeight; @@ -116,5 +113,37 @@ public override double DifficultyValue() return difficulty; } + + /// + /// Amount of strains that will be saved in the sorted strains list. + /// Use value = 0 in case you want all strains to be saved. + /// + protected virtual int MaxStrainCount => 200; + + protected SortedList GetCurrentStrainsSorted() + { + int newStrainsToAdd = strainPeaks.Count - amountOfStrainsAddedSinceSave; + + var peaks = strainPeaks.TakeLast(newStrainsToAdd).Where(s => s > 0); + savedSortedStrains.AddRange(peaks); + amountOfStrainsAddedSinceSave = strainPeaks.Count; + + // We're saving only the largest 200 strains + int excessStrainsCount = savedSortedStrains.Count - MaxStrainCount; + + if (MaxStrainCount > 0 && excessStrainsCount > 0) + { + savedSortedStrains.RemoveRange(savedSortedStrains.Count - excessStrainsCount, excessStrainsCount); + } + + var strainsWithCurrent = new SortedList((a, b) => a < b ? 1 : (a > b ? -1 : 0)); + strainsWithCurrent.AddRange(savedSortedStrains); + strainsWithCurrent.Add(currentSectionPeak); + + return strainsWithCurrent; + } + + private readonly SortedList savedSortedStrains = new SortedList((a, b) => a < b ? 1 : (a > b ? -1 : 0)); + private int amountOfStrainsAddedSinceSave; } }