-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove estimations where score data is available for osu! difficulty calculations #27691
Changes from all commits
941c048
4db6f28
3dafdc0
8408455
12afa8d
eb30b4a
c9e3c10
b0d20e6
6fe478c
4f5f0e5
58bc184
c24f99e
2dd4903
dd17c89
ca24601
77814ec
759a826
4a7b813
d1dcac0
4fe55d4
1f55c14
6c9e906
8dea601
44c9425
3d7f4ae
b921424
3b517e0
3ac6a9f
29b1697
88af578
5192599
6bcfed8
1337b7e
6d4cb60
31e0853
e31e10d
3778246
5907c2a
98800fe
bcb9970
acf282d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -25,6 +25,19 @@ public class OsuPerformanceCalculator : PerformanceCalculator | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private int countMeh; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private int countMiss; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/// <summary> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/// Missed slider ticks that includes missed reverse arrows. Will only be correct on non-classic scores | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/// </summary> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private int countSliderTickMiss; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/// <summary> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/// Amount of missed slider tails that don't break combo. Will only be correct on non-classic scores | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/// </summary> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private int countSliderEndsDropped; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since slider-related code uses the term "tail" (and the hit result does too), I'd suggest renaming this to |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/// <summary> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/// Estimated total amount of combo breaks | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/// </summary> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private double effectiveMissCount; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public OsuPerformanceCalculator() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -44,7 +57,36 @@ protected override PerformanceAttributes CreatePerformanceAttributes(ScoreInfo s | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
effectiveMissCount = calculateEffectiveMissCount(osuAttributes); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
countSliderEndsDropped = osuAttributes.SliderCount - score.Statistics.GetValueOrDefault(HitResult.SliderTailHit); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
countSliderTickMiss = score.Statistics.GetValueOrDefault(HitResult.LargeTickMiss); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (osuAttributes.SliderCount > 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (usingClassicSliderAccuracy) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Consider that full combo is maximum combo minus dropped slider tails since they don't contribute to combo but also don't break it | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// In classic scores we can't know the amount of dropped sliders so we estimate to 10% of all sliders on the map | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
double fullComboThreshold = attributes.MaxCombo - 0.1 * osuAttributes.SliderCount; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (scoreMaxCombo < fullComboThreshold) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
effectiveMissCount = fullComboThreshold / Math.Max(1.0, scoreMaxCombo); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// In classic scores there can't be more misses than a sum of all non-perfect judgements | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
effectiveMissCount = Math.Min(effectiveMissCount, totalImperfectHits); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
double fullComboThreshold = attributes.MaxCombo - countSliderEndsDropped; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+78
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
A comment to stay consistent with the other if-branch would be nice There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Repeating the same comment is redundant There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fair just wanted to mention it incase it is desired |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (scoreMaxCombo < fullComboThreshold) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
effectiveMissCount = fullComboThreshold / Math.Max(1.0, scoreMaxCombo); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Combine regular misses with tick misses since tick misses break combo as well | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
effectiveMissCount = Math.Min(effectiveMissCount, countSliderTickMiss + countMiss); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
effectiveMissCount = Math.Max(countMiss, effectiveMissCount); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
double multiplier = PERFORMANCE_BASE_MULTIPLIER; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -124,8 +166,22 @@ private double computeAimValue(ScoreInfo score, OsuDifficultyAttributes attribut | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (attributes.SliderCount > 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
double estimateSliderEndsDropped = Math.Clamp(Math.Min(countOk + countMeh + countMiss, attributes.MaxCombo - scoreMaxCombo), 0, estimateDifficultSliders); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
double sliderNerfFactor = (1 - attributes.SliderFactor) * Math.Pow(1 - estimateSliderEndsDropped / estimateDifficultSliders, 3) + attributes.SliderFactor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
double estimateImproperlyFollowedDifficultSliders; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (usingClassicSliderAccuracy) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// When the score is considered classic (regardless if it was made on old client or not) we consider all missing combo to be dropped difficult sliders | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
int maximumPossibleDroppedSliders = totalImperfectHits; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
estimateImproperlyFollowedDifficultSliders = Math.Clamp(Math.Min(maximumPossibleDroppedSliders, attributes.MaxCombo - scoreMaxCombo), 0, estimateDifficultSliders); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// We add tick misses here since they too mean that the player didn't follow the slider properly | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// We however aren't adding misses here because missing slider heads has a harsh penalty by itself and doesn't mean that the rest of the slider wasn't followed properly | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
estimateImproperlyFollowedDifficultSliders = Math.Min(countSliderEndsDropped + countSliderTickMiss, estimateDifficultSliders); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
double sliderNerfFactor = (1 - attributes.SliderFactor) * Math.Pow(1 - estimateImproperlyFollowedDifficultSliders / estimateDifficultSliders, 3) + attributes.SliderFactor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+169
to
+184
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The variable name is quite long and since it's not always estimated so it doesn't necessarily have a valuable meaning besides making the code more straining to read. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer longer variable names that encapsulate what it actually is instead of hiding the fact that it can be estimated (and will be for billions of scores) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm idk, I feel like there isn't much difference between "estimated" when it can or cannot be vs. not saying it when it can or cannot be |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
aimValue *= sliderNerfFactor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -247,29 +303,12 @@ private double computeFlashlightValue(ScoreInfo score, OsuDifficultyAttributes a | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return flashlightValue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private double calculateEffectiveMissCount(OsuDifficultyAttributes attributes) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// 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 / Math.Max(1.0, scoreMaxCombo); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Clamp miss count to maximum amount of possible breaks | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
comboBasedMissCount = Math.Min(comboBasedMissCount, countOk + countMeh + countMiss); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return Math.Max(countMiss, comboBasedMissCount); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Miss penalty assumes that a player will miss on the hardest parts of a map, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// so we use the amount of relatively difficult sections to adjust miss penalty | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// to make it more punishing on maps with lower amount of hard sections. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private double calculateMissPenalty(double missCount, double difficultStrainCount) => 0.96 / ((missCount / (4 * Math.Pow(Math.Log(difficultStrainCount), 0.94))) + 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private int totalImperfectHits => countOk + countMeh + countMiss; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like renaming this to
countLargeTickMiss
makes it alot clearer what it is because despite the comment reading it anywhere else in code could put up confusion... I don't see why it shouldn't be named 1:1 after the hit result it represents.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"LargeTick" does not represent anything on its own unless you're aware of what it actually contains
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like it's still a more meaningful term since it matches the hit result and also the "Large Tick Misses" field I added to osu-tools GUI and soon to CLI.