Skip to content
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

Fix Score V1 simulation in scoring test scene incorrectly applying multiplier #24792

Merged
merged 1 commit into from
Sep 13, 2023

Conversation

bdach
Copy link
Collaborator

@bdach bdach commented Sep 12, 2023

TestSceneScoring included a local simulation of stable's Score V1 algorithm. One of the parts of said algorithm is a mysterious "score multiplier", influenced by - among others - the beatmap's drain rate, overall difficulty, circle size, object count, drain length, and active mods. (An implementation of this already exists in lazer source, in OsuLegacyScoreSimulator, but more on this later.)

However, TestSceneScoring had this multiplier in two places, with two distinct values, one of which being 1 (i.e. basically off). Unfortunately, the place that had 1 as the multiplier was the wrong one.

Stable calculates the score increase for every hit in two stages; first, it takes the raw numerical value of the judgement, but then applies a combo-based bonus on top of it:

scoreIncrease += (int)(Math.Max(0, ComboCounter.HitCombo - 1) * (scoreIncrease / 25 * ScoreMultiplier));

On the face of it, it may appear that the ScoreMultiplier factor can be factored out and applied at the end only when returning total score. However, once the above formula is rewritten as:

scoreIncrease = scoreIncrease + (int)(Math.Max(0, ComboCounter.HitCombo - 1) * (scoreIncrease / 25 * ScoreMultiplier));
              = scoreIncrease * (1 + (Math.Max(0, ComboCounter.HitCombo - 1) / 25 * ScoreMultiplier))

it becomes clear that that assumption is actually incorrect, and the ScoreMultiplier must be applied to every score increase individually.


This was spurred by a cross-check of TestSceneScoring for bugs after this discord message. It's the only issue I found, so not sure what that report was about.

Everything above was cross-checked experimentally against stable source on an example test map with 100 objects, and a replay hitting them perfectly.

The following archive contains the aforementioned test beatmap and replay: ScoreV1PerfectTest.zip

To test against stable, apply:

diff --git a/osu!/GameModes/Play/Rulesets/Ruleset.cs b/osu!/GameModes/Play/Rulesets/Ruleset.cs
index d7e50b7..079285b 100644
--- a/osu!/GameModes/Play/Rulesets/Ruleset.cs
+++ b/osu!/GameModes/Play/Rulesets/Ruleset.cs
@@ -381,6 +381,7 @@ namespace osu.GameModes.Play.Rulesets
             Player.Relaxing2 = ModManager.CheckActive(Player.currentScore.EnabledMods, Mods.Relax2);
             ScoreMultiplier = BeatmapManager.Current.DifficultyPeppyStars *
                                 ModManager.ScoreMultiplier(CurrentScore.EnabledMods, Player.Mode, hitObjectManager.Beatmap);
+            Logger.Log($"ScoreMultiplier = {ScoreMultiplier}");
             if (!ModManager.CheckActive(Player.currentScore.EnabledMods, Mods.SuddenDeath))
             {
                 if (ModManager.CheckActive(Player.currentScore.EnabledMods, Mods.Easy))
@@ -1228,6 +1229,7 @@ namespace osu.GameModes.Play.Rulesets
 
             if (player.Status == PlayerStatus.Playing && ScoreMeter != null)
                 ScoreMeter.AddMeter(value);
+            Logger.Log($"total score = {CurrentScore.TotalScore}");
 
             player.CheckFailed();
         }

to extract the intermediate score totals. You should get something like the following:

2023-09-12T18:20:14: ScoreMultiplier = 3

2023-09-12T18:12:31: total score = 300
2023-09-12T18:12:32: total score = 600
2023-09-12T18:12:32: total score = 936
2023-09-12T18:12:33: total score = 1308
2023-09-12T18:12:33: total score = 1716
2023-09-12T18:12:34: total score = 2160
2023-09-12T18:12:34: total score = 2640
2023-09-12T18:12:35: total score = 3156
2023-09-12T18:12:35: total score = 3708
2023-09-12T18:12:36: total score = 4296
2023-09-12T18:12:36: total score = 4920
2023-09-12T18:12:37: total score = 5580
2023-09-12T18:12:37: total score = 6276
2023-09-12T18:12:38: total score = 7008
2023-09-12T18:12:38: total score = 7776
2023-09-12T18:12:39: total score = 8580
2023-09-12T18:12:39: total score = 9420
2023-09-12T18:12:40: total score = 10296
2023-09-12T18:12:40: total score = 11208
2023-09-12T18:12:41: total score = 12156
2023-09-12T18:12:41: total score = 13140
2023-09-12T18:12:42: total score = 14160
2023-09-12T18:12:42: total score = 15216
2023-09-12T18:12:43: total score = 16308
2023-09-12T18:12:43: total score = 17436
2023-09-12T18:12:44: total score = 18600
2023-09-12T18:12:44: total score = 19800
2023-09-12T18:12:45: total score = 21036
2023-09-12T18:12:45: total score = 22308
2023-09-12T18:12:46: total score = 23616
2023-09-12T18:12:46: total score = 24960
2023-09-12T18:12:47: total score = 26340
2023-09-12T18:12:47: total score = 27756
2023-09-12T18:12:48: total score = 29208
2023-09-12T18:12:48: total score = 30696
2023-09-12T18:12:49: total score = 32220
2023-09-12T18:12:49: total score = 33780
2023-09-12T18:12:50: total score = 35376
2023-09-12T18:12:50: total score = 37008
2023-09-12T18:12:51: total score = 38676
2023-09-12T18:12:51: total score = 40380
2023-09-12T18:12:52: total score = 42120
2023-09-12T18:12:52: total score = 43896
2023-09-12T18:12:53: total score = 45708
2023-09-12T18:12:53: total score = 47556
2023-09-12T18:12:54: total score = 49440
2023-09-12T18:12:54: total score = 51360
2023-09-12T18:12:55: total score = 53316
2023-09-12T18:12:55: total score = 55308
2023-09-12T18:12:56: total score = 57336
2023-09-12T18:12:56: total score = 59400
2023-09-12T18:12:57: total score = 61500
2023-09-12T18:12:57: total score = 63636
2023-09-12T18:12:58: total score = 65808
2023-09-12T18:12:58: total score = 68016
2023-09-12T18:12:59: total score = 70260
2023-09-12T18:12:59: total score = 72540
2023-09-12T18:13:00: total score = 74856
2023-09-12T18:13:00: total score = 77208
2023-09-12T18:13:01: total score = 79596
2023-09-12T18:13:01: total score = 82020
2023-09-12T18:13:02: total score = 84480
2023-09-12T18:13:02: total score = 86976
2023-09-12T18:13:03: total score = 89508
2023-09-12T18:13:03: total score = 92076
2023-09-12T18:13:04: total score = 94680
2023-09-12T18:13:04: total score = 97320
2023-09-12T18:13:05: total score = 99996
2023-09-12T18:13:05: total score = 102708
2023-09-12T18:13:06: total score = 105456
2023-09-12T18:13:06: total score = 108240
2023-09-12T18:13:07: total score = 111060
2023-09-12T18:13:07: total score = 113916
2023-09-12T18:13:08: total score = 116808
2023-09-12T18:13:08: total score = 119736
2023-09-12T18:13:09: total score = 122700
2023-09-12T18:13:09: total score = 125700
2023-09-12T18:13:10: total score = 128736
2023-09-12T18:13:10: total score = 131808
2023-09-12T18:13:11: total score = 134916
2023-09-12T18:13:11: total score = 138060
2023-09-12T18:13:12: total score = 141240
2023-09-12T18:13:12: total score = 144456
2023-09-12T18:13:13: total score = 147708
2023-09-12T18:13:13: total score = 150996
2023-09-12T18:13:14: total score = 154320
2023-09-12T18:13:14: total score = 157680
2023-09-12T18:13:15: total score = 161076
2023-09-12T18:13:15: total score = 164508
2023-09-12T18:13:16: total score = 167976
2023-09-12T18:13:16: total score = 171480
2023-09-12T18:13:17: total score = 175020
2023-09-12T18:13:17: total score = 178596
2023-09-12T18:13:18: total score = 182208
2023-09-12T18:13:18: total score = 185856
2023-09-12T18:13:19: total score = 189540
2023-09-12T18:13:19: total score = 193260
2023-09-12T18:13:20: total score = 197016
2023-09-12T18:13:20: total score = 200808
2023-09-12T18:13:21: total score = 204636

To cross-check this PR against the above, adjust the score_multiplier in the test scene.

For what it's worth, I'm not sure the score V1 simulation code in the test scene is going to live for much longer, as with each passing minute I'm eyeing the ILegacyScoreSimulators for reuse. But I'm not willing to make a concrete judgement on this yet, it's a one-liner fix, and I already put the time in, so I figure what's the harm?

…ltiplier

`TestSceneScoring` included a local simulation of stable's Score V1
algorithm. One of the parts of said algorithm is a mysterious
"score multiplier", influenced by - among others - the beatmap's drain
rate, overall difficulty, circle size, object count, drain length,
and active mods. (An implementation of this already exists in lazer
source, in `OsuLegacyScoreSimulator`, but more on this later.)

However, `TestSceneScoring` had this multiplier in _two_ places, with
_two_ distinct values, one of which being 1 (i.e. basically off).
Unfortunately, the place that had 1 as the multiplier was the wrong one.

Stable calculates the score increase for every hit in two stages;
first, it takes the raw numerical value of the judgement, but then
applies a combo-based bonus on top of it:

    scoreIncrease += (int)(Math.Max(0, ComboCounter.HitCombo - 1) * (scoreIncrease / 25 * ScoreMultiplier));

On the face of it, it may appear that the `ScoreMultiplier` factor
can be factored out and applied at the end only when returning total
score. However, once the above formula is rewritten as:

    scoreIncrease = scoreIncrease + (int)(Math.Max(0, ComboCounter.HitCombo - 1) * (scoreIncrease / 25 * ScoreMultiplier));
                  = scoreIncrease * (1 + (Math.Max(0, ComboCounter.HitCombo - 1) / 25 * ScoreMultiplier))

it becomes clear that that assumption is actually _incorrect_,
and the `ScoreMultiplier` _must_ be applied to every score increase
individually.

The above was cross-checked experimentally against stable source
on an example test map with 100 objects, and a replay hitting them
perfectly.
@peppy peppy merged commit f90f249 into ppy:master Sep 13, 2023
@bdach bdach deleted the fix-score-v1-multiplier-handling branch September 13, 2023 04:32
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