diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs
index ba247c68d459..de3bec5fcf2d 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs
@@ -14,13 +14,13 @@ public class TaikoDifficultyCalculatorTest : DifficultyCalculatorTest
{
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 25428c8b2fd3..3ff5b87fb6cf 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs
@@ -36,18 +36,70 @@ public static double EvaluateDifficultyOf(RepeatingHitPatterns repeatingHitPatte
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;
}
}