1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-13 08:32:57 +08:00

Fix tests

This commit is contained in:
Dan Balasescu 2023-05-23 18:18:27 +09:00
parent d45b54399b
commit 844c023fb7
3 changed files with 75 additions and 148 deletions

View File

@ -76,22 +76,38 @@ namespace osu.Game.Tests.Gameplay
// Reset with a miss instead.
scoreProcessor.ResetFromReplayFrame(new OsuReplayFrame
{
Header = new FrameHeader(0, 0, 0, new Dictionary<HitResult, int> { { HitResult.Miss, 1 } }, DateTimeOffset.Now)
Header = new FrameHeader(0, 0, 0, 0, new Dictionary<HitResult, int> { { HitResult.Miss, 1 } }, new ScoreProcessorStatistics
{
MaximumBaseScore = 300,
BaseScore = 0,
CountAccuracyJudgements = 1,
ComboPortion = 0,
BonusPortion = 0
}, DateTimeOffset.Now)
});
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
Assert.That(scoreProcessor.Combo.Value, Is.EqualTo(0));
Assert.That(scoreProcessor.Accuracy.Value, Is.EqualTo(0));
// Reset with no judged hit.
scoreProcessor.ResetFromReplayFrame(new OsuReplayFrame
{
Header = new FrameHeader(0, 0, 0, new Dictionary<HitResult, int>(), DateTimeOffset.Now)
Header = new FrameHeader(0, 0, 0, 0, new Dictionary<HitResult, int>(), new ScoreProcessorStatistics
{
MaximumBaseScore = 0,
BaseScore = 0,
CountAccuracyJudgements = 0,
ComboPortion = 0,
BonusPortion = 0
}, DateTimeOffset.Now)
});
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
Assert.That(scoreProcessor.JudgedHits, Is.Zero);
Assert.That(scoreProcessor.Combo.Value, Is.EqualTo(0));
Assert.That(scoreProcessor.Accuracy.Value, Is.EqualTo(1));
}
[Test]

View File

@ -14,11 +14,12 @@ using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Scoring.Legacy;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Rulesets.Scoring
@ -31,7 +32,7 @@ namespace osu.Game.Tests.Rulesets.Scoring
[SetUp]
public void SetUp()
{
scoreProcessor = new ScoreProcessor(new TestRuleset());
scoreProcessor = new ScoreProcessor(new OsuRuleset());
beatmap = new TestBeatmap(new RulesetInfo())
{
HitObjects = new List<HitObject>
@ -41,15 +42,14 @@ namespace osu.Game.Tests.Rulesets.Scoring
};
}
[TestCase(ScoringMode.Standardised, HitResult.Meh, 750_000)]
[TestCase(ScoringMode.Standardised, HitResult.Ok, 800_000)]
[TestCase(ScoringMode.Standardised, HitResult.Meh, 116_667)]
[TestCase(ScoringMode.Standardised, HitResult.Ok, 233_338)]
[TestCase(ScoringMode.Standardised, HitResult.Great, 1_000_000)]
[TestCase(ScoringMode.Classic, HitResult.Meh, 20)]
[TestCase(ScoringMode.Classic, HitResult.Ok, 23)]
[TestCase(ScoringMode.Classic, HitResult.Meh, 0)]
[TestCase(ScoringMode.Classic, HitResult.Ok, 2)]
[TestCase(ScoringMode.Classic, HitResult.Great, 36)]
public void TestSingleOsuHit(ScoringMode scoringMode, HitResult hitResult, int expectedScore)
{
scoreProcessor.Mode.Value = scoringMode;
scoreProcessor.ApplyBeatmap(beatmap);
var judgementResult = new JudgementResult(beatmap.HitObjects.Single(), new OsuJudgement())
@ -58,7 +58,7 @@ namespace osu.Game.Tests.Rulesets.Scoring
};
scoreProcessor.ApplyResult(judgementResult);
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(expectedScore).Within(0.5d));
Assert.That(scoreProcessor.GetDisplayScore(scoringMode), Is.EqualTo(expectedScore).Within(0.5d));
}
/// <summary>
@ -70,39 +70,29 @@ namespace osu.Game.Tests.Rulesets.Scoring
/// <param name="expectedScore">Expected score after all objects have been judged, rounded to the nearest integer.</param>
/// <remarks>
/// This test intentionally misses the 3rd hitobject to achieve lower than 75% accuracy and 50% max combo.
/// <para>
/// For standardised scoring, <paramref name="expectedScore"/> is calculated using the following formula:
/// 1_000_000 * (((3 * <paramref name="hitResult"/>) / (4 * <paramref name="maxResult"/>)) * 30% + (bestCombo / maxCombo) * 70%)
/// </para>
/// <para>
/// For classic scoring, <paramref name="expectedScore"/> is calculated using the following formula:
/// <paramref name="hitResult"/> / <paramref name="maxResult"/> * 936
/// where 936 is simplified from:
/// 75% * 4 * 300 * (1 + 1/25)
/// </para>
/// </remarks>
[TestCase(ScoringMode.Standardised, HitResult.Miss, HitResult.Great, 0)] // (3 * 0) / (4 * 300) * 300_000 + (0 / 4) * 700_000
[TestCase(ScoringMode.Standardised, HitResult.Meh, HitResult.Great, 387_500)] // (3 * 50) / (4 * 300) * 300_000 + (2 / 4) * 700_000
[TestCase(ScoringMode.Standardised, HitResult.Ok, HitResult.Great, 425_000)] // (3 * 100) / (4 * 300) * 300_000 + (2 / 4) * 700_000
[TestCase(ScoringMode.Standardised, HitResult.Good, HitResult.Perfect, 492_857)] // (3 * 200) / (4 * 350) * 300_000 + (2 / 4) * 700_000
[TestCase(ScoringMode.Standardised, HitResult.Great, HitResult.Great, 575_000)] // (3 * 300) / (4 * 300) * 300_000 + (2 / 4) * 700_000
[TestCase(ScoringMode.Standardised, HitResult.Perfect, HitResult.Perfect, 575_000)] // (3 * 350) / (4 * 350) * 300_000 + (2 / 4) * 700_000
[TestCase(ScoringMode.Standardised, HitResult.SmallTickMiss, HitResult.SmallTickHit, 700_000)] // (3 * 0) / (4 * 10) * 300_000 + 700_000 (max combo 0)
[TestCase(ScoringMode.Standardised, HitResult.SmallTickHit, HitResult.SmallTickHit, 925_000)] // (3 * 10) / (4 * 10) * 300_000 + 700_000 (max combo 0)
[TestCase(ScoringMode.Standardised, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] // (3 * 0) / (4 * 30) * 300_000 + (0 / 4) * 700_000
[TestCase(ScoringMode.Standardised, HitResult.LargeTickHit, HitResult.LargeTickHit, 575_000)] // (3 * 30) / (4 * 30) * 300_000 + (0 / 4) * 700_000
[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.Standardised, HitResult.Miss, HitResult.Great, 0)]
[TestCase(ScoringMode.Standardised, HitResult.Meh, HitResult.Great, 79_333)]
[TestCase(ScoringMode.Standardised, HitResult.Ok, HitResult.Great, 158_667)]
[TestCase(ScoringMode.Standardised, HitResult.Good, HitResult.Perfect, 302_402)]
[TestCase(ScoringMode.Standardised, HitResult.Great, HitResult.Great, 492_894)]
[TestCase(ScoringMode.Standardised, HitResult.Perfect, HitResult.Perfect, 492_894)]
[TestCase(ScoringMode.Standardised, HitResult.SmallTickMiss, HitResult.SmallTickHit, 0)]
[TestCase(ScoringMode.Standardised, HitResult.SmallTickHit, HitResult.SmallTickHit, 541_894)]
[TestCase(ScoringMode.Standardised, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)]
[TestCase(ScoringMode.Standardised, HitResult.LargeTickHit, HitResult.LargeTickHit, 492_894)]
[TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 1_000_030)]
[TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 1_000_150)]
[TestCase(ScoringMode.Classic, HitResult.Miss, HitResult.Great, 0)]
[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.Meh, HitResult.Great, 4)]
[TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 15)]
[TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 53)]
[TestCase(ScoringMode.Classic, HitResult.Great, HitResult.Great, 140)]
[TestCase(ScoringMode.Classic, HitResult.Perfect, HitResult.Perfect, 140)]
[TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, HitResult.SmallTickHit, 0)]
[TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 11)]
[TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)]
[TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 12)]
[TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 9)]
[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)
@ -113,59 +103,18 @@ namespace osu.Game.Tests.Rulesets.Scoring
{
HitObjects = new List<HitObject>(Enumerable.Repeat(new TestHitObject(maxResult), 4))
};
scoreProcessor.Mode.Value = scoringMode;
scoreProcessor.ApplyBeatmap(fourObjectBeatmap);
for (int i = 0; i < 4; i++)
{
var judgementResult = new JudgementResult(fourObjectBeatmap.HitObjects[i], new Judgement())
var judgementResult = new JudgementResult(fourObjectBeatmap.HitObjects[i], new TestJudgement(maxResult))
{
Type = i == 2 ? minResult : hitResult
};
scoreProcessor.ApplyResult(judgementResult);
}
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(expectedScore).Within(0.5d));
}
/// <remarks>
/// This test uses a beatmap with four small ticks and one object with the <see cref="Judgement.MaxResult"/> of <see cref="HitResult.Ok"/>.
/// Its goal is to ensure that with the <see cref="ScoringMode"/> of <see cref="ScoringMode.Standardised"/>,
/// small ticks contribute to the accuracy portion, but not the combo portion.
/// In contrast, <see cref="ScoringMode.Classic"/> does not have separate combo and accuracy portion (they are multiplied by each other).
/// </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, 34)]
[TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, 30)]
public void TestSmallTicksAccuracy(ScoringMode scoringMode, HitResult hitResult, int expectedScore)
{
IEnumerable<HitObject> hitObjects = Enumerable
.Repeat(new TestHitObject(HitResult.SmallTickHit), 4)
.Append(new TestHitObject(HitResult.Ok));
IBeatmap fiveObjectBeatmap = new TestBeatmap(new RulesetInfo())
{
HitObjects = hitObjects.ToList()
};
scoreProcessor.Mode.Value = scoringMode;
scoreProcessor.ApplyBeatmap(fiveObjectBeatmap);
for (int i = 0; i < 4; i++)
{
var judgementResult = new JudgementResult(fiveObjectBeatmap.HitObjects[i], new Judgement())
{
Type = i == 2 ? HitResult.SmallTickMiss : hitResult
};
scoreProcessor.ApplyResult(judgementResult);
}
var lastJudgementResult = new JudgementResult(fiveObjectBeatmap.HitObjects.Last(), new Judgement())
{
Type = HitResult.Ok
};
scoreProcessor.ApplyResult(lastJudgementResult);
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(expectedScore).Within(0.5d));
Assert.That(scoreProcessor.GetDisplayScore(scoringMode), Is.EqualTo(expectedScore).Within(0.5d));
}
[Test]
@ -173,10 +122,9 @@ namespace osu.Game.Tests.Rulesets.Scoring
[Values(ScoringMode.Standardised, ScoringMode.Classic)]
ScoringMode scoringMode)
{
scoreProcessor.Mode.Value = scoringMode;
scoreProcessor.ApplyBeatmap(new TestBeatmap(new RulesetInfo()));
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
Assert.That(scoreProcessor.GetDisplayScore(scoringMode), Is.Zero);
}
[TestCase(HitResult.IgnoreHit, HitResult.IgnoreMiss)]
@ -294,28 +242,6 @@ namespace osu.Game.Tests.Rulesets.Scoring
Assert.AreEqual(expectedReturnValue, hitResult.IsScorable());
}
[TestCase(HitResult.Perfect, 1_000_000)]
[TestCase(HitResult.SmallTickHit, 1_000_000)]
[TestCase(HitResult.LargeTickHit, 1_000_000)]
[TestCase(HitResult.SmallBonus, 1_000_000 + Judgement.SMALL_BONUS_SCORE)]
[TestCase(HitResult.LargeBonus, 1_000_000 + Judgement.LARGE_BONUS_SCORE)]
public void TestGetScoreWithExternalStatistics(HitResult result, int expectedScore)
{
var statistic = new Dictionary<HitResult, int> { { result, 1 } };
scoreProcessor.ApplyBeatmap(new Beatmap
{
HitObjects = { new TestHitObject(result) }
});
Assert.That(scoreProcessor.ComputeScore(ScoringMode.Standardised, new ScoreInfo
{
Ruleset = new TestRuleset().RulesetInfo,
MaxCombo = result.AffectsCombo() ? 1 : 0,
Statistics = statistic
}), Is.EqualTo(expectedScore).Within(0.5d));
}
#pragma warning disable CS0618
[Test]
public void TestLegacyComboIncrease()
@ -330,29 +256,6 @@ namespace osu.Game.Tests.Rulesets.Scoring
Assert.That(HitResult.LegacyComboIncrease.IsHit(), Is.True);
Assert.That(HitResult.LegacyComboIncrease.IsScorable(), Is.True);
Assert.That(HitResultExtensions.ALL_TYPES, Does.Not.Contain(HitResult.LegacyComboIncrease));
// Cannot be used to apply results.
Assert.Throws<ArgumentException>(() => scoreProcessor.ApplyBeatmap(new Beatmap
{
HitObjects = { new TestHitObject(HitResult.LegacyComboIncrease) }
}));
ScoreInfo testScore = new ScoreInfo
{
MaxCombo = 1,
Statistics = new Dictionary<HitResult, int>
{
{ HitResult.Great, 1 }
},
MaximumStatistics = new Dictionary<HitResult, int>
{
{ HitResult.Great, 1 },
{ HitResult.LegacyComboIncrease, 1 }
}
};
double totalScore = new TestScoreProcessor().ComputeScore(ScoringMode.Standardised, testScore);
Assert.That(totalScore, Is.EqualTo(750_000)); // 500K from accuracy (100%), and 250K from combo (50%).
}
#pragma warning restore CS0618
@ -362,16 +265,24 @@ namespace osu.Game.Tests.Rulesets.Scoring
const int count_judgements = 1000;
const int count_misses = 1;
double actual = new TestScoreProcessor().ComputeAccuracy(new ScoreInfo
beatmap = new TestBeatmap(new RulesetInfo())
{
Statistics = new Dictionary<HitResult, int>
HitObjects = new List<HitObject>(Enumerable.Repeat(new TestHitObject(HitResult.Great), count_judgements))
};
scoreProcessor = new TestScoreProcessor();
scoreProcessor.ApplyBeatmap(beatmap);
for (int i = 0; i < beatmap.HitObjects.Count; i++)
{
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[i], new TestJudgement(HitResult.Great))
{
{ HitResult.Great, count_judgements - count_misses },
{ HitResult.Miss, count_misses }
}
});
Type = i == 0 ? HitResult.Miss : HitResult.Great
});
}
const double expected = (count_judgements - count_misses) / (double)count_judgements;
double actual = scoreProcessor.Accuracy.Value;
Assert.That(actual, Is.Not.EqualTo(0.0));
Assert.That(actual, Is.Not.EqualTo(1.0));
@ -419,14 +330,18 @@ namespace osu.Game.Tests.Rulesets.Scoring
private partial class TestScoreProcessor : ScoreProcessor
{
protected override double DefaultAccuracyPortion => 0.5;
protected override double DefaultComboPortion => 0.5;
public TestScoreProcessor()
: base(new TestRuleset())
{
}
protected override double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion)
{
return 500000 * comboProgress +
500000 * Accuracy.Value * accuracyProgress +
bonusPortion;
}
// ReSharper disable once MemberHidesStaticFromOuterClass
private class TestRuleset : Ruleset
{

View File

@ -21,7 +21,6 @@ using osu.Game.Online.Multiplayer;
using osu.Game.Online.Spectator;
using osu.Game.Replays.Legacy;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Play.HUD;
namespace osu.Game.Tests.Visual.Multiplayer
@ -188,15 +187,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
if (!lastHeaders.TryGetValue(userId, out var header))
{
lastHeaders[userId] = header = new FrameHeader(new ScoreInfo
lastHeaders[userId] = header = new FrameHeader(0, 0, 0, 0, new Dictionary<HitResult, int>
{
Statistics = new Dictionary<HitResult, int>
{
[HitResult.Miss] = 0,
[HitResult.Meh] = 0,
[HitResult.Great] = 0
}
});
[HitResult.Miss] = 0,
[HitResult.Meh] = 0,
[HitResult.Great] = 0
}, new ScoreProcessorStatistics(), DateTimeOffset.Now);
}
switch (RNG.Next(0, 3))