1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 20:22:55 +08:00

Merge pull request #16938 from smoogipoo/classic-score-hitobject-factor

Scale classic score by hitobject count instead of max combo
This commit is contained in:
Dean Herbert 2022-02-22 00:40:56 +09:00 committed by GitHub
commit 897f0ecd5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 28 additions and 23 deletions

View File

@ -36,9 +36,9 @@ namespace osu.Game.Tests.Rulesets.Scoring
[TestCase(ScoringMode.Standardised, HitResult.Meh, 750_000)]
[TestCase(ScoringMode.Standardised, HitResult.Ok, 800_000)]
[TestCase(ScoringMode.Standardised, HitResult.Great, 1_000_000)]
[TestCase(ScoringMode.Classic, HitResult.Meh, 41)]
[TestCase(ScoringMode.Classic, HitResult.Ok, 46)]
[TestCase(ScoringMode.Classic, HitResult.Great, 72)]
[TestCase(ScoringMode.Classic, HitResult.Meh, 20)]
[TestCase(ScoringMode.Classic, HitResult.Ok, 23)]
[TestCase(ScoringMode.Classic, HitResult.Great, 36)]
public void TestSingleOsuHit(ScoringMode scoringMode, HitResult hitResult, int expectedScore)
{
scoreProcessor.Mode.Value = scoringMode;
@ -86,17 +86,17 @@ namespace osu.Game.Tests.Rulesets.Scoring
[TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 1_000_030)] // 1 * 300_000 + 700_000 (max combo 0) + 3 * 10 (bonus points)
[TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 1_000_150)] // 1 * 300_000 + 700_000 (max combo 0) + 3 * 50 (bonus points)
[TestCase(ScoringMode.Classic, HitResult.Miss, HitResult.Great, 0)]
[TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 68)]
[TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 81)]
[TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 109)]
[TestCase(ScoringMode.Classic, HitResult.Great, HitResult.Great, 149)]
[TestCase(ScoringMode.Classic, HitResult.Perfect, HitResult.Perfect, 149)]
[TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, HitResult.SmallTickHit, 9)]
[TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 15)]
[TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 86)]
[TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 104)]
[TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 140)]
[TestCase(ScoringMode.Classic, HitResult.Great, HitResult.Great, 190)]
[TestCase(ScoringMode.Classic, HitResult.Perfect, HitResult.Perfect, 190)]
[TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, HitResult.SmallTickHit, 18)]
[TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 31)]
[TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)]
[TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 149)]
[TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 18)]
[TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 18)]
[TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 12)]
[TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 36)]
[TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 36)]
public void TestFourVariousResultsOneMiss(ScoringMode scoringMode, HitResult hitResult, HitResult maxResult, int expectedScore)
{
var minResult = new TestJudgement(hitResult).MinResult;
@ -128,8 +128,8 @@ namespace osu.Game.Tests.Rulesets.Scoring
/// </remarks>
[TestCase(ScoringMode.Standardised, HitResult.SmallTickHit, 978_571)] // (3 * 10 + 100) / (4 * 10 + 100) * 300_000 + (1 / 1) * 700_000
[TestCase(ScoringMode.Standardised, HitResult.SmallTickMiss, 914_286)] // (3 * 0 + 100) / (4 * 10 + 100) * 300_000 + (1 / 1) * 700_000
[TestCase(ScoringMode.Classic, HitResult.SmallTickHit, 69)] // (((3 * 10 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25)
[TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, 60)] // (((3 * 0 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25)
[TestCase(ScoringMode.Classic, HitResult.SmallTickHit, 34)]
[TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, 30)]
public void TestSmallTicksAccuracy(ScoringMode scoringMode, HitResult hitResult, int expectedScore)
{
IEnumerable<HitObject> hitObjects = Enumerable

View File

@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Scoring
private double getScore(ScoringMode mode)
{
return GetScore(mode, maxAchievableCombo,
return GetScore(mode,
calculateAccuracyRatio(baseScore),
calculateComboRatio(HighestCombo.Value),
scoreResultCounts);
@ -222,12 +222,11 @@ namespace osu.Game.Rulesets.Scoring
/// Computes the total score.
/// </summary>
/// <param name="mode">The <see cref="ScoringMode"/> to compute the total score in.</param>
/// <param name="maxCombo">The maximum combo achievable in the beatmap.</param>
/// <param name="accuracyRatio">The accuracy percentage achieved by the player.</param>
/// <param name="comboRatio">The proportion of <paramref name="maxCombo"/> achieved by the player.</param>
/// <param name="comboRatio">The proportion of the max combo achieved by the player.</param>
/// <param name="statistics">Any statistics to be factored in.</param>
/// <returns>The total score.</returns>
public double GetScore(ScoringMode mode, int maxCombo, double accuracyRatio, double comboRatio, Dictionary<HitResult, int> statistics)
public double GetScore(ScoringMode mode, double accuracyRatio, double comboRatio, Dictionary<HitResult, int> statistics)
{
switch (mode)
{
@ -238,10 +237,16 @@ namespace osu.Game.Rulesets.Scoring
return (max_score * (accuracyScore + comboScore) + getBonusScore(statistics)) * scoreMultiplier;
case ScoringMode.Classic:
int totalHitObjects = statistics.Where(k => k.Key >= HitResult.Miss && k.Key <= HitResult.Perfect).Sum(k => k.Value);
// If there are no hitobjects then the beatmap can be composed of only ticks or spinners, so ensure we don't multiply by 0 at all times.
if (totalHitObjects == 0)
totalHitObjects = 1;
// This gives a similar feeling to osu!stable scoring (ScoreV1) while keeping classic scoring as only a constant multiple of standardised scoring.
// The invariant is important to ensure that scores don't get re-ordered on leaderboards between the two scoring modes.
double scaledStandardised = GetScore(ScoringMode.Standardised, maxCombo, accuracyRatio, comboRatio, statistics) / max_score;
return Math.Pow(scaledStandardised * (maxCombo + 1), 2) * 18;
double scaledStandardised = GetScore(ScoringMode.Standardised, accuracyRatio, comboRatio, statistics) / max_score;
return Math.Pow(scaledStandardised * totalHitObjects, 2) * 36;
}
}
@ -265,7 +270,7 @@ namespace osu.Game.Rulesets.Scoring
computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value;
}
return GetScore(mode, maxAchievableCombo, calculateAccuracyRatio(computedBaseScore), calculateComboRatio(maxCombo), statistics);
return GetScore(mode, calculateAccuracyRatio(computedBaseScore), calculateComboRatio(maxCombo), statistics);
}
/// <summary>

View File

@ -184,7 +184,7 @@ namespace osu.Game.Scoring
var scoreProcessor = ruleset.CreateScoreProcessor();
scoreProcessor.Mods.Value = score.Mods;
return (long)Math.Round(scoreProcessor.GetScore(mode, beatmapMaxCombo, accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics));
return (long)Math.Round(scoreProcessor.GetScore(mode, accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics));
}
/// <summary>