From 205d95e8c62ce26cf6c6ebe63c01edb7e9b9b9f9 Mon Sep 17 00:00:00 2001 From: StanR Date: Wed, 13 Oct 2021 20:04:34 +0300 Subject: [PATCH 1/3] Approximate amount of effective misses using combo --- .../Difficulty/OsuDifficultyAttributes.cs | 1 + .../Difficulty/OsuDifficultyCalculator.cs | 2 + .../Difficulty/OsuPerformanceCalculator.cs | 38 ++++++++++++++----- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index bd4c0f2ad52b..a08fe3b7c564 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -14,6 +14,7 @@ public class OsuDifficultyAttributes : DifficultyAttributes public double OverallDifficulty { get; set; } public double DrainRate { get; set; } public int HitCircleCount { get; set; } + public int SliderCount { get; set; } public int SpinnerCount { get; set; } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 790aa0eb7d72..b0a764dc4d5d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -64,6 +64,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1); int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle); + int sliderCount = beatmap.HitObjects.Count(h => h is Slider); int spinnerCount = beatmap.HitObjects.Count(h => h is Spinner); return new OsuDifficultyAttributes @@ -78,6 +79,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat DrainRate = drainRate, MaxCombo = maxCombo, HitCircleCount = hitCirclesCount, + SliderCount = sliderCount, SpinnerCount = spinnerCount, Skills = skills }; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 4e4dbc02a11c..d0b5e877b511 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -25,6 +25,8 @@ public class OsuPerformanceCalculator : PerformanceCalculator private int countMeh; private int countMiss; + private int effectiveMissCount; + public OsuPerformanceCalculator(Ruleset ruleset, DifficultyAttributes attributes, ScoreInfo score) : base(ruleset, attributes, score) { @@ -39,19 +41,20 @@ public override double Calculate(Dictionary categoryRatings = nu countOk = Score.Statistics.GetValueOrDefault(HitResult.Ok); countMeh = Score.Statistics.GetValueOrDefault(HitResult.Meh); countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss); + effectiveMissCount = calculateEffectiveMissCount(); double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. // Custom multipliers for NoFail and SpunOut. if (mods.Any(m => m is OsuModNoFail)) - multiplier *= Math.Max(0.90, 1.0 - 0.02 * countMiss); + multiplier *= Math.Max(0.90, 1.0 - 0.02 * effectiveMissCount); if (mods.Any(m => m is OsuModSpunOut)) multiplier *= 1.0 - Math.Pow((double)Attributes.SpinnerCount / totalHits, 0.85); if (mods.Any(h => h is OsuModRelax)) { - countMiss += countOk + countMeh; + effectiveMissCount += countOk + countMeh; multiplier *= 0.6; } @@ -97,8 +100,8 @@ private double computeAimValue() aimValue *= lengthBonus; // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. - if (countMiss > 0) - aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), countMiss); + if (effectiveMissCount > 0) + aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), effectiveMissCount); // Combo scaling. if (Attributes.MaxCombo > 0) @@ -115,7 +118,7 @@ private double computeAimValue() double approachRateBonus = 1.0 + (0.03 + 0.37 * approachRateTotalHitsFactor) * approachRateFactor; if (mods.Any(m => m is OsuModBlinds)) - aimValue *= 1.3 + (totalHits * (0.0016 / (1 + 2 * countMiss)) * Math.Pow(accuracy, 16)) * (1 - 0.003 * Attributes.DrainRate * Attributes.DrainRate); + aimValue *= 1.3 + (totalHits * (0.0016 / (1 + 2 * effectiveMissCount)) * Math.Pow(accuracy, 16)) * (1 - 0.003 * Attributes.DrainRate * Attributes.DrainRate); else if (mods.Any(h => h is OsuModHidden)) { // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. @@ -142,8 +145,8 @@ private double computeSpeedValue() speedValue *= lengthBonus; // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. - if (countMiss > 0) - speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); + if (effectiveMissCount > 0) + speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), Math.Pow(effectiveMissCount, .875)); // Combo scaling. if (Attributes.MaxCombo > 0) @@ -231,8 +234,8 @@ private double computeFlashlightValue() flashlightValue *= 1.3; // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. - if (countMiss > 0) - flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); + if (effectiveMissCount > 0) + flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), Math.Pow(effectiveMissCount, .875)); // Combo scaling. if (Attributes.MaxCombo > 0) @@ -250,6 +253,23 @@ private double computeFlashlightValue() return flashlightValue; } + private int calculateEffectiveMissCount() + { + // guess the number of misses + slider breaks from combo + double comboBasedMissCount = 0.0; + + if (Attributes.SliderCount > 0) + { + double fullComboThreshold = Attributes.MaxCombo - 0.1 * Attributes.SliderCount; + if (scoreMaxCombo < fullComboThreshold) + comboBasedMissCount = fullComboThreshold / scoreMaxCombo; + else + comboBasedMissCount = Math.Pow((Attributes.MaxCombo - scoreMaxCombo) / (0.1 * Attributes.SliderCount), 3); + } + + return Math.Max(countMiss, (int)Math.Floor(comboBasedMissCount)); + } + private int totalHits => countGreat + countOk + countMeh + countMiss; private int totalSuccessfulHits => countGreat + countOk + countMeh; } From c8d99e68a59a9f08f5daa423ba004bb072897b98 Mon Sep 17 00:00:00 2001 From: StanR Date: Fri, 15 Oct 2021 16:51:05 +0300 Subject: [PATCH 2/3] Remove calculation for scores with combo above threshold, avoid division by zero --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index d0b5e877b511..3895389f61ab 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -262,9 +262,7 @@ private int calculateEffectiveMissCount() { double fullComboThreshold = Attributes.MaxCombo - 0.1 * Attributes.SliderCount; if (scoreMaxCombo < fullComboThreshold) - comboBasedMissCount = fullComboThreshold / scoreMaxCombo; - else - comboBasedMissCount = Math.Pow((Attributes.MaxCombo - scoreMaxCombo) / (0.1 * Attributes.SliderCount), 3); + comboBasedMissCount = fullComboThreshold / Math.Max(1.0, scoreMaxCombo); } return Math.Max(countMiss, (int)Math.Floor(comboBasedMissCount)); From ccaac977947174af7d8de690f90f5977b230242d Mon Sep 17 00:00:00 2001 From: StanR Date: Sat, 16 Oct 2021 14:50:15 +0300 Subject: [PATCH 3/3] Clamp comboBasedMissCount --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 3895389f61ab..4bca87204ad1 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -265,6 +265,9 @@ private int calculateEffectiveMissCount() comboBasedMissCount = fullComboThreshold / Math.Max(1.0, scoreMaxCombo); } + // we're clamping misscount because since its derived from combo it can be higher than total hits and that breaks some calculations + comboBasedMissCount = Math.Min(comboBasedMissCount, totalHits); + return Math.Max(countMiss, (int)Math.Floor(comboBasedMissCount)); }