Feat: Normalize Scoring - #132
Conversation
|
overall checks out, but i feel it'd be better to just round on hit rather than on total score, that way you can avoid rounding everywhere just keep the int cast that was here double hitScore = Attempt.UnitHitScore * Attempt.ComboMultiplier * factor; |
|
Rounding on hit makes a bigger error then just rounding the total score. |
|
right, my mistake, if you round down once the score can be too low. Math.ceil should do it then, instead of Math.round btw, i'm against having a double score counter because the game doesn't support it visually, and afaik no other rg i've seen uses decimal places |
|
Again, it's off by 40 points. To not round it every time you could store the score as a double in RawScore and write the getter of Score to round RawScore and convert it to uint (thinking about it, it's still rounding everytime but it's written somewhere else). |
|
aaaaalright, i've been testing around for a bit and the decimal part does matter, as the formula stops working properly at around 1k notes. the score shown in game should regardless be rounded to not show any decimal places, so you do need to round on every note. on another note, i checked this loop again and it looks quite naive for (uint i = 1; i <= 7; i++)
{
NoteWeight += ComboMultiplierIncrement * i;
}
NoteWeight += ((uint)Map.Notes.Length - ComboMultiplierIncrement * 7) * 8;suppose there is a map with 1 note; obviously that note should award 1M points by itself, however:
therefore you'll get the wrong weight, even negative (e.g. w/ 1 note, increment of 2): |
|
quick question but how would normalization work with the ingame score multiplier? cuz i feel like if normilization gets added thatd either have to be reworked or removed completely. |
The multiplier applies to the max score. So a 1.1x multiplier means that the maximum score is no longer 1,000,000 but 1,100,000.
You are right to mention that, I did not account for maps under 8 notes. I will make another comment/commit as soon as I get it working. |
|
Now the normalization accounts for levels under 9 notes by calculating the max combo you reach in a level, instead of having it hardcoded to 7. |
|
i feel like this line ComboMultiplierIncrement = Map.Notes.Length <= 8 ? 1 : Math.Max(2, (uint)Map.Notes.Length / 200);causes some weird behavior now. the combo increment shouldn't really change even if the map is under 8 notes. a map with 9 notes should reach combo 4, same as 8 notes, given the code that was previously there. with that new expression, a map with 8 notes will get up to combo 7 (clamped by 1-7 range), which is quite unbalanced. also maybe for the sake of clarity, stick with maximum possible combo being 8 instead of 7. edit: in fact, this sum for (uint i = 1; i <= MaxComboMultiplier; i++)
{
NoteWeight += ComboMultiplierIncrement * i;
}
NoteWeight += ((uint)Map.Notes.Length - ComboMultiplierIncrement * MaxComboMultiplier) * (MaxComboMultiplier + 1);runs once more than needed on combos below 7 (because the last sentence always runs) ... atp if you wanna just replicate this code then that also works |
|
Yeah, I named the variable wrong. It isn't really the max combo rather the iterations for the loop and since I used it more times I decided to put it in a variable. ComboMultiplierIncrement = Math.Max(2, (uint)Map.Notes.Length / 200);
// ...
uint noteWeightIterations = (uint)Math.Clamp(Math.Floor((double)Map.Notes.Length / ComboMultiplierIncrement), 1, 7);
for (uint i = 1; i <= noteWeightIterations; i++)
{
NoteWeight += Math.Min(ComboMultiplierIncrement, (uint)Map.Notes.Length) * i;
}
NoteWeight += ((uint)Map.Notes.Length - Math.Min(ComboMultiplierIncrement, (uint)Map.Notes.Length) * noteWeightIterations) * (noteWeightIterations + 1);This should be clearer (also removed the ternary operator as you suggested).
If we have 7 notes that would mean a max combo of 3. The for loop adds 2 + 4 + 6 = 12 and the sum after the for loop adds an additional 4 (notes left * next combo which is 1 * 4 = 4 in this case). So this brings the note weight to 16 notes. During gameplay you hit the first 2 notes that weigh 1, 2 notes that weigh 2, 2 that weigh 3 and 1 that weighs 4. So the weight of the notes hit during gameplay is 2 + 4 + 6 + 4, the same as the one calculated before. I don't see where it runs an additional time. |
what i meant is the 8x score multiplier u get for hitting notes without missing. you could theoretically include that but it seems unviable in a way |
this is what we're trying to do, it's perfectly possible. most similar example i can think of is djmax which goes up to x5 over time, but the scoring is normalized to 1M
ok right, mb, didn't account for the case where the map length == combo increment * n.º of increments correctly edit: since combo increment is a constant you can inline the loop, probably the compiler does this internally |
I didn't think about squashing the for loop into one computation. That would in fact be more optimal. uint noteWeightIterations = (uint)Math.Clamp(Math.Floor((double)Map.Notes.Length / ComboMultiplierIncrement), 1, 7);
NoteWeight += Math.Min(ComboMultiplierIncrement, (uint)Map.Notes.Length) * (1 + noteWeightIterations) * noteWeightIterations / 2;
NoteWeight += ((uint)Map.Notes.Length - Math.Min(ComboMultiplierIncrement, (uint)Map.Notes.Length) * noteWeightIterations) * (noteWeightIterations + 1);This code sums up the range of i from 1 to noteWeightIterations and calculates the weight of the notes during ramp up only once. Now the variable name |
Score is capped to 1 milion for all levels.
Modifiers and speed modifiers are applied to the max score making it higher or lower.
I also had to fix Attempt.ModsMultiplier since it was not being assigned and would always be equal to 1 no matter what.