-
Notifications
You must be signed in to change notification settings - Fork 94
Fix non-CL acc generation #275
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
base: master
Are you sure you want to change the base?
Changes from 4 commits
c8277b1
a20964a
4058490
0c099e2
b850f53
a2a61ce
4d40ecc
c4fc855
2c2425d
56bf793
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 |
|---|---|---|
|
|
@@ -50,28 +50,57 @@ protected override Dictionary<HitResult, int> GenerateHitResults(IBeatmap beatma | |
| // Use lazer info only if score has sliderhead accuracy | ||
| if (mods.OfType<OsuModClassic>().Any(m => m.NoSliderHeadAccuracy.Value)) | ||
| { | ||
| return generateHitResults(beatmap, Accuracy / 100, Misses, Mehs, Goods, null, null); | ||
| return generateHitResults(beatmap, Accuracy / 100, mods, Misses, Mehs, Goods, null, null); | ||
| } | ||
| else | ||
| { | ||
| return generateHitResults(beatmap, Accuracy / 100, Misses, Mehs, Goods, largeTickMisses, sliderTailMisses); | ||
| return generateHitResults(beatmap, Accuracy / 100, mods, Misses, Mehs, Goods, largeTickMisses, sliderTailMisses); | ||
| } | ||
| } | ||
|
|
||
| private static Dictionary<HitResult, int> generateHitResults(IBeatmap beatmap, double accuracy, int countMiss, int? countMeh, int? countGood, int? countLargeTickMisses, int? countSliderTailMisses) | ||
| private static Dictionary<HitResult, int> generateHitResults(IBeatmap beatmap, double accuracy, Mod[] mods, int countMiss, int? countMeh, int? countGood, int? countLargeTickMisses, int? countSliderTailMisses) | ||
| { | ||
| bool usingClassicSliderAccuracy = mods.OfType<OsuModClassic>().Any(m => m.NoSliderHeadAccuracy.Value); | ||
|
|
||
| int countGreat; | ||
|
|
||
| int totalResultCount = beatmap.HitObjects.Count; | ||
|
|
||
| int countLargeTicks = beatmap.HitObjects.Sum(obj => obj.NestedHitObjects.Count(x => x is SliderTick or SliderRepeat)); | ||
| int countSmallTicks = beatmap.HitObjects.Count(x => x is Slider); | ||
|
|
||
| // Sliderheads are large ticks too if slideracc is disabled | ||
| if (usingClassicSliderAccuracy) | ||
| countLargeTicks += countSmallTicks; | ||
|
|
||
| countLargeTickMisses = Math.Min(countLargeTickMisses ?? 0, countLargeTicks); | ||
| countSliderTailMisses = Math.Min(countSliderTailMisses ?? 0, countSmallTicks); | ||
|
|
||
| if (countMeh != null || countGood != null) | ||
| { | ||
| countGreat = totalResultCount - (countGood ?? 0) - (countMeh ?? 0) - countMiss; | ||
| } | ||
| else | ||
| { | ||
| // Total result count excluding countMiss | ||
| int relevantResultCount = totalResultCount - countMiss; | ||
| // Relevant result count without misses (normal misses and slider-related misses) | ||
| // We need to exclude them from judgement count so total value will be equal to desired after misses are accounted for | ||
| double relevantResultCount; | ||
|
Member
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.
Contributor
Author
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. Well, it's not "true" count successful hits because in non-CL case it's also weighted
Member
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 think it's fine even if its weighted, im mostly concerned with the naming being self-explanatory enough for people to grasp the idea behind all this without too much strain - right now if i see
Member
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 think renaming it to something like StanR's suggestion is fine, even with it being weighted in mind. The current name doesn't mean anything to me |
||
|
|
||
| // If there's no classic slider accuracy - we need to weight circle judgements accordingly | ||
| double normalJudgementWeight = 1.0; | ||
|
Member
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.
Member
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'd also appreciate a comment as to why this exist and why we need to use it
Contributor
Author
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. For example let's assume that map has enough sliders so 10% of the score belongs to sliderends and sliderpoints. In this case we need to compensate for the fact that "circles" (includes sliderheads and spinners) only hold 90% of the influence they would've had in the CL scenario where they're the only type of judgments. circleJudgementWeight is technically inaccurate (I probably need to fix comment above). This refers to 300s/100s/50s/misses. When "not normal" judgements are slider end hits and big tick hits.
Member
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.
Member
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. // If this is a score with slider accuracy then we need to compensate for the fact that circles, sliderheads and spinners generally hold less influence compared to classic slider accuracy where nested judgements don't exist.
double nonNestedJudgementWeight = 1.0;something like this? |
||
|
|
||
| if (usingClassicSliderAccuracy) | ||
| { | ||
| relevantResultCount = totalResultCount - countMiss; | ||
| } | ||
| else | ||
| { | ||
| double maxSliderPortion = countSmallTicks * 0.5 + countLargeTicks * 0.1; | ||
| normalJudgementWeight = (totalResultCount + maxSliderPortion) / totalResultCount; | ||
|
|
||
| double missedSliderPortion = (double)countSliderTailMisses * 0.5 + (double)countLargeTickMisses * 0.1; | ||
| relevantResultCount = totalResultCount - (countMiss + missedSliderPortion) / normalJudgementWeight; | ||
| } | ||
|
|
||
| // Accuracy excluding countMiss. We need that because we're trying to achieve target accuracy without touching countMiss | ||
| // So it's better to pretened that there were 0 misses in the 1st place | ||
|
|
@@ -87,7 +116,7 @@ private static Dictionary<HitResult, int> generateHitResults(IBeatmap beatmap, d | |
| double ratio50To100 = Math.Pow(1 - (relevantAccuracy - 0.25) / 0.75, 2); | ||
|
|
||
| // Derived from the formula: Accuracy = (6 * c300 + 2 * c100 + c50) / (6 * totalHits), assuming that c50 = c100 * ratio50to100 | ||
| double count100Estimate = 6 * relevantResultCount * (1 - relevantAccuracy) / (5 * ratio50To100 + 4); | ||
| double count100Estimate = 6 * relevantResultCount * (1 - relevantAccuracy) / (5 * ratio50To100 + 4) * normalJudgementWeight; | ||
|
|
||
| // Get count50 according to c50 = c100 * ratio50to100 | ||
| double count50Estimate = count100Estimate * ratio50To100; | ||
|
|
@@ -108,17 +137,17 @@ private static Dictionary<HitResult, int> generateHitResults(IBeatmap beatmap, d | |
| double count50Estimate = relevantResultCount - count100Estimate; | ||
|
|
||
| // Round it to get int number of 100s | ||
| countGood = (int?)Math.Round(count100Estimate); | ||
| countGood = (int?)Math.Round(count100Estimate * normalJudgementWeight); | ||
|
|
||
| // Get number of 50s as difference between total mistimed hits and count100 | ||
| countMeh = (int?)(Math.Round(count100Estimate + count50Estimate) - countGood); | ||
| countMeh = (int?)(Math.Round((count100Estimate + count50Estimate) * normalJudgementWeight) - countGood); | ||
| } | ||
| // If accuracy is less than 16.67% - it means that we have only 50s or misses | ||
| // Assuming that we removed misses in the 1st place - that means that we need to add additional misses to achieve target accuracy | ||
| else | ||
| { | ||
| // Derived from the formula: Accuracy = (6 * c300 + 2 * c100 + c50) / (6 * totalHits), assuming that c300 = c100 = 0 | ||
| double count50Estimate = 6 * relevantResultCount * relevantAccuracy; | ||
| double count50Estimate = 6 * (totalResultCount - countMiss) * relevantAccuracy; | ||
|
|
||
| // We have 0 100s, because we can't start adding 100s again after reaching "only 50s" point | ||
| countGood = 0; | ||
|
|
@@ -130,6 +159,10 @@ private static Dictionary<HitResult, int> generateHitResults(IBeatmap beatmap, d | |
| countMiss = (int)(totalResultCount - countMeh); | ||
| } | ||
|
|
||
| // Clamp goods if total amount is bigger than possible | ||
| countGood -= Math.Clamp((int)(countGood + countMeh + countMiss - totalResultCount), 0, (int)countGood); | ||
|
tsunyoku marked this conversation as resolved.
|
||
| countMeh -= Math.Clamp((int)(countGood + countMeh + countMiss - totalResultCount), 0, (int)countMeh); | ||
|
|
||
| // Rest of the hits are 300s | ||
| countGreat = (int)(totalResultCount - countGood - countMeh - countMiss); | ||
| } | ||
|
|
@@ -142,17 +175,18 @@ private static Dictionary<HitResult, int> generateHitResults(IBeatmap beatmap, d | |
| { HitResult.Miss, countMiss } | ||
| }; | ||
|
|
||
| if (countLargeTickMisses != null) | ||
| result[HitResult.LargeTickMiss] = countLargeTickMisses.Value; | ||
|
|
||
| if (countSliderTailMisses != null) | ||
| result[HitResult.SliderTailHit] = beatmap.HitObjects.Count(x => x is Slider) - countSliderTailMisses.Value; | ||
| result[HitResult.LargeTickHit] = countLargeTicks - (int)countLargeTickMisses; | ||
| result[HitResult.LargeTickMiss] = (int)countLargeTickMisses; | ||
| result[usingClassicSliderAccuracy ? HitResult.SmallTickHit : HitResult.SliderTailHit] = countSmallTicks - (int)countSliderTailMisses; | ||
|
Givikap120 marked this conversation as resolved.
|
||
| if (usingClassicSliderAccuracy) result[HitResult.SmallTickMiss] = (int)countSliderTailMisses; | ||
|
Givikap120 marked this conversation as resolved.
Outdated
|
||
|
|
||
| return result; | ||
| } | ||
|
|
||
| protected override double GetAccuracy(IBeatmap beatmap, Dictionary<HitResult, int> statistics, Mod[] mods) | ||
| { | ||
| bool usingClassicSliderAccuracy = mods.OfType<OsuModClassic>().Any(m => m.NoSliderHeadAccuracy.Value); | ||
|
|
||
| int countGreat = statistics[HitResult.Great]; | ||
| int countGood = statistics[HitResult.Ok]; | ||
| int countMeh = statistics[HitResult.Meh]; | ||
|
|
@@ -161,18 +195,18 @@ protected override double GetAccuracy(IBeatmap beatmap, Dictionary<HitResult, in | |
| double total = 6 * countGreat + 2 * countGood + countMeh; | ||
| double max = 6 * (countGreat + countGood + countMeh + countMiss); | ||
|
|
||
| if (statistics.TryGetValue(HitResult.SliderTailHit, out int countSliderTailHit)) | ||
| if (!usingClassicSliderAccuracy && statistics.TryGetValue(HitResult.SliderTailHit, out int countSliderTailHit)) | ||
| { | ||
| int countSliders = beatmap.HitObjects.Count(x => x is Slider); | ||
|
|
||
| total += 3 * countSliderTailHit; | ||
| max += 3 * countSliders; | ||
| } | ||
|
|
||
| if (statistics.TryGetValue(HitResult.LargeTickMiss, out int countLargeTickMiss)) | ||
| if (!usingClassicSliderAccuracy && statistics.TryGetValue(HitResult.LargeTickMiss, out int countLargeTicksMiss)) | ||
|
Givikap120 marked this conversation as resolved.
Outdated
|
||
| { | ||
| int countLargeTicks = beatmap.HitObjects.Sum(obj => obj.NestedHitObjects.Count(x => x is SliderTick or SliderRepeat)); | ||
| int countLargeTickHit = countLargeTicks - countLargeTickMiss; | ||
| int countLargeTickHit = countLargeTicks - countLargeTicksMiss; | ||
|
|
||
| total += 0.6 * countLargeTickHit; | ||
| max += 0.6 * countLargeTicks; | ||
|
|
||
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.
count...is used for hitresults here, so maybetotal...instead? preferably for the other usages in this file that already exist as well, to reduce the confusionThere 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.
totalLargeTicks doesn't sounds good to me
totalResultCounthas "count" in it, and "totalResult" is a descriptor of what kind of count it ismaybe rename to
largeTickCountwould be better?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 agree with
largeTickCount