From 2f2006715eab49d36b0205ea72f6fa75f4d6c69f Mon Sep 17 00:00:00 2001 From: StanR Date: Fri, 17 Dec 2021 23:39:03 +0300 Subject: [PATCH] Slightly refactor difficulty and pp calculators --- .../Difficulty/CatchPerformanceCalculator.cs | 6 +++ .../Difficulty/ManiaDifficultyAttributes.cs | 4 +- .../Difficulty/ManiaPerformanceCalculator.cs | 39 ++++++++++--------- .../Difficulty/OsuDifficultyAttributes.cs | 28 ++++++------- .../Difficulty/OsuDifficultyCalculator.cs | 6 +-- .../Difficulty/OsuPerformanceCalculator.cs | 25 ++++++------ .../Difficulty/TaikoDifficultyAttributes.cs | 16 ++++---- .../Difficulty/TaikoDifficultyCalculator.cs | 6 +-- .../Difficulty/TaikoPerformanceCalculator.cs | 25 ++++++------ .../Difficulty/DifficultyAttributes.cs | 2 +- 10 files changed, 85 insertions(+), 72 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index 439890dac2..d3f3445ff1 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -90,6 +90,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (mods.Any(m => m is ModNoFail)) value *= 0.90; + if (categoryDifficulty != null) + { + categoryDifficulty.Add("AR", Attributes.ApproachRate); + categoryDifficulty.Add("Max Combo", Attributes.MaxCombo); + } + return value; } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs index bfdef893e9..979a04ddf8 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty yield return v; // Todo: osu!mania doesn't output MaxCombo attribute for some reason. - yield return (ATTRIB_ID_STRAIN, StarRating); + yield return (ATTRIB_ID_DIFFICULTY, StarRating); yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow); yield return (ATTRIB_ID_SCORE_MULTIPLIER, ScoreMultiplier); } @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty { base.FromDatabaseAttributes(values); - StarRating = values[ATTRIB_ID_STRAIN]; + StarRating = values[ATTRIB_ID_DIFFICULTY]; GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW]; ScoreMultiplier = values[ATTRIB_ID_SCORE_MULTIPLIER]; } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs index b04ff3548f..6bf0a5acb4 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs @@ -61,48 +61,51 @@ namespace osu.Game.Rulesets.Mania.Difficulty if (mods.Any(m => m is ModEasy)) multiplier *= 0.5; - double strainValue = computeStrainValue(); - double accValue = computeAccuracyValue(strainValue); + double difficultyValue = computeDifficultyValue(); + double accValue = computeAccuracyValue(difficultyValue); double totalValue = Math.Pow( - Math.Pow(strainValue, 1.1) + + Math.Pow(difficultyValue, 1.1) + Math.Pow(accValue, 1.1), 1.0 / 1.1 ) * multiplier; if (categoryDifficulty != null) { - categoryDifficulty["Strain"] = strainValue; - categoryDifficulty["Accuracy"] = accValue; + categoryDifficulty.Add("Difficulty", difficultyValue); + categoryDifficulty.Add("Accuracy", accValue); + categoryDifficulty.Add("Scaled Score", scaledScore); + categoryDifficulty.Add("Great Hit Window", Attributes.GreatHitWindow); + categoryDifficulty.Add("Max Combo", Attributes.MaxCombo); } return totalValue; } - private double computeStrainValue() + private double computeDifficultyValue() { - // Obtain strain difficulty - double strainValue = Math.Pow(5 * Math.Max(1, Attributes.StarRating / 0.2) - 4.0, 2.2) / 135.0; + // Obtain difficulty + double difficultyValue = Math.Pow(5 * Math.Max(1, Attributes.StarRating / 0.2) - 4.0, 2.2) / 135.0; // Longer maps are worth more - strainValue *= 1.0 + 0.1 * Math.Min(1.0, totalHits / 1500.0); + difficultyValue *= 1.0 + 0.1 * Math.Min(1.0, totalHits / 1500.0); if (scaledScore <= 500000) - strainValue = 0; + difficultyValue = 0; else if (scaledScore <= 600000) - strainValue *= (scaledScore - 500000) / 100000 * 0.3; + difficultyValue *= (scaledScore - 500000) / 100000 * 0.3; else if (scaledScore <= 700000) - strainValue *= 0.3 + (scaledScore - 600000) / 100000 * 0.25; + difficultyValue *= 0.3 + (scaledScore - 600000) / 100000 * 0.25; else if (scaledScore <= 800000) - strainValue *= 0.55 + (scaledScore - 700000) / 100000 * 0.20; + difficultyValue *= 0.55 + (scaledScore - 700000) / 100000 * 0.20; else if (scaledScore <= 900000) - strainValue *= 0.75 + (scaledScore - 800000) / 100000 * 0.15; + difficultyValue *= 0.75 + (scaledScore - 800000) / 100000 * 0.15; else - strainValue *= 0.90 + (scaledScore - 900000) / 100000 * 0.1; + difficultyValue *= 0.90 + (scaledScore - 900000) / 100000 * 0.1; - return strainValue; + return difficultyValue; } - private double computeAccuracyValue(double strainValue) + private double computeAccuracyValue(double difficultyValue) { if (Attributes.GreatHitWindow <= 0) return 0; @@ -110,7 +113,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty // Lots of arbitrary values from testing. // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution double accuracyValue = Math.Max(0.0, 0.2 - (Attributes.GreatHitWindow - 34) * 0.006667) - * strainValue + * difficultyValue * Math.Pow(Math.Max(0.0, scaledScore - 960000) / 40000, 1.1); // Bonus for many hitcircles - it's harder to keep good accuracy up for longer diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 4b2e54da17..128ff772fd 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -12,14 +12,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty { public class OsuDifficultyAttributes : DifficultyAttributes { - [JsonProperty("aim_strain")] - public double AimStrain { get; set; } + [JsonProperty("aim_difficulty")] + public double AimDifficulty { get; set; } - [JsonProperty("speed_strain")] - public double SpeedStrain { get; set; } + [JsonProperty("speed_difficulty")] + public double SpeedDifficulty { get; set; } - [JsonProperty("flashlight_rating")] - public double FlashlightRating { get; set; } + [JsonProperty("flashlight_difficulty")] + public double FlashlightDifficulty { get; set; } [JsonProperty("slider_factor")] public double SliderFactor { get; set; } @@ -43,15 +43,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty foreach (var v in base.ToDatabaseAttributes()) yield return v; - yield return (ATTRIB_ID_AIM, AimStrain); - yield return (ATTRIB_ID_SPEED, SpeedStrain); + yield return (ATTRIB_ID_AIM, AimDifficulty); + yield return (ATTRIB_ID_SPEED, SpeedDifficulty); yield return (ATTRIB_ID_OVERALL_DIFFICULTY, OverallDifficulty); yield return (ATTRIB_ID_APPROACH_RATE, ApproachRate); yield return (ATTRIB_ID_MAX_COMBO, MaxCombo); - yield return (ATTRIB_ID_STRAIN, StarRating); + yield return (ATTRIB_ID_DIFFICULTY, StarRating); if (ShouldSerializeFlashlightRating()) - yield return (ATTRIB_ID_FLASHLIGHT, FlashlightRating); + yield return (ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty); yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor); } @@ -60,13 +60,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty { base.FromDatabaseAttributes(values); - AimStrain = values[ATTRIB_ID_AIM]; - SpeedStrain = values[ATTRIB_ID_SPEED]; + AimDifficulty = values[ATTRIB_ID_AIM]; + SpeedDifficulty = values[ATTRIB_ID_SPEED]; OverallDifficulty = values[ATTRIB_ID_OVERALL_DIFFICULTY]; ApproachRate = values[ATTRIB_ID_APPROACH_RATE]; MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO]; - StarRating = values[ATTRIB_ID_STRAIN]; - FlashlightRating = values.GetValueOrDefault(ATTRIB_ID_FLASHLIGHT); + StarRating = values[ATTRIB_ID_DIFFICULTY]; + FlashlightDifficulty = values.GetValueOrDefault(ATTRIB_ID_FLASHLIGHT); SliderFactor = values[ATTRIB_ID_SLIDER_FACTOR]; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index ed42f333c0..c5b1baaad1 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -74,9 +74,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty { StarRating = starRating, Mods = mods, - AimStrain = aimRating, - SpeedStrain = speedRating, - FlashlightRating = flashlightRating, + AimDifficulty = aimRating, + SpeedDifficulty = speedRating, + FlashlightDifficulty = flashlightRating, SliderFactor = sliderFactor, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, OverallDifficulty = (80 - hitWindowGreat) / 6, diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 8d45c7a8cc..96c92bdb0d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { } - public override double Calculate(Dictionary categoryRatings = null) + public override double Calculate(Dictionary categoryDifficulty = null) { mods = Score.Mods; accuracy = Score.Accuracy; @@ -72,15 +72,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty Math.Pow(flashlightValue, 1.1), 1.0 / 1.1 ) * multiplier; - if (categoryRatings != null) + if (categoryDifficulty != null) { - categoryRatings.Add("Aim", aimValue); - categoryRatings.Add("Speed", speedValue); - categoryRatings.Add("Accuracy", accuracyValue); - categoryRatings.Add("Flashlight", flashlightValue); - categoryRatings.Add("OD", Attributes.OverallDifficulty); - categoryRatings.Add("AR", Attributes.ApproachRate); - categoryRatings.Add("Max Combo", Attributes.MaxCombo); + categoryDifficulty.Add("Aim", aimValue); + categoryDifficulty.Add("Speed", speedValue); + categoryDifficulty.Add("Accuracy", accuracyValue); + categoryDifficulty.Add("Flashlight", flashlightValue); + categoryDifficulty.Add("OD", Attributes.OverallDifficulty); + categoryDifficulty.Add("AR", Attributes.ApproachRate); + categoryDifficulty.Add("Max Combo", Attributes.MaxCombo); + categoryDifficulty.Add("Effective Miss Count", effectiveMissCount); } return totalValue; @@ -88,7 +89,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty private double computeAimValue() { - double rawAim = Attributes.AimStrain; + double rawAim = Attributes.AimDifficulty; if (mods.Any(m => m is OsuModTouchDevice)) rawAim = Math.Pow(rawAim, 0.8); @@ -144,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty private double computeSpeedValue() { - double speedValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.SpeedStrain / 0.0675) - 4.0, 3.0) / 100000.0; + double speedValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.SpeedDifficulty / 0.0675) - 4.0, 3.0) / 100000.0; // Longer maps are worth more. double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) + @@ -227,7 +228,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (!mods.Any(h => h is OsuModFlashlight)) return 0.0; - double rawFlashlight = Attributes.FlashlightRating; + double rawFlashlight = Attributes.FlashlightDifficulty; if (mods.Any(m => m is OsuModTouchDevice)) rawFlashlight = Math.Pow(rawFlashlight, 0.8); diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs index b2b5d056c3..31f5a6f570 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs @@ -9,14 +9,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { public class TaikoDifficultyAttributes : DifficultyAttributes { - [JsonProperty("stamina_strain")] - public double StaminaStrain { get; set; } + [JsonProperty("stamina_difficulty")] + public double StaminaDifficulty { get; set; } - [JsonProperty("rhythm_strain")] - public double RhythmStrain { get; set; } + [JsonProperty("rhythm_difficulty")] + public double RhythmDifficulty { get; set; } - [JsonProperty("colour_strain")] - public double ColourStrain { get; set; } + [JsonProperty("colour_difficulty")] + public double ColourDifficulty { get; set; } [JsonProperty("approach_rate")] public double ApproachRate { get; set; } @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty yield return v; yield return (ATTRIB_ID_MAX_COMBO, MaxCombo); - yield return (ATTRIB_ID_STRAIN, StarRating); + yield return (ATTRIB_ID_DIFFICULTY, StarRating); yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow); } @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty base.FromDatabaseAttributes(values); MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO]; - StarRating = values[ATTRIB_ID_STRAIN]; + StarRating = values[ATTRIB_ID_DIFFICULTY]; GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW]; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index e84bee3d28..6afdef3f3c 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -91,9 +91,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { StarRating = starRating, Mods = mods, - StaminaStrain = staminaRating, - RhythmStrain = rhythmRating, - ColourStrain = colourRating, + StaminaDifficulty = staminaRating, + RhythmDifficulty = rhythmRating, + ColourDifficulty = colourRating, GreatHitWindow = hitWindows.WindowFor(HitResult.Great) / clockRate, MaxCombo = beatmap.HitObjects.Count(h => h is Hit), }; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index 90dd733dfd..d287998f3b 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -44,43 +44,46 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (mods.Any(m => m is ModHidden)) multiplier *= 1.10; - double strainValue = computeStrainValue(); + double difficultyValue = computeDifficultyValue(); double accuracyValue = computeAccuracyValue(); double totalValue = Math.Pow( - Math.Pow(strainValue, 1.1) + + Math.Pow(difficultyValue, 1.1) + Math.Pow(accuracyValue, 1.1), 1.0 / 1.1 ) * multiplier; if (categoryDifficulty != null) { - categoryDifficulty["Strain"] = strainValue; - categoryDifficulty["Accuracy"] = accuracyValue; + categoryDifficulty.Add("Difficulty", difficultyValue); + categoryDifficulty.Add("Accuracy", accuracyValue); + categoryDifficulty.Add("AR", Attributes.ApproachRate); + categoryDifficulty.Add("Great Hit Window", Attributes.GreatHitWindow); + categoryDifficulty.Add("Max Combo", Attributes.MaxCombo); } return totalValue; } - private double computeStrainValue() + private double computeDifficultyValue() { - double strainValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0075) - 4.0, 2.0) / 100000.0; + double difficultyValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0075) - 4.0, 2.0) / 100000.0; // Longer maps are worth more double lengthBonus = 1 + 0.1 * Math.Min(1.0, totalHits / 1500.0); - strainValue *= lengthBonus; + difficultyValue *= lengthBonus; // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available - strainValue *= Math.Pow(0.985, countMiss); + difficultyValue *= Math.Pow(0.985, countMiss); if (mods.Any(m => m is ModHidden)) - strainValue *= 1.025; + difficultyValue *= 1.025; if (mods.Any(m => m is ModFlashlight)) // Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps. - strainValue *= 1.05 * lengthBonus; + difficultyValue *= 1.05 * lengthBonus; // Scale the speed value with accuracy _slightly_ - return strainValue * Score.Accuracy; + return difficultyValue * Score.Accuracy; } private double computeAccuracyValue() diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index 47736ee033..991b567f57 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Difficulty protected const int ATTRIB_ID_OVERALL_DIFFICULTY = 5; protected const int ATTRIB_ID_APPROACH_RATE = 7; protected const int ATTRIB_ID_MAX_COMBO = 9; - protected const int ATTRIB_ID_STRAIN = 11; + protected const int ATTRIB_ID_DIFFICULTY = 11; protected const int ATTRIB_ID_GREAT_HIT_WINDOW = 13; protected const int ATTRIB_ID_SCORE_MULTIPLIER = 15; protected const int ATTRIB_ID_FLASHLIGHT = 17;