-
-
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
Conversation
Score data for non-CL scores includes sliderends dropped, meaning no need to estimate. CL scores are still estimated.
…or classic" This reverts commit 941c048.
No need to estimate misses for non-CL scores.
Miss count fix
Use actual sliderends dropped instead of estimating
Very much open to discussion on if these should be weighed differently
I am not sure the "available" score data in question can be used. We've previously expressed worry that this was going to spiral into two separate implementations of pp for classic and non-classic scores. |
My bad, I was not aware of these conversations. Could you drop a link to them so I can read through them? |
#21938 would be a good starting point I guess |
Stan's decision of splitting the calculator into 2 parts for classic and non-classic is a direction I personally dislike, and the way Fina is doing it here is the way I would approach it myself. I don't see any harm in using the new metrics, and PP spiraling into 2 calculators should only happen if per note judgements ever become available, and prove to give much better results than without them. These new metrics are easily estimable, and the existing estimations can just be made harsher if they prove to give an advantage to stable scores. |
Why is this only using additional data if slideracc enabled? |
The implications of a change like this are massive. If I set scores with sliderbreaks, I will only ever be punished more on lazer than I would be if I played on stable. I play on lazer as my main client, but a change like this on its own would make me consider moving back to stable because I don't want to play with something like this that is just a clear disadvantage. You could try tuning the estimations for stable scores to be more harsh, but I doubt people would be happy with that and I'm honestly not sure if that's even possible. The complexity to balance this kind of change is so high that I don't think it's worth the trouble to begin with. |
@Flamiii you’re already at an objective disadvantage if you play on lazer. This arguably helps you since it means miss estimations can no longer assume you may have sliderbroke where you didn’t. Any non-cl 1 miss plays where the break was in the middle are buffed. Same for plays on hard slider maps where you broke but didn’t drop any sliderends. |
If by "objective disadvantage" you mean slideracc, I play on lazer because I'm certain that will be taken into account in the future (#27063). The scenario I'm thinking of which happens quite a bit is the one where a player misses near the end of a map but also sliderbreaks 2 times afterwards. In stable, this play is counted as a 1 miss and the sliderbreaks only negatively impact your accuracy. With these changes in lazer, this play would be treated as if it was a 3 miss. As far as I know, there really isn't a way to adjust stable's estimation to make this scenario balanced because the relevant information just isn't there. There are some situations where you're punished a bit less, but the scenario I mentioned is extremely common and you would be punished way more for playing on lazer with these changes. Edit: It's also worth noting that using HitResults.LargeTickMiss to increase the effective miss count means that missing buzz sliders or any fast sliders with many sliderticks will be incredibly punishing. For example, misaiming a buzz slider with 5 repeats and missing it entirely would increase the effective miss count by 6, which would basically kill any score. |
There actually is a way, if you take a look at the function in question, we could, for example, lower fullComboThreshold to make it assume there were more misses than before, and if you look at the changes in this pr, this function is only used if CL with slideracc is on.
This is a valid point, however, I can assure you that future work will be done to account for this. |
I see. As long as these changes are balanced properly, I have no issues with them. |
shoot I hit the wrong button |
Ok but again this is already a thing. Sliderbreaks by missing the slider head are already counted as misses in lazer, so this scenario can already happen without any pp changes.
I know, I'm more apprehensive about that part and wanted to spark discussion for that specifically. The crux of the matter is, some scores will be punished by being unable to slip through the cracks. That's inevitable. The gain of scores no longer being undeservingly punished from estimations counteracts this and, at least in my personal opinion, is a fair trade off. |
As per suggestion by givikap, I was not aware that non-legacy cl scores stored this data
…into estimation-removal
After letting the comments @Flamiii left brew for a while, I realized they were very much right about the buzz slider thing. As such, I've implemented a quick and dirty untested fix that will hopefully have zero unintended side-effects :) I don't see this as a permanent or final solution yet. There's definitely some potential issues/inaccuracies that could arise with maps like Notch Hell or IOException's Black Rover, but afaik this implementation would not cause any issues that stable doesn't already have.
!diffcalc |
Updated the original post to hopefully make the goals of the PR more clear |
@smoogipoo multiple people have told me that a unit test is no longer required since I've cleaned up and simplified everything, do you still want me to write one? |
slightly miffed by the lack of build errors but oh well
This is honestly harder to read now than before, now that it has 4 separate places individually dealing with "usingClassicSliderAccuracy". |
I honestly think it's fine, at least to me the |
{ | ||
double fullComboThreshold = attributes.MaxCombo - countSliderEndsDropped; |
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.
{ | |
double fullComboThreshold = attributes.MaxCombo - countSliderEndsDropped; | |
{ | |
// Consider that full combo is maximum combo minus dropped slider tails since they don't contribute to combo but also don't break it | |
double fullComboThreshold = attributes.MaxCombo - countSliderEndsDropped; |
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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
fair just wanted to mention it incase it is desired
/// <summary> | ||
/// Missed slider ticks that includes missed reverse arrows. Will only be correct on non-classic scores | ||
/// </summary> | ||
private int countSliderTickMiss; |
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.
/// <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 comment
The 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 countSliderTailMiss
or countSliderTailsDropped
. I'd prefer to consider "miss" the opposite of hit even for slider tails but calling them "dropped" is arguable
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; |
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.
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; | |
double improperlyFollowedDifficultSliders; | |
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; | |
improperlyFollowedDifficultSliders = 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 | |
improperlyFollowedDifficultSliders = Math.Min(countSliderEndsDropped + countSliderTickMiss, estimateDifficultSliders); | |
} | |
double sliderNerfFactor = (1 - attributes.SliderFactor) * Math.Pow(1 - improperlyFollowedDifficultSliders / estimateDifficultSliders, 3) + attributes.SliderFactor; |
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 comment
The 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 comment
The 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
we'll need a new sheet following recent commits, 98800fe will have resulted in value changes even before StanR's refactors |
!diffcalc |
@ppy/osu-pp-committee please check above sheet! |
@smoogipoo all good! |
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.
one more for luck :]
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.
Reads fine to me now
This PR replaces estimations with score data where applicable for Lazer scores. More specifically, it…
For removing slider break estimations:
HitResult.Miss
includes both misses and slider head misses, accounting for two types of combo breaksHitResult.LargeTickMiss
includes dropped reverse arrows and dropped slider ticksFor removing slider end drop estimations:
Notes:
=== original post ===
(contains outdated info)
On non-CL lazer scores, extra slider data means certain estimations are no longer required.
For effective miss count, HitResults.Miss + HitResults.LargeTickMiss is used.
For sliderends dropped, HitResults.SmallTickMiss is used.
To my understanding, LargeTickMiss includes slider ticks and reverse arrows, the two parts that can break your combo, while SmallTickMiss is for sliderends. Please correct me if I am wrong.