1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-18 10:43:22 +08:00

Refactor TaikoDifficultyCalculator and add DifficultStrain attributes (#31191)

* refactor + countdifficultstrain

* norm in utils

* adjust scaling shift

* fix comment

* revert all value changes

* add the else back

* remove cds comments
This commit is contained in:
Jay Lawton 2024-12-19 21:32:59 +10:00 committed by GitHub
parent 0f2f25db53
commit 4ca88ae2d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 58 additions and 39 deletions

View File

@ -34,11 +34,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
[JsonProperty("colour_difficulty")]
public double ColourDifficulty { get; set; }
/// <summary>
/// The difficulty corresponding to the hardest parts of the map.
/// </summary>
[JsonProperty("peak_difficulty")]
public double PeakDifficulty { get; set; }
[JsonProperty("rhythm_difficult_strains")]
public double RhythmTopStrains { get; set; }
[JsonProperty("colour_difficult_strains")]
public double ColourTopStrains { get; set; }
[JsonProperty("stamina_difficult_strains")]
public double StaminaTopStrains { get; set; }
/// <summary>
/// The perceived hit window for a GREAT hit inclusive of rate-adjusting mods (DT/HT/etc).

View File

@ -8,6 +8,7 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Difficulty.Utils;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
@ -53,18 +54,25 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
{
List<DifficultyHitObject> difficultyHitObjects = new List<DifficultyHitObject>();
List<TaikoDifficultyHitObject> centreObjects = new List<TaikoDifficultyHitObject>();
List<TaikoDifficultyHitObject> rimObjects = new List<TaikoDifficultyHitObject>();
List<TaikoDifficultyHitObject> noteObjects = new List<TaikoDifficultyHitObject>();
var difficultyHitObjects = new List<DifficultyHitObject>();
var centreObjects = new List<TaikoDifficultyHitObject>();
var rimObjects = new List<TaikoDifficultyHitObject>();
var noteObjects = new List<TaikoDifficultyHitObject>();
// Generate TaikoDifficultyHitObjects from the beatmap's hit objects.
for (int i = 2; i < beatmap.HitObjects.Count; i++)
{
difficultyHitObjects.Add(
new TaikoDifficultyHitObject(
beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, difficultyHitObjects,
centreObjects, rimObjects, noteObjects, difficultyHitObjects.Count)
);
difficultyHitObjects.Add(new TaikoDifficultyHitObject(
beatmap.HitObjects[i],
beatmap.HitObjects[i - 1],
beatmap.HitObjects[i - 2],
clockRate,
difficultyHitObjects,
centreObjects,
rimObjects,
noteObjects,
difficultyHitObjects.Count
));
}
TaikoColourDifficultyPreprocessor.ProcessAndAssign(difficultyHitObjects);
@ -79,28 +87,33 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
bool isRelax = mods.Any(h => h is TaikoModRelax);
Colour colour = (Colour)skills.First(x => x is Colour);
Rhythm rhythm = (Rhythm)skills.First(x => x is Rhythm);
Colour colour = (Colour)skills.First(x => x is Colour);
Stamina stamina = (Stamina)skills.First(x => x is Stamina);
Stamina singleColourStamina = (Stamina)skills.Last(x => x is Stamina);
double colourRating = colour.DifficultyValue() * colour_skill_multiplier;
double rhythmRating = rhythm.DifficultyValue() * rhythm_skill_multiplier;
double colourRating = colour.DifficultyValue() * colour_skill_multiplier;
double staminaRating = stamina.DifficultyValue() * stamina_skill_multiplier;
double monoStaminaRating = singleColourStamina.DifficultyValue() * stamina_skill_multiplier;
double monoStaminaFactor = staminaRating == 0 ? 1 : Math.Pow(monoStaminaRating / staminaRating, 5);
double rhythmDifficultStrains = rhythm.CountTopWeightedStrains();
double colourDifficultStrains = colour.CountTopWeightedStrains();
double staminaDifficultStrains = stamina.CountTopWeightedStrains();
double combinedRating = combinedDifficultyValue(rhythm, colour, stamina, isRelax);
double starRating = rescale(combinedRating * 1.4);
// TODO: This is temporary measure as we don't detect abuse of multiple-input playstyles of converts within the current system.
// Converts are penalised outside the scope of difficulty calculation, as our assumptions surrounding standard play-styles becomes out-of-scope.
if (beatmap.BeatmapInfo.Ruleset.OnlineID == 0)
{
starRating *= 0.925;
// For maps with either relax or low colour variance and high stamina requirement, multiple inputs are more likely to be abused.
// For maps with relax, multiple inputs are more likely to be abused.
if (isRelax)
starRating *= 0.60;
// For maps with either relax or low colour variance and high stamina requirement, multiple inputs are more likely to be abused.
else if (colourRating < 2 && staminaRating > 8)
starRating *= 0.80;
}
@ -112,11 +125,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
{
StarRating = starRating,
Mods = mods,
StaminaDifficulty = staminaRating,
MonoStaminaFactor = monoStaminaFactor,
RhythmDifficulty = rhythmRating,
ColourDifficulty = colourRating,
PeakDifficulty = combinedRating,
StaminaDifficulty = staminaRating,
MonoStaminaFactor = monoStaminaFactor,
StaminaTopStrains = staminaDifficultStrains,
RhythmTopStrains = rhythmDifficultStrains,
ColourTopStrains = colourDifficultStrains,
GreatHitWindow = hitWindows.WindowFor(HitResult.Great) / clockRate,
OkHitWindow = hitWindows.WindowFor(HitResult.Ok) / clockRate,
MaxCombo = beatmap.GetMaxCombo(),
@ -125,17 +140,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
return attributes;
}
/// <summary>
/// Applies a final re-scaling of the star rating.
/// </summary>
/// <param name="sr">The raw star rating value before re-scaling.</param>
private double rescale(double sr)
{
if (sr < 0) return sr;
return 10.43 * Math.Log(sr / 8 + 1);
}
/// <summary>
/// Returns the combined star rating of the beatmap, calculated using peak strains from all sections of the map.
/// </summary>
@ -153,8 +157,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
for (int i = 0; i < colourPeaks.Count; i++)
{
double colourPeak = colourPeaks[i] * colour_skill_multiplier;
double rhythmPeak = rhythmPeaks[i] * rhythm_skill_multiplier;
double colourPeak = colourPeaks[i] * colour_skill_multiplier;
double staminaPeak = staminaPeaks[i] * stamina_skill_multiplier;
if (isRelax)
@ -163,8 +167,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
staminaPeak /= 1.5; // Stamina difficulty is decreased with an increased available finger count.
}
double peak = norm(1.5, colourPeak, staminaPeak);
peak = norm(2, peak, rhythmPeak);
double peak = DifficultyCalculationUtils.Norm(2, DifficultyCalculationUtils.Norm(1.5, colourPeak, staminaPeak), rhythmPeak);
// 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.
@ -185,10 +188,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
}
/// <summary>
/// Returns the <i>p</i>-norm of an <i>n</i>-dimensional vector.
/// Applies a final re-scaling of the star rating.
/// </summary>
/// <param name="p">The value of <i>p</i> to calculate the norm for.</param>
/// <param name="values">The coefficients of the vector.</param>
private double norm(double p, params double[] values) => Math.Pow(values.Sum(x => Math.Pow(x, p)), 1 / p);
/// <param name="sr">The raw star rating value before re-scaling.</param>
private double rescale(double sr)
{
if (sr < 0) return sr;
return 10.43 * Math.Log(sr / 8 + 1);
}
}
}

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
namespace osu.Game.Rulesets.Difficulty.Utils
{
@ -46,5 +47,13 @@ namespace osu.Game.Rulesets.Difficulty.Utils
/// <param name="exponent">Exponent</param>
/// <returns>The output of logistic function</returns>
public static double Logistic(double exponent, double maxValue = 1) => maxValue / (1 + Math.Exp(exponent));
/// <summary>
/// Returns the <i>p</i>-norm of an <i>n</i>-dimensional vector (https://en.wikipedia.org/wiki/Norm_(mathematics))
/// </summary>
/// <param name="p">The value of <i>p</i> to calculate the norm for.</param>
/// <param name="values">The coefficients of the vector.</param>
/// <returns>The <i>p</i>-norm of the vector.</returns>
public static double Norm(double p, params double[] values) => Math.Pow(values.Sum(x => Math.Pow(x, p)), 1 / p);
}
}