Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion osu.Game.Rulesets.Taiko/Difficulty/Evaluators/RhythmEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,21 @@
double sameRhythm = 0;
double samePattern = 0;
double intervalPenalty = 0;
double gapPenalty = 0;

double hitWindow = hitObject.HitWindow(HitResult.Great);

if (rhythmData.SameRhythmGroupedHitObjects?.FirstHitObject == hitObject) // Difficulty for SameRhythmGroupedHitObjects
{
sameRhythm += 10.0 * evaluateDifficultyOf(rhythmData.SameRhythmGroupedHitObjects, hitWindow);
intervalPenalty = repeatedIntervalPenalty(rhythmData.SameRhythmGroupedHitObjects, hitWindow);
gapPenalty = longGapPenalty(rhythmData.SameRhythmGroupedHitObjects.Previous);
}

if (rhythmData.SamePatternsGroupedHitObjects?.FirstHitObject == hitObject) // Difficulty for SamePatternsGroupedHitObjects
samePattern += 1.15 * ratioDifficulty(rhythmData.SamePatternsGroupedHitObjects.IntervalRatio);

difficulty += Math.Max(sameRhythm, samePattern) * intervalPenalty;
difficulty += Math.Max(sameRhythm, samePattern) * intervalPenalty * gapPenalty;

return difficulty;
}
Expand Down Expand Up @@ -159,5 +161,34 @@
/// </summary>
private static double termPenalty(double ratio, int denominator, double power, double multiplier) =>
-multiplier * Math.Pow(Math.Cos(denominator * Math.PI * ratio), power);

/// <summary>
/// Frequent rhythm changes containing long gaps (i.e. 1/4 + 1/6 with 1/2 gaps) award more difficulty than expected.
/// Due to limitations of the current rhythm evaluation, these cases are targeted and penalised.
/// The previous hit object grouping is used as often the rhythm change *two* rhythms after a long gap awards the unexpected difficulty.
/// </summary>
private static double longGapPenalty(SameRhythmHitObjectGrouping? previousOrNull)
{
if (previousOrNull == null)
return 1.0;

SameRhythmHitObjectGrouping previous = (SameRhythmHitObjectGrouping) previousOrNull;

Check failure on line 175 in osu.Game.Rulesets.Taiko/Difficulty/Evaluators/RhythmEvaluator.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Cast is redundant. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0004)

Check failure on line 175 in osu.Game.Rulesets.Taiko/Difficulty/Evaluators/RhythmEvaluator.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0055)

Check failure on line 175 in osu.Game.Rulesets.Taiko/Difficulty/Evaluators/RhythmEvaluator.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Cast is redundant. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0004)

Check failure on line 175 in osu.Game.Rulesets.Taiko/Difficulty/Evaluators/RhythmEvaluator.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0055)

double gapInterval = previous.FirstHitObject.DeltaTime;
double rhythmInterval = previous.HitObjectInterval ?? gapInterval;

// The ratio of the gap before this rhythm to the rhythm itself.
double gapRatio = gapInterval / Math.Max(rhythmInterval, 1);

// The gap ratio normalised to represent if the gap is long.
double gapFactor = DifficultyCalculationUtils.Logistic(gapRatio, 1.75, 20);

double rhythmLength = previous.HitObjects.Count;

// The length in objects of this rhythm normalised to represent if the rhythm change is frequent enough to be penalised.
double lengthFactor = DifficultyCalculationUtils.Logistic(rhythmLength, 4, -2.5);

return 1.0 - 0.75 * gapFactor * lengthFactor;
}
}
}
Loading