From a7a25eb91c1ce8b43ec1b478595e1acf15a2647f Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sat, 17 Aug 2024 16:17:31 +0300 Subject: [PATCH 1/6] optimisation for std counter --- .../Difficulty/OsuDifficultyCalculator.cs | 11 ++- .../Difficulty/Skills/OsuStrainSkill.cs | 23 +++-- .../Difficulty/DifficultyCalculator.cs | 40 ++++++++- .../Rulesets/Difficulty/Skills/StrainSkill.cs | 84 +++++++++++++++++-- 4 files changed, 135 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index e93475ecff04..1a4d979b5b27 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -81,11 +81,14 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; double drainRate = beatmap.Difficulty.DrainRate; - int maxCombo = beatmap.GetMaxCombo(); - int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle); - int sliderCount = beatmap.HitObjects.Count(h => h is Slider); - int spinnerCount = beatmap.HitObjects.Count(h => h is Spinner); + ProgressiveCalculationBeatmap pcBeatmap = beatmap as ProgressiveCalculationBeatmap; + + int maxCombo = pcBeatmap != null ? pcBeatmap.GetMaxCombo() : beatmap.GetMaxCombo(); + + int hitCirclesCount = pcBeatmap != null ? pcBeatmap.GetHitObjectCountOf(typeof(HitCircle)) : beatmap.HitObjects.Count(h => h is HitCircle); + int sliderCount = pcBeatmap != null ? pcBeatmap.GetHitObjectCountOf(typeof(Slider)) : beatmap.HitObjects.Count(h => h is Slider); + int spinnerCount = pcBeatmap != null ? pcBeatmap.GetHitObjectCountOf(typeof(Spinner)) : beatmap.HitObjects.Count(h => h is Spinner); HitWindows hitWindows = new OsuHitWindows(); hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index c4068ef0d72f..f3a3cc4fc5a6 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -44,28 +44,33 @@ 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); + List 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 + foreach (double reducedStrain in reducedStrains) + InsertElementInReverseSortedList(strains, reducedStrain); + // 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; } - return difficulty * DifficultyMultiplier; + return difficulty * DifficultyMultiplier; } public static double DifficultyToPerformance(double difficulty) => Math.Pow(5.0 * Math.Max(1.0, difficulty / 0.0675) - 4.0, 3.0) / 100000.0; diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 63b27243d02a..e7d78a98df35 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -18,6 +18,7 @@ using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Utils; namespace osu.Game.Rulesets.Difficulty @@ -114,7 +115,7 @@ public List CalculateTimed([NotNull] IEnumerable foreach (var obj in Beatmap.HitObjects) { - progressiveBeatmap.HitObjects.Add(obj); + progressiveBeatmap.AddHitObject(obj); while (currentIndex < difficultyObjects.Length && difficultyObjects[currentIndex].BaseObject.GetEndTime() <= obj.GetEndTime()) { @@ -293,7 +294,7 @@ static IEnumerable createDifficultyAdjustmentModCombinations(ReadOnlyMemory /// /// Used to calculate timed difficulty attributes, where only a subset of hitobjects should be visible at any point in time. /// - private class ProgressiveCalculationBeatmap : IBeatmap + protected class ProgressiveCalculationBeatmap : IBeatmap { private readonly IBeatmap baseBeatmap; @@ -302,9 +303,40 @@ public ProgressiveCalculationBeatmap(IBeatmap baseBeatmap) this.baseBeatmap = baseBeatmap; } - public readonly List HitObjects = new List(); + public void AddHitObject(HitObject hitObject) + { + hitObjects.Add(hitObject); + + var objectType = hitObject.GetType(); + if (!hitObjectsCounts.ContainsKey(objectType)) + hitObjectsCounts[objectType] = 0; // Initialize to 0 if not present + hitObjectsCounts[objectType]++; + } + + private readonly List hitObjects = new List(); + + private Dictionary hitObjectsCounts = new Dictionary(); + + public int GetHitObjectCountOf(Type type) => hitObjectsCounts.GetValueOrDefault(type); + + IReadOnlyList IBeatmap.HitObjects => hitObjects; - IReadOnlyList IBeatmap.HitObjects => HitObjects; + private int comboObjectIndex = 0, combo = 0; + public int GetMaxCombo() + { + for (; comboObjectIndex < hitObjects.Count; comboObjectIndex++) + addCombo(hitObjects[comboObjectIndex], ref combo); + return combo; + + static void addCombo(HitObject hitObject, ref int combo) + { + if (hitObject.Judgement.MaxResult.AffectsCombo()) + combo++; + + foreach (var nested in hitObject.NestedHitObjects) + addCombo(nested, ref combo); + } + } #region Delegated IBeatmap implementation diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index b07e8399c024..b97122e07a9b 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -55,9 +55,16 @@ public sealed override void Process(DifficultyHitObject current) saveCurrentPeak(); startNewSectionFrom(currentSectionEnd, current); currentSectionEnd += SectionLength; + + amountOfStrainsAddedSinceSave++; } - currentSectionPeak = Math.Max(StrainValueAt(current), currentSectionPeak); + double currentStrain = StrainValueAt(current); + if (currentSectionPeak < currentStrain) + { + currentSectionPeak = currentStrain; + isSavedCurrentStrainRelevant = false; + } } /// @@ -102,13 +109,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 +119,74 @@ public override double DifficultyValue() return difficulty; } + + protected List GetCurrentStrainsSorted() + { + List strains; + + // If no saved strains - calculate them from 0, and save them after that + if (savedSortedStrains == null || savedSortedStrains.Count == 0) + { + var peaks = GetCurrentStrainPeaks().Where(p => p > 0); + + strains = peaks.OrderDescending().ToList(); + + savedSortedStrains = new List(strains); + amountOfStrainsAddedSinceSave = 0; + savedCurrentStrain = currentSectionPeak > 0 ? currentSectionPeak : -1; + isSavedCurrentStrainRelevant = true; + } + // If several sections were added since last save - insert them into saved strains list + else if (amountOfStrainsAddedSinceSave > 0) + { + var newPeaks = GetCurrentStrainPeaks().TakeLast(amountOfStrainsAddedSinceSave).Where(p => p > 0); + foreach (double newPeak in newPeaks) + InsertElementInReverseSortedList(savedSortedStrains, newPeak); + + strains = new List(savedSortedStrains); + + amountOfStrainsAddedSinceSave = 0; + savedCurrentStrain = currentSectionPeak > 0 ? currentSectionPeak : -1; + isSavedCurrentStrainRelevant = true; + } + // If no section was added, but last one was changed - find it and replace it with new one + else if (!isSavedCurrentStrainRelevant && savedCurrentStrain != -1) + { + int invalidStrainIndex = savedSortedStrains.BinarySearch(savedCurrentStrain, new ReverseComparer()); + savedSortedStrains.RemoveAt(invalidStrainIndex); + InsertElementInReverseSortedList(savedSortedStrains, currentSectionPeak); + + strains = new List(savedSortedStrains); + + savedCurrentStrain = currentSectionPeak > 0 ? currentSectionPeak : -1; + isSavedCurrentStrainRelevant = true; + } + // Otherwise - just use saved strains + else + { + strains = new List(savedSortedStrains); + } + + return strains; + } + + private List? savedSortedStrains; + private double savedCurrentStrain = -1; + private bool isSavedCurrentStrainRelevant = false; + private int amountOfStrainsAddedSinceSave = 0; + + protected static void InsertElementInReverseSortedList(List list, double element) + { + int indexToInsert = list.BinarySearch(element, new ReverseComparer()); + if (indexToInsert < 0) + indexToInsert = ~indexToInsert; + + list.Insert(indexToInsert, element); + } + + private class ReverseComparer : IComparer + { + public int Compare(double x, double y) => Comparer.Default.Compare(y, x); + } } } From df0044376456ce0cee29701ef959df3975715cb3 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sat, 17 Aug 2024 17:58:03 +0300 Subject: [PATCH 2/6] fix CI --- osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index f3a3cc4fc5a6..1a952ba357ea 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; -using System.Linq; using osu.Framework.Utils; namespace osu.Game.Rulesets.Osu.Difficulty.Skills @@ -70,7 +69,7 @@ public override double DifficultyValue() weight *= DecayWeight; } - return difficulty * DifficultyMultiplier; + return difficulty * DifficultyMultiplier; } public static double DifficultyToPerformance(double difficulty) => Math.Pow(5.0 * Math.Max(1.0, difficulty / 0.0675) - 4.0, 3.0) / 100000.0; From cdd8f5572932dd14c38085914b27248d71ece55e Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sat, 17 Aug 2024 18:21:37 +0300 Subject: [PATCH 3/6] removed unnecessary complexity --- osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index b97122e07a9b..6bce37961ebc 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -133,7 +133,7 @@ protected List GetCurrentStrainsSorted() savedSortedStrains = new List(strains); amountOfStrainsAddedSinceSave = 0; - savedCurrentStrain = currentSectionPeak > 0 ? currentSectionPeak : -1; + savedCurrentStrain = currentSectionPeak; isSavedCurrentStrainRelevant = true; } // If several sections were added since last save - insert them into saved strains list @@ -146,11 +146,11 @@ protected List GetCurrentStrainsSorted() strains = new List(savedSortedStrains); amountOfStrainsAddedSinceSave = 0; - savedCurrentStrain = currentSectionPeak > 0 ? currentSectionPeak : -1; + savedCurrentStrain = currentSectionPeak; isSavedCurrentStrainRelevant = true; } // If no section was added, but last one was changed - find it and replace it with new one - else if (!isSavedCurrentStrainRelevant && savedCurrentStrain != -1) + else if (!isSavedCurrentStrainRelevant && savedCurrentStrain > 0) { int invalidStrainIndex = savedSortedStrains.BinarySearch(savedCurrentStrain, new ReverseComparer()); savedSortedStrains.RemoveAt(invalidStrainIndex); @@ -158,7 +158,7 @@ protected List GetCurrentStrainsSorted() strains = new List(savedSortedStrains); - savedCurrentStrain = currentSectionPeak > 0 ? currentSectionPeak : -1; + savedCurrentStrain = currentSectionPeak; isSavedCurrentStrainRelevant = true; } // Otherwise - just use saved strains @@ -171,7 +171,7 @@ protected List GetCurrentStrainsSorted() } private List? savedSortedStrains; - private double savedCurrentStrain = -1; + private double savedCurrentStrain = 0; private bool isSavedCurrentStrainRelevant = false; private int amountOfStrainsAddedSinceSave = 0; From 8b6106c2fe27136436cfb87a37045db740bbbf7b Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sat, 17 Aug 2024 20:27:39 +0300 Subject: [PATCH 4/6] fixed CI (again) --- .../Difficulty/OsuDifficultyCalculator.cs | 22 ++++++++++++++----- .../Difficulty/DifficultyCalculator.cs | 5 +++-- .../Rulesets/Difficulty/Skills/StrainSkill.cs | 7 +++--- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 1a4d979b5b27..08141b684bc4 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -82,13 +82,25 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; double drainRate = beatmap.Difficulty.DrainRate; - ProgressiveCalculationBeatmap pcBeatmap = beatmap as ProgressiveCalculationBeatmap; + int maxCombo; + int hitCirclesCount, sliderCount, spinnerCount; - int maxCombo = pcBeatmap != null ? pcBeatmap.GetMaxCombo() : beatmap.GetMaxCombo(); + if (beatmap is ProgressiveCalculationBeatmap pcBeatmap) + { + maxCombo = pcBeatmap.GetMaxCombo(); - int hitCirclesCount = pcBeatmap != null ? pcBeatmap.GetHitObjectCountOf(typeof(HitCircle)) : beatmap.HitObjects.Count(h => h is HitCircle); - int sliderCount = pcBeatmap != null ? pcBeatmap.GetHitObjectCountOf(typeof(Slider)) : beatmap.HitObjects.Count(h => h is Slider); - int spinnerCount = pcBeatmap != null ? pcBeatmap.GetHitObjectCountOf(typeof(Spinner)) : beatmap.HitObjects.Count(h => h is Spinner); + hitCirclesCount = pcBeatmap.GetHitObjectCountOf(typeof(HitCircle)); + sliderCount = pcBeatmap.GetHitObjectCountOf(typeof(Slider)); + spinnerCount = pcBeatmap.GetHitObjectCountOf(typeof(Spinner)); + } + else + { + maxCombo = beatmap.GetMaxCombo(); + + hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle); + sliderCount = beatmap.HitObjects.Count(h => h is Slider); + spinnerCount = beatmap.HitObjects.Count(h => h is Spinner); + } HitWindows hitWindows = new OsuHitWindows(); hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index e7d78a98df35..8922dd9256d4 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -315,13 +315,14 @@ public void AddHitObject(HitObject hitObject) private readonly List hitObjects = new List(); - private Dictionary hitObjectsCounts = new Dictionary(); + private readonly Dictionary hitObjectsCounts = new Dictionary(); public int GetHitObjectCountOf(Type type) => hitObjectsCounts.GetValueOrDefault(type); IReadOnlyList IBeatmap.HitObjects => hitObjects; - private int comboObjectIndex = 0, combo = 0; + private int comboObjectIndex, combo; + public int GetMaxCombo() { for (; comboObjectIndex < hitObjects.Count; comboObjectIndex++) diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index 6bce37961ebc..2dc594068a21 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -60,6 +60,7 @@ public sealed override void Process(DifficultyHitObject current) } double currentStrain = StrainValueAt(current); + if (currentSectionPeak < currentStrain) { currentSectionPeak = currentStrain; @@ -171,9 +172,9 @@ protected List GetCurrentStrainsSorted() } private List? savedSortedStrains; - private double savedCurrentStrain = 0; - private bool isSavedCurrentStrainRelevant = false; - private int amountOfStrainsAddedSinceSave = 0; + private double savedCurrentStrain; + private bool isSavedCurrentStrainRelevant; + private int amountOfStrainsAddedSinceSave; protected static void InsertElementInReverseSortedList(List list, double element) { From 29bf1637b9f1fba88b2e72ad1c44a0ca088b27ac Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Mon, 19 Aug 2024 17:44:05 +0300 Subject: [PATCH 5/6] fixed mentioned issues --- .../Difficulty/OsuDifficultyCalculator.cs | 22 +++---------- osu.Game/Beatmaps/Beatmap.cs | 20 +++++++++++ osu.Game/Beatmaps/IBeatmap.cs | 31 ++++++----------- .../Difficulty/DifficultyCalculator.cs | 33 +++++++++---------- osu.Game/Screens/Edit/EditorBeatmap.cs | 4 +++ 5 files changed, 53 insertions(+), 57 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 08141b684bc4..061007589837 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -82,25 +82,11 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; double drainRate = beatmap.Difficulty.DrainRate; - int maxCombo; - int hitCirclesCount, sliderCount, spinnerCount; + int maxCombo = beatmap.GetMaxCombo(); - if (beatmap is ProgressiveCalculationBeatmap pcBeatmap) - { - maxCombo = pcBeatmap.GetMaxCombo(); - - hitCirclesCount = pcBeatmap.GetHitObjectCountOf(typeof(HitCircle)); - sliderCount = pcBeatmap.GetHitObjectCountOf(typeof(Slider)); - spinnerCount = pcBeatmap.GetHitObjectCountOf(typeof(Spinner)); - } - else - { - maxCombo = beatmap.GetMaxCombo(); - - hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle); - sliderCount = beatmap.HitObjects.Count(h => h is Slider); - spinnerCount = beatmap.HitObjects.Count(h => h is Spinner); - } + int hitCirclesCount = beatmap.GetHitObjectCountOf(typeof(HitCircle)); + int sliderCount = beatmap.GetHitObjectCountOf(typeof(Slider)); + int spinnerCount = beatmap.GetHitObjectCountOf(typeof(Spinner)); HitWindows hitWindows = new OsuHitWindows(); hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 282f8fe7941c..a60e9cbd31a3 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -10,6 +10,7 @@ using Newtonsoft.Json; using osu.Framework.Lists; using osu.Game.IO.Serialization.Converters; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Beatmaps { @@ -115,6 +116,25 @@ public double GetMostCommonBeatLength() return mostCommon.beatLength; } + public int GetMaxCombo() + { + int combo = 0; + foreach (var h in HitObjects) + addCombo(h, ref combo); + return combo; + + static void addCombo(HitObject hitObject, ref int combo) + { + if (hitObject.Judgement.MaxResult.AffectsCombo()) + combo++; + + foreach (var nested in hitObject.NestedHitObjects) + addCombo(nested, ref combo); + } + } + + public int GetHitObjectCountOf(Type type) => HitObjects.Count(h => h.GetType() == type); + IBeatmap IBeatmap.Clone() => Clone(); public Beatmap Clone() => (Beatmap)MemberwiseClone(); diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index 430a31769bcd..0f316a81478d 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -8,7 +8,6 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Beatmaps { @@ -74,6 +73,16 @@ public interface IBeatmap /// /// The shallow-cloned beatmap. IBeatmap Clone(); + + /// + /// Finds the maximum achievable combo by hitting all s in a beatmap. + /// + int GetMaxCombo(); + + /// + /// Finds amount of s that have given type. + /// + int GetHitObjectCountOf(Type type); } /// @@ -90,26 +99,6 @@ public interface IBeatmap : IBeatmap public static class BeatmapExtensions { - /// - /// Finds the maximum achievable combo by hitting all s in a beatmap. - /// - public static int GetMaxCombo(this IBeatmap beatmap) - { - int combo = 0; - foreach (var h in beatmap.HitObjects) - addCombo(h, ref combo); - return combo; - - static void addCombo(HitObject hitObject, ref int combo) - { - if (hitObject.Judgement.MaxResult.AffectsCombo()) - combo++; - - foreach (var nested in hitObject.NestedHitObjects) - addCombo(nested, ref combo); - } - } - /// /// Find the total milliseconds between the first and last hittable objects. /// diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 8922dd9256d4..56b37d7c6a07 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -303,6 +303,10 @@ public ProgressiveCalculationBeatmap(IBeatmap baseBeatmap) this.baseBeatmap = baseBeatmap; } + private int maxCombo; + + public int GetMaxCombo() => maxCombo; + public void AddHitObject(HitObject hitObject) { hitObjects.Add(hitObject); @@ -311,6 +315,17 @@ public void AddHitObject(HitObject hitObject) if (!hitObjectsCounts.ContainsKey(objectType)) hitObjectsCounts[objectType] = 0; // Initialize to 0 if not present hitObjectsCounts[objectType]++; + + addCombo(hitObject); + + void addCombo(HitObject hitObject) + { + if (hitObject.Judgement.MaxResult.AffectsCombo()) + maxCombo++; + + foreach (var nested in hitObject.NestedHitObjects) + addCombo(nested); + }; } private readonly List hitObjects = new List(); @@ -321,24 +336,6 @@ public void AddHitObject(HitObject hitObject) IReadOnlyList IBeatmap.HitObjects => hitObjects; - private int comboObjectIndex, combo; - - public int GetMaxCombo() - { - for (; comboObjectIndex < hitObjects.Count; comboObjectIndex++) - addCombo(hitObjects[comboObjectIndex], ref combo); - return combo; - - static void addCombo(HitObject hitObject, ref int combo) - { - if (hitObject.Judgement.MaxResult.AffectsCombo()) - combo++; - - foreach (var nested in hitObject.NestedHitObjects) - addCombo(nested, ref combo); - } - } - #region Delegated IBeatmap implementation public BeatmapInfo BeatmapInfo diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index ad31c2ccc324..fc20e254e4c4 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -198,6 +198,10 @@ SortedList IBeatmap.Breaks public double GetMostCommonBeatLength() => PlayableBeatmap.GetMostCommonBeatLength(); + public int GetMaxCombo() => PlayableBeatmap.GetMaxCombo(); + + public int GetHitObjectCountOf(Type type) => PlayableBeatmap.GetHitObjectCountOf(type); + public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone(); private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects; From b1b782a26ba81dba2560722dc9303f64b54ae30b Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Mon, 19 Aug 2024 17:56:41 +0300 Subject: [PATCH 6/6] fixed CI --- .../Rulesets/Difficulty/DifficultyCalculator.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 56b37d7c6a07..1eb49539720a 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -294,7 +294,7 @@ static IEnumerable createDifficultyAdjustmentModCombinations(ReadOnlyMemory /// /// Used to calculate timed difficulty attributes, where only a subset of hitobjects should be visible at any point in time. /// - protected class ProgressiveCalculationBeatmap : IBeatmap + private class ProgressiveCalculationBeatmap : IBeatmap { private readonly IBeatmap baseBeatmap; @@ -317,15 +317,15 @@ public void AddHitObject(HitObject hitObject) hitObjectsCounts[objectType]++; addCombo(hitObject); + } - void addCombo(HitObject hitObject) - { - if (hitObject.Judgement.MaxResult.AffectsCombo()) - maxCombo++; + private void addCombo(HitObject hitObject) + { + if (hitObject.Judgement.MaxResult.AffectsCombo()) + maxCombo++; - foreach (var nested in hitObject.NestedHitObjects) - addCombo(nested); - }; + foreach (var nested in hitObject.NestedHitObjects) + addCombo(nested); } private readonly List hitObjects = new List();