Skip to content

Fix accuracy and combo proportions being back-to-front in mania standardised score conversion algorithm#38321

Open
bdach wants to merge 1 commit into
ppy:masterfrom
bdach:mania-score-conversion-epic-fail
Open

Fix accuracy and combo proportions being back-to-front in mania standardised score conversion algorithm#38321
bdach wants to merge 1 commit into
ppy:masterfrom
bdach:mania-score-conversion-epic-fail

Conversation

@bdach

@bdach bdach commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator
  • Pending sheet generation & verification

Warning

The deployment of this fix, if accepted, will consist of reverification of all mania legacy scores.

Of particular note, #38203 is also a change that requires server-side reprocessing and that reprocessing has not happened yet as far as I am aware.

Coming from #38102 (but does not fix it).

Compare:

protected override double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion)
{
return 150000 * comboProgress
+ 850000 * Math.Pow(Accuracy.Value, 2 + 2 * Accuracy.Value) * accuracyProgress
+ bonusPortion;
}
convertedTotalScoreWithoutMods = (long)Math.Round(
850000 * comboProportion
+ 150000 * Math.Pow(score.Accuracy, 2 + 2 * score.Accuracy)
+ bonusProportion);

Note once more that this does not fix the reported user issue. In that particular instance, the outcome will just be flipped (the lazer score will be favoured over the stable score). Unfortunately there is no way to improve the estimate any further to reproduce the exact result. The reason why is that the combo portion is calculated as

protected override double GetComboScoreChange(JudgementResult result)
{
return getBaseComboScoreForResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base));
}

The key factor in the above calculation is result.ComboAfterJudgement, which is not replicable without being able to access full judgement history for a score, which is not available for stable scores unless the play is somehow fully simulated headless.

…ardised score conversion algorithm

Coming from ppy#38102 (but does not
fix it).

Compare:

https://github.com/ppy/osu/blob/d54366dd1192c030b133d85887a19220a4024399/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs#L28-L33
https://github.com/ppy/osu/blob/d54366dd1192c030b133d85887a19220a4024399/osu.Game/Database/StandardisedScoreMigrationTools.cs#L336-L339

Note once more that this *does not fix* the reported user issue. In that
particular instance, the outcome will just be flipped (the lazer score
will be favoured over the stable score). Unfortunately there is no way
to improve the estimate any further to reproduce the exact result. The
reason why is that the combo portion is calculated as

https://github.com/ppy/osu/blob/d54366dd1192c030b133d85887a19220a4024399/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs#L35-L38

The key factor in the above calculation is `result.ComboAfterJudgement`,
which is not replicable without being able to access full judgement
history for a score, which is not available unless the play is fully
simulated headless.
@bdach

bdach commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

!diffcalc
RULESET=mania
GENERATORS=score

convertedTotalScoreWithoutMods = (long)Math.Round(
850000 * comboProportion
+ 150000 * Math.Pow(score.Accuracy, 2 + 2 * score.Accuracy)
150000 * comboProportion

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙈

@bdach

bdach commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Sheet generator looks stuck again.

Looks like I'll have to run it locally over the weekend or something.

@peppy

peppy commented Jul 17, 2026

Copy link
Copy Markdown
Member

It will likely get stuck for you locally in a similar way. This has been a common theme recently.

@bdach

bdach commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

Locally generated sheet: https://docs.google.com/spreadsheets/d/12ma0U_uv815Yg_7Ik8l2WaNP5yZ7JwbKTHK8Yn_ivsg/edit

Will examine tomorrow.

@bdach

bdach commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

Queueing up another run via actions just to see if it happens to work this time:

!diffcalc
RULESET=mania
GENERATORS=score

@bdach

bdach commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

Locally generated sheet: https://docs.google.com/spreadsheets/d/12ma0U_uv815Yg_7Ik8l2WaNP5yZ7JwbKTHK8Yn_ivsg/edit

In inspection, I found out that for some scores involving converted beatmaps and key mods the value on the sheet will not match the value shown in client. The reason for that is that in the production database osu_beatmaps.countTotal can have completely bonkers values for no reason, which then manifests over here. That value being wrong then causes the calculation of the original key count post-conversion at

if (difficulty.TotalObjectCount > 0 && difficulty.EndTimeObjectCount >= 0)
{
int countSliderOrSpinner = difficulty.EndTimeObjectCount;
// In osu!stable, this division appears as if it happens on floats, but due to release-mode
// optimisations, it actually ends up happening on doubles.
double percentSpecialObjects = (double)countSliderOrSpinner / difficulty.TotalObjectCount;
if (percentSpecialObjects < 0.2)
return 7;
if (percentSpecialObjects < 0.3 || roundedCircleSize >= 5)
return roundedOverallDifficulty > 5 ? 7 : 6;
if (percentSpecialObjects > 0.6)
return roundedOverallDifficulty > 4 ? 5 : 4;
}
to return a wrong value, and thus not apply the appropriate below-1 score multiplier at
double legacyModMultiplier = legacyRuleset.CreateLegacyScoreSimulator().GetLegacyScoreMultiplier(score.Mods, difficulty);
int maximumLegacyAccuracyScore = attributes.AccuracyScore;
long maximumLegacyComboScore = (long)Math.Round(attributes.ComboScore * legacyModMultiplier);
which causes the discrepancy.

The above issue cannot be fixed in client because the issue isn't in client, and in general for all of the scores I checked (via downloading the replay if present and and playing it back) the new estimation is closer to the played-back total score than the old one.

To that end, undrafting.

@bdach
bdach marked this pull request as ready for review July 23, 2026 07:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants