Skip to content

Commit

Permalink
Merge pull request #25993 from smoogipoo/fix-total-score-conversion
Browse files Browse the repository at this point in the history
Relocate numeric HitResult values, add accuracy conversion
  • Loading branch information
bdach authored Dec 21, 2023
2 parents 0648201 + c3c1752 commit 7e9d12e
Show file tree
Hide file tree
Showing 16 changed files with 167 additions and 131 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Mods;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
Expand All @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty
{
internal class CatchLegacyScoreSimulator : ILegacyScoreSimulator
{
private readonly ScoreProcessor scoreProcessor = new CatchScoreProcessor();

private int legacyBonusScore;
private int standardisedBonusScore;
private int combo;
Expand Down Expand Up @@ -134,7 +136,7 @@ private void simulateHit(HitObject hitObject, ref LegacyScoreAttributes attribut
if (isBonus)
{
legacyBonusScore += scoreIncrease;
standardisedBonusScore += Judgement.ToNumericResult(bonusResult);
standardisedBonusScore += scoreProcessor.GetBaseScoreForResult(bonusResult);
}
else
attributes.AccuracyScore += scoreIncrease;
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ protected override double ComputeTotalScore(double comboProgress, double accurac
}

protected override double GetComboScoreChange(JudgementResult result)
=> GetNumericResultFor(result) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base));
=> GetBaseScoreForResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base));

public override ScoreRank RankFromAccuracy(double accuracy)
{
Expand Down
31 changes: 9 additions & 22 deletions osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,44 +31,31 @@ protected override double ComputeTotalScore(double comboProgress, double accurac
+ bonusPortion;
}

protected override double GetNumericResultFor(JudgementResult result)
protected override double GetComboScoreChange(JudgementResult result)
{
switch (result.Type)
{
case HitResult.Perfect:
return 305;
}

return base.GetNumericResultFor(result);
return getBaseComboScoreForResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base));
}

protected override double GetMaxNumericResultFor(JudgementResult result)
public override int GetBaseScoreForResult(HitResult result)
{
switch (result.Judgement.MaxResult)
switch (result)
{
case HitResult.Perfect:
return 305;
}

return base.GetMaxNumericResultFor(result);
return base.GetBaseScoreForResult(result);
}

protected override double GetComboScoreChange(JudgementResult result)
private int getBaseComboScoreForResult(HitResult result)
{
double numericResult;

switch (result.Type)
switch (result)
{
case HitResult.Perfect:
numericResult = 300;
break;

default:
numericResult = GetNumericResultFor(result);
break;
return 300;
}

return numericResult * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base));
return GetBaseScoreForResult(result);
}

private class JudgementOrderComparer : IComparer<HitObject>
Expand Down
11 changes: 5 additions & 6 deletions osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,7 @@ public void TestSpinnerRewindingRotation()
double trackerRotationTolerance = 0;

addSeekStep(5000);
AddStep("calculate rotation tolerance", () =>
{
trackerRotationTolerance = Math.Abs(drawableSpinner.RotationTracker.Rotation * 0.1f);
});
AddStep("calculate rotation tolerance", () => { trackerRotationTolerance = Math.Abs(drawableSpinner.RotationTracker.Rotation * 0.1f); });
AddAssert("is disc rotation not almost 0", () => drawableSpinner.RotationTracker.Rotation, () => Is.Not.EqualTo(0).Within(100));
AddAssert("is disc rotation absolute not almost 0", () => drawableSpinner.Result.TotalRotation, () => Is.Not.EqualTo(0).Within(100));

Expand Down Expand Up @@ -133,9 +130,11 @@ public void TestSpinnerNormalBonusRewinding()

AddAssert("player score matching expected bonus score", () =>
{
var scoreProcessor = ((ScoreExposedPlayer)Player).ScoreProcessor;

// multipled by 2 to nullify the score multiplier. (autoplay mod selected)
long totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2;
return totalScore == (int)(drawableSpinner.Result.TotalRotation / 360) * new SpinnerTick().CreateJudgement().MaxNumericResult;
long totalScore = scoreProcessor.TotalScore.Value * 2;
return totalScore == (int)(drawableSpinner.Result.TotalRotation / 360) * scoreProcessor.GetBaseScoreForResult(new SpinnerTick().CreateJudgement().MaxResult);
});

addSeekStep(0);
Expand Down
6 changes: 4 additions & 2 deletions osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,21 @@
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Scoring.Legacy;

namespace osu.Game.Rulesets.Osu.Difficulty
{
internal class OsuLegacyScoreSimulator : ILegacyScoreSimulator
{
private readonly ScoreProcessor scoreProcessor = new OsuScoreProcessor();

private int legacyBonusScore;
private int standardisedBonusScore;
private int combo;
Expand Down Expand Up @@ -171,7 +173,7 @@ private void simulateHit(HitObject hitObject, ref LegacyScoreAttributes attribut
if (isBonus)
{
legacyBonusScore += scoreIncrease;
standardisedBonusScore += Judgement.ToNumericResult(bonusResult);
standardisedBonusScore += scoreProcessor.GetBaseScoreForResult(bonusResult);
}
else
attributes.AccuracyScore += scoreIncrease;
Expand Down
3 changes: 2 additions & 1 deletion osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Rulesets.Scoring;
Expand Down Expand Up @@ -312,7 +313,7 @@ protected override void UpdateAfterChildren()
updateBonusScore();
}

private static readonly int score_per_tick = new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxNumericResult;
private static readonly int score_per_tick = new OsuScoreProcessor().GetBaseScoreForResult(new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxResult);

private void updateBonusScore()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,21 @@
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Scoring.Legacy;
using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Scoring;

namespace osu.Game.Rulesets.Taiko.Difficulty
{
internal class TaikoLegacyScoreSimulator : ILegacyScoreSimulator
{
private readonly ScoreProcessor scoreProcessor = new TaikoScoreProcessor();

private int legacyBonusScore;
private int standardisedBonusScore;
private int combo;
Expand Down Expand Up @@ -191,7 +193,7 @@ private void simulateHit(HitObject hitObject, ref LegacyScoreAttributes attribut
if (isBonus)
{
legacyBonusScore += scoreIncrease;
standardisedBonusScore += Judgement.ToNumericResult(bonusResult);
standardisedBonusScore += scoreProcessor.GetBaseScoreForResult(bonusResult);
}
else
attributes.AccuracyScore += scoreIncrease;
Expand Down
8 changes: 4 additions & 4 deletions osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,20 @@ protected override double ComputeTotalScore(double comboProgress, double accurac

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

protected override double GetNumericResultFor(JudgementResult result)
public override int GetBaseScoreForResult(HitResult result)
{
switch (result.Type)
switch (result)
{
case HitResult.Ok:
return 150;
}

return base.GetNumericResultFor(result);
return base.GetBaseScoreForResult(result);
}

private double strongScaleValue(JudgementResult result)
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Tests/Gameplay/TestSceneScoreProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public void TestOnlyBonusScore()
// Apply a judgement
scoreProcessor.ApplyResult(new JudgementResult(new HitObject(), new TestJudgement(HitResult.LargeBonus)) { Type = HitResult.LargeBonus });

Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(Judgement.LARGE_BONUS_SCORE));
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(scoreProcessor.GetBaseScoreForResult(HitResult.LargeBonus)));
}

[Test]
Expand Down
5 changes: 1 addition & 4 deletions osu.Game/BackgroundDataStoreProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -340,15 +340,12 @@ private void convertLegacyTotalScoreToStandardised()

try
{
var score = scoreManager.Query(s => s.ID == id);
long newTotalScore = StandardisedScoreMigrationTools.ConvertFromLegacyTotalScore(score, beatmapManager);

// Can't use async overload because we're not on the update thread.
// ReSharper disable once MethodHasAsyncOverload
realmAccess.Write(r =>
{
ScoreInfo s = r.Find<ScoreInfo>(id)!;
s.TotalScore = newTotalScore;
StandardisedScoreMigrationTools.UpdateFromLegacy(s, beatmapManager);
s.TotalScoreVersion = LegacyScoreEncoder.LATEST_VERSION;
});

Expand Down
88 changes: 80 additions & 8 deletions osu.Game/Database/StandardisedScoreMigrationTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@ public static long GetNewStandardised(ScoreInfo score)
// We are constructing a "best possible" score from the statistics provided because it's the best we can do.
List<HitResult> sortedHits = score.Statistics
.Where(kvp => kvp.Key.AffectsCombo())
.OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key))
.OrderByDescending(kvp => processor.GetBaseScoreForResult(kvp.Key))
.SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value))
.ToList();

// Attempt to use maximum statistics from the database.
var maximumJudgements = score.MaximumStatistics
.Where(kvp => kvp.Key.AffectsCombo())
.OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key))
.OrderByDescending(kvp => processor.GetBaseScoreForResult(kvp.Key))
.SelectMany(kvp => Enumerable.Repeat(new FakeJudgement(kvp.Key), kvp.Value))
.ToList();

Expand Down Expand Up @@ -169,10 +169,10 @@ private static HitResult getMaxJudgementFor(HitResult hitResult, HitResult max)
public static long GetOldStandardised(ScoreInfo score)
{
double accuracyScore =
(double)score.Statistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value)
/ score.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value);
(double)score.Statistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => numericScoreFor(kvp.Key) * kvp.Value)
/ score.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => numericScoreFor(kvp.Key) * kvp.Value);
double comboScore = (double)score.MaxCombo / score.MaximumStatistics.Where(kvp => kvp.Key.AffectsCombo()).Sum(kvp => kvp.Value);
double bonusScore = score.Statistics.Where(kvp => kvp.Key.IsBonus()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value);
double bonusScore = score.Statistics.Where(kvp => kvp.Key.IsBonus()).Sum(kvp => numericScoreFor(kvp.Key) * kvp.Value);

double accuracyPortion = 0.3;

Expand All @@ -193,6 +193,65 @@ public static long GetOldStandardised(ScoreInfo score)
modMultiplier *= mod.ScoreMultiplier;

return (long)Math.Round((1000000 * (accuracyPortion * accuracyScore + (1 - accuracyPortion) * comboScore) + bonusScore) * modMultiplier);

static int numericScoreFor(HitResult result)
{
switch (result)
{
default:
return 0;

case HitResult.SmallTickHit:
return 10;

case HitResult.LargeTickHit:
return 30;

case HitResult.Meh:
return 50;

case HitResult.Ok:
return 100;

case HitResult.Good:
return 200;

case HitResult.Great:
return 300;

case HitResult.Perfect:
return 315;

case HitResult.SmallBonus:
return 10;

case HitResult.LargeBonus:
return 50;
}
}
}

/// <summary>
/// Updates a legacy <see cref="ScoreInfo"/> to standardised scoring.
/// </summary>
/// <param name="score">The score to update.</param>
/// <param name="beatmaps">A <see cref="BeatmapManager"/> used for <see cref="WorkingBeatmap"/> lookups.</param>
public static void UpdateFromLegacy(ScoreInfo score, BeatmapManager beatmaps)
{
score.TotalScore = convertFromLegacyTotalScore(score, beatmaps);
score.Accuracy = ComputeAccuracy(score);
}

/// <summary>
/// Updates a legacy <see cref="ScoreInfo"/> to standardised scoring.
/// </summary>
/// <param name="score">The score to update.</param>
/// <param name="difficulty">The beatmap difficulty.</param>
/// <param name="attributes">The legacy scoring attributes for the beatmap which the score was set on.</param>
public static void UpdateFromLegacy(ScoreInfo score, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes)
{
score.TotalScore = convertFromLegacyTotalScore(score, difficulty, attributes);
score.Accuracy = ComputeAccuracy(score);
}

/// <summary>
Expand All @@ -201,7 +260,7 @@ public static long GetOldStandardised(ScoreInfo score)
/// <param name="score">The score to convert the total score of.</param>
/// <param name="beatmaps">A <see cref="BeatmapManager"/> used for <see cref="WorkingBeatmap"/> lookups.</param>
/// <returns>The standardised total score.</returns>
public static long ConvertFromLegacyTotalScore(ScoreInfo score, BeatmapManager beatmaps)
private static long convertFromLegacyTotalScore(ScoreInfo score, BeatmapManager beatmaps)
{
if (!score.IsLegacyScore)
return score.TotalScore;
Expand All @@ -224,7 +283,7 @@ public static long ConvertFromLegacyTotalScore(ScoreInfo score, BeatmapManager b
ILegacyScoreSimulator sv1Simulator = legacyRuleset.CreateLegacyScoreSimulator();
LegacyScoreAttributes attributes = sv1Simulator.Simulate(beatmap, playableBeatmap);

return ConvertFromLegacyTotalScore(score, LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap.Beatmap), attributes);
return convertFromLegacyTotalScore(score, LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap.Beatmap), attributes);
}

/// <summary>
Expand All @@ -234,7 +293,7 @@ public static long ConvertFromLegacyTotalScore(ScoreInfo score, BeatmapManager b
/// <param name="difficulty">The beatmap difficulty.</param>
/// <param name="attributes">The legacy scoring attributes for the beatmap which the score was set on.</param>
/// <returns>The standardised total score.</returns>
public static long ConvertFromLegacyTotalScore(ScoreInfo score, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes)
private static long convertFromLegacyTotalScore(ScoreInfo score, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes)
{
if (!score.IsLegacyScore)
return score.TotalScore;
Expand Down Expand Up @@ -386,6 +445,19 @@ double lowerEstimateOfComboPortionInStandardisedScore
}
}

public static double ComputeAccuracy(ScoreInfo scoreInfo)
{
Ruleset ruleset = scoreInfo.Ruleset.CreateInstance();
ScoreProcessor scoreProcessor = ruleset.CreateScoreProcessor();

int baseScore = scoreInfo.Statistics.Where(kvp => kvp.Key.AffectsAccuracy())
.Sum(kvp => kvp.Value * scoreProcessor.GetBaseScoreForResult(kvp.Key));
int maxBaseScore = scoreInfo.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy())
.Sum(kvp => kvp.Value * scoreProcessor.GetBaseScoreForResult(kvp.Key));

return maxBaseScore == 0 ? 1 : baseScore / (double)maxBaseScore;
}

/// <summary>
/// Used to populate the <paramref name="score"/> model using data parsed from its corresponding replay file.
/// </summary>
Expand Down
Loading

0 comments on commit 7e9d12e

Please sign in to comment.