From 42aa3724c3bcadefb9ebbeb8eb04e9103ab99f9a Mon Sep 17 00:00:00 2001 From: Fina Date: Thu, 21 Mar 2024 16:27:47 -0700 Subject: [PATCH 1/3] Treat misses as 100s Makes sliderbreaks and misses equal --- .../Difficulty/OsuPerformanceCalculator.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index b31f4ff519..c6395b24e6 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -33,13 +33,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty { var osuAttributes = (OsuDifficultyAttributes)attributes; - accuracy = score.Accuracy; scoreMaxCombo = score.MaxCombo; countGreat = score.Statistics.GetValueOrDefault(HitResult.Great); countOk = score.Statistics.GetValueOrDefault(HitResult.Ok); countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh); countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss); effectiveMissCount = calculateEffectiveMissCount(osuAttributes); + accuracy = calculateEffectiveAccuracy(countGreat, countOk, countMeh, countMiss, totalHits); double multiplier = PERFORMANCE_BASE_MULTIPLIER; @@ -173,7 +173,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty double relevantCountGreat = Math.Max(0, countGreat - relevantTotalDiff); double relevantCountOk = Math.Max(0, countOk - Math.Max(0, relevantTotalDiff - countGreat)); double relevantCountMeh = Math.Max(0, countMeh - Math.Max(0, relevantTotalDiff - countGreat - countOk)); - double relevantAccuracy = attributes.SpeedNoteCount == 0 ? 0 : (relevantCountGreat * 6.0 + relevantCountOk * 2.0 + relevantCountMeh) / (attributes.SpeedNoteCount * 6.0); + double relevantCountMiss = Math.Max(0, countMiss - Math.Max(0, relevantTotalDiff - countGreat - countOk - countMeh)); + double relevantAccuracy = attributes.SpeedNoteCount == 0 ? 0 : calculateEffectiveAccuracy(relevantCountGreat, relevantCountOk, relevantCountMeh, relevantCountMiss, attributes.SpeedNoteCount); // Scale the speed value with accuracy and OD. speedValue *= (0.95 + Math.Pow(attributes.OverallDifficulty, 2) / 750) * Math.Pow((accuracy + relevantAccuracy) / 2.0, (14.5 - Math.Max(attributes.OverallDifficulty, 8)) / 2); @@ -194,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty int amountHitObjectsWithAccuracy = attributes.HitCircleCount; if (amountHitObjectsWithAccuracy > 0) - betterAccuracyPercentage = ((countGreat - (totalHits - amountHitObjectsWithAccuracy)) * 6 + countOk * 2 + countMeh) / (double)(amountHitObjectsWithAccuracy * 6); + betterAccuracyPercentage = calculateEffectiveAccuracy(countGreat - (totalHits - amountHitObjectsWithAccuracy), countOk, countMeh, countMiss, amountHitObjectsWithAccuracy); else betterAccuracyPercentage = 0; @@ -263,6 +264,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty return Math.Max(countMiss, comboBasedMissCount); } + private double calculateEffectiveAccuracy(double countGreat, double countOk, double countMeh, double countMiss, double totalHits) => (countGreat * 6 + countOk * 2 + countMeh + countMiss * 2) / (double)(totalHits * 6); private double getComboScalingFactor(OsuDifficultyAttributes attributes) => attributes.MaxCombo <= 0 ? 1.0 : Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(attributes.MaxCombo, 0.8), 1.0); private int totalHits => countGreat + countOk + countMeh + countMiss; From 65e125fff68de722e1d3de31c51d4e665f8b0c4d Mon Sep 17 00:00:00 2001 From: Fina Date: Thu, 21 Mar 2024 20:57:33 -0700 Subject: [PATCH 2/3] Use betterAccuracyPercentage Everywhere Someone brought this up during discussion, and from what I can tell, there's no real reason why this wasn't done either. All this does is use the number of circles instead of the number of objects for accuracy calculations, since circles are the only objects that check for accuracy. Could also be merged with #27063 to use circles + sliders when sliders check for accuracy (aka when classic mod is disabled). Currently the code for that is included but commented out. --- .../Difficulty/OsuPerformanceCalculator.cs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index c6395b24e6..532c9becae 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -23,6 +23,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty private int countMiss; private double effectiveMissCount; + private int amountHitObjectsWithAccuracy; public OsuPerformanceCalculator() : base(new OsuRuleset()) @@ -39,7 +40,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh); countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss); effectiveMissCount = calculateEffectiveMissCount(osuAttributes); - accuracy = calculateEffectiveAccuracy(countGreat, countOk, countMeh, countMiss, totalHits); + + amountHitObjectsWithAccuracy = osuAttributes.HitCircleCount; + // Potential merge with #27063 + //if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value)) + // amountHitObjectsWithAccuracy += osuAttributes.SliderCount; + + if (amountHitObjectsWithAccuracy > 0) + accuracy = calculateEffectiveAccuracy(countGreat - (totalHits - amountHitObjectsWithAccuracy), countOk, countMeh, countMiss, amountHitObjectsWithAccuracy); + else + accuracy = 0; double multiplier = PERFORMANCE_BASE_MULTIPLIER; @@ -190,22 +200,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (score.Mods.Any(h => h is OsuModRelax)) return 0.0; - // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window. - double betterAccuracyPercentage; - int amountHitObjectsWithAccuracy = attributes.HitCircleCount; - - if (amountHitObjectsWithAccuracy > 0) - betterAccuracyPercentage = calculateEffectiveAccuracy(countGreat - (totalHits - amountHitObjectsWithAccuracy), countOk, countMeh, countMiss, amountHitObjectsWithAccuracy); - else - betterAccuracyPercentage = 0; - // It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points. - if (betterAccuracyPercentage < 0) - betterAccuracyPercentage = 0; + if (accuracy < 0) + accuracy = 0; // Lots of arbitrary values from testing. // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution. - double accuracyValue = Math.Pow(1.52163, attributes.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83; + double accuracyValue = Math.Pow(1.52163, attributes.OverallDifficulty) * Math.Pow(accuracy, 24) * 2.83; // Bonus for many hitcircles - it's harder to keep good accuracy up for longer. accuracyValue *= Math.Min(1.15, Math.Pow(amountHitObjectsWithAccuracy / 1000.0, 0.3)); From 936a6e91ec8ef903cdcbe67c172b85bc6e3c167b Mon Sep 17 00:00:00 2001 From: Fina Date: Fri, 22 Mar 2024 17:13:31 -0700 Subject: [PATCH 3/3] Revert "Use betterAccuracyPercentage Everywhere" This reverts commit 65e125fff68de722e1d3de31c51d4e665f8b0c4d. --- .../Difficulty/OsuPerformanceCalculator.cs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 532c9becae..c6395b24e6 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -23,7 +23,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty private int countMiss; private double effectiveMissCount; - private int amountHitObjectsWithAccuracy; public OsuPerformanceCalculator() : base(new OsuRuleset()) @@ -40,16 +39,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh); countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss); effectiveMissCount = calculateEffectiveMissCount(osuAttributes); - - amountHitObjectsWithAccuracy = osuAttributes.HitCircleCount; - // Potential merge with #27063 - //if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value)) - // amountHitObjectsWithAccuracy += osuAttributes.SliderCount; - - if (amountHitObjectsWithAccuracy > 0) - accuracy = calculateEffectiveAccuracy(countGreat - (totalHits - amountHitObjectsWithAccuracy), countOk, countMeh, countMiss, amountHitObjectsWithAccuracy); - else - accuracy = 0; + accuracy = calculateEffectiveAccuracy(countGreat, countOk, countMeh, countMiss, totalHits); double multiplier = PERFORMANCE_BASE_MULTIPLIER; @@ -200,13 +190,22 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (score.Mods.Any(h => h is OsuModRelax)) return 0.0; + // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window. + double betterAccuracyPercentage; + int amountHitObjectsWithAccuracy = attributes.HitCircleCount; + + if (amountHitObjectsWithAccuracy > 0) + betterAccuracyPercentage = calculateEffectiveAccuracy(countGreat - (totalHits - amountHitObjectsWithAccuracy), countOk, countMeh, countMiss, amountHitObjectsWithAccuracy); + else + betterAccuracyPercentage = 0; + // It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points. - if (accuracy < 0) - accuracy = 0; + if (betterAccuracyPercentage < 0) + betterAccuracyPercentage = 0; // Lots of arbitrary values from testing. // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution. - double accuracyValue = Math.Pow(1.52163, attributes.OverallDifficulty) * Math.Pow(accuracy, 24) * 2.83; + double accuracyValue = Math.Pow(1.52163, attributes.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83; // Bonus for many hitcircles - it's harder to keep good accuracy up for longer. accuracyValue *= Math.Min(1.15, Math.Pow(amountHitObjectsWithAccuracy / 1000.0, 0.3));