1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-14 19:53:08 +08:00
osu-lazer/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs
2025-02-05 15:08:15 +09:00

92 lines
4.4 KiB
C#

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Utils;
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour;
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data;
namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
{
public static class ColourEvaluator
{
/// <summary>
/// Calculates a consistency penalty based on the number of consecutive consistent intervals,
/// considering the delta time between each colour sequence.
/// </summary>
/// <param name="hitObject">The current hitObject to consider.</param>
/// <param name="threshold"> The allowable margin of error for determining whether ratios are consistent.</param>
/// <param name="maxObjectsToCheck">The maximum objects to check per count of consistent ratio.</param>
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.RhythmData.Ratio;
double previousRatio = previousHitObject.RhythmData.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;
}
/// <summary>
/// Evaluate the difficulty of the first hitobject within a colour streak.
/// </summary>
public static double EvaluateDifficultyOf(DifficultyHitObject hitObject)
{
var taikoObject = (TaikoDifficultyHitObject)hitObject;
TaikoColourData colourData = taikoObject.ColourData;
double difficulty = 0.0d;
if (colourData.MonoStreak?.FirstHitObject == hitObject) // Difficulty for MonoStreak
difficulty += evaluateMonoStreakDifficulty(colourData.MonoStreak);
if (colourData.AlternatingMonoPattern?.FirstHitObject == hitObject) // Difficulty for AlternatingMonoPattern
difficulty += evaluateAlternatingMonoPatternDifficulty(colourData.AlternatingMonoPattern);
if (colourData.RepeatingHitPattern?.FirstHitObject == hitObject) // Difficulty for RepeatingHitPattern
difficulty += evaluateRepeatingHitPatternsDifficulty(colourData.RepeatingHitPattern);
double consistencyPenalty = consistentRatioPenalty(taikoObject);
difficulty *= consistencyPenalty;
return difficulty;
}
private static double evaluateMonoStreakDifficulty(MonoStreak monoStreak) =>
DifficultyCalculationUtils.Logistic(exponent: Math.E * monoStreak.Index - 2 * Math.E) * evaluateAlternatingMonoPatternDifficulty(monoStreak.Parent) * 0.5;
private static double evaluateAlternatingMonoPatternDifficulty(AlternatingMonoPattern alternatingMonoPattern) =>
DifficultyCalculationUtils.Logistic(exponent: Math.E * alternatingMonoPattern.Index - 2 * Math.E) * evaluateRepeatingHitPatternsDifficulty(alternatingMonoPattern.Parent);
private static double evaluateRepeatingHitPatternsDifficulty(RepeatingHitPatterns repeatingHitPattern) =>
2 * (1 - DifficultyCalculationUtils.Logistic(exponent: Math.E * repeatingHitPattern.RepetitionInterval - 2 * Math.E));
}
}