From 4095b2662bc67da4e3eeb90da0d747b2cc135dcb Mon Sep 17 00:00:00 2001 From: Jay Lawton Date: Tue, 7 Jan 2025 21:36:56 +1000 Subject: [PATCH 1/2] Add `consistentRatioPenalty` to the `Colour` skill. (#31285) * fix colour * review fix Co-authored-by: StanR * remove cancelled out operand * increase nerf, adjust tests * fix automated spacing issues * up penalty * adjust tests * apply review changes * fix nullable hell --------- Co-authored-by: StanR --- .../TaikoDifficultyCalculatorTest.cs | 8 +-- .../Difficulty/Evaluators/ColourEvaluator.cs | 54 ++++++++++++++++++- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs index ba247c68d4..de3bec5fcf 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Taiko.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; - [TestCase(3.0950934814938953d, 200, "diffcalc-test")] - [TestCase(3.0950934814938953d, 200, "diffcalc-test-strong")] + [TestCase(2.837609165845338d, 200, "diffcalc-test")] + [TestCase(2.837609165845338d, 200, "diffcalc-test-strong")] public void Test(double expectedStarRating, int expectedMaxCombo, string name) => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(4.0839365008715403d, 200, "diffcalc-test")] - [TestCase(4.0839365008715403d, 200, "diffcalc-test-strong")] + [TestCase(3.8005218640444949, 200, "diffcalc-test")] + [TestCase(3.8005218640444949, 200, "diffcalc-test-strong")] public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new TaikoModDoubleTime()); diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 25428c8b2f..3ff5b87fb6 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -36,18 +36,70 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators return 2 * (1 - DifficultyCalculationUtils.Logistic(exponent: Math.E * repeatingHitPattern.RepetitionInterval - 2 * Math.E)); } + /// + /// Calculates a consistency penalty based on the number of consecutive consistent intervals, + /// considering the delta time between each colour sequence. + /// + /// The current hitObject to consider. + /// The allowable margin of error for determining whether ratios are consistent. + /// The maximum objects to check per count of consistent ratio. + private static double consistentRatioPenalty(TaikoDifficultyHitObject hitObject, double threshold = 0.01, int maxObjectsToCheck = 64) + { + int consistentRatioCount = 0; + double totalRatioCount = 0.0; + + TaikoDifficultyHitObject current = hitObject; + + for (int i = 0; i < maxObjectsToCheck; i++) + { + // Break if there is no valid previous object + if (current.Index <= 1) + break; + + var previousHitObject = (TaikoDifficultyHitObject)current.Previous(1); + + double currentRatio = current.Rhythm.Ratio; + double previousRatio = previousHitObject.Rhythm.Ratio; + + // A consistent interval is defined as the percentage difference between the two rhythmic ratios with the margin of error. + if (Math.Abs(1 - currentRatio / previousRatio) <= threshold) + { + consistentRatioCount++; + totalRatioCount += currentRatio; + break; + } + + // Move to the previous object + current = previousHitObject; + } + + // Ensure no division by zero + double ratioPenalty = 1 - totalRatioCount / (consistentRatioCount + 1) * 0.80; + + return ratioPenalty; + } + + /// + /// Evaluate the difficulty of the first hitobject within a colour streak. + /// public static double EvaluateDifficultyOf(DifficultyHitObject hitObject) { - TaikoDifficultyHitObjectColour colour = ((TaikoDifficultyHitObject)hitObject).Colour; + var taikoObject = (TaikoDifficultyHitObject)hitObject; + TaikoDifficultyHitObjectColour colour = taikoObject.Colour; double difficulty = 0.0d; if (colour.MonoStreak?.FirstHitObject == hitObject) // Difficulty for MonoStreak difficulty += EvaluateDifficultyOf(colour.MonoStreak); + if (colour.AlternatingMonoPattern?.FirstHitObject == hitObject) // Difficulty for AlternatingMonoPattern difficulty += EvaluateDifficultyOf(colour.AlternatingMonoPattern); + if (colour.RepeatingHitPattern?.FirstHitObject == hitObject) // Difficulty for RepeatingHitPattern difficulty += EvaluateDifficultyOf(colour.RepeatingHitPattern); + double consistencyPenalty = consistentRatioPenalty(taikoObject); + difficulty *= consistencyPenalty; + return difficulty; } } From 3b58d5e43565e9b16b94667972ba968dbea36ba1 Mon Sep 17 00:00:00 2001 From: StanR Date: Tue, 7 Jan 2025 17:49:55 +0500 Subject: [PATCH 2/2] Clamp OD in performance calculation to fix negative OD gaining pp (#31447) Co-authored-by: James Wilson --- .../Difficulty/OsuPerformanceCalculator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index df418fb3f8..5cf7a56d8a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -191,7 +191,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= accuracy; // It is important to consider accuracy difficulty when scaling with accuracy. - aimValue *= 0.98 + Math.Pow(attributes.OverallDifficulty, 2) / 2500; + aimValue *= 0.98 + Math.Pow(Math.Max(0, attributes.OverallDifficulty), 2) / 2500; return aimValue; } @@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double relevantAccuracy = attributes.SpeedNoteCount == 0 ? 0 : (relevantCountGreat * 6.0 + relevantCountOk * 2.0 + relevantCountMeh) / (attributes.SpeedNoteCount * 6.0); // Scale the speed value with accuracy and OD. - speedValue *= (0.95 + Math.Pow(attributes.OverallDifficulty, 2) / 750) * Math.Pow((accuracy + relevantAccuracy) / 2.0, (14.5 - attributes.OverallDifficulty) / 2); + speedValue *= (0.95 + Math.Pow(Math.Max(0, attributes.OverallDifficulty), 2) / 750) * Math.Pow((accuracy + relevantAccuracy) / 2.0, (14.5 - attributes.OverallDifficulty) / 2); // Scale the speed value with # of 50s to punish doubletapping. speedValue *= Math.Pow(0.99, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); @@ -305,7 +305,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Scale the flashlight value with accuracy _slightly_. flashlightValue *= 0.5 + accuracy / 2.0; // It is important to also consider accuracy difficulty when doing that. - flashlightValue *= 0.98 + Math.Pow(attributes.OverallDifficulty, 2) / 2500; + flashlightValue *= 0.98 + Math.Pow(Math.Max(0, attributes.OverallDifficulty), 2) / 2500; return flashlightValue; }