mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 15:32:54 +08:00
Merge pull request #16575 from hlysine/osu-diff-calc-max-combo
Fix max combo calculation in osu diffcalc and add tests
This commit is contained in:
commit
9bd7ccb1ce
@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
||||
|
||||
[TestCase(4.0505463516206195d, "diffcalc-test")]
|
||||
public void Test(double expected, string name)
|
||||
=> base.Test(expected, name);
|
||||
[TestCase(4.0505463516206195d, 127, "diffcalc-test")]
|
||||
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||
|
||||
[TestCase(5.1696411260785498d, "diffcalc-test")]
|
||||
public void TestClockRateAdjusted(double expected, string name)
|
||||
=> Test(expected, name, new CatchModDoubleTime());
|
||||
[TestCase(5.1696411260785498d, 127, "diffcalc-test")]
|
||||
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> Test(expectedStarRating, expectedMaxCombo, name, new CatchModDoubleTime());
|
||||
|
||||
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset().RulesetInfo, beatmap);
|
||||
|
||||
|
@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
||||
|
||||
[TestCase(2.3449735700206298d, "diffcalc-test")]
|
||||
public void Test(double expected, string name)
|
||||
=> base.Test(expected, name);
|
||||
[TestCase(2.3449735700206298d, 151, "diffcalc-test")]
|
||||
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||
|
||||
[TestCase(2.7879104989252959d, "diffcalc-test")]
|
||||
public void TestClockRateAdjusted(double expected, string name)
|
||||
=> Test(expected, name, new ManiaModDoubleTime());
|
||||
[TestCase(2.7879104989252959d, 151, "diffcalc-test")]
|
||||
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> Test(expectedStarRating, expectedMaxCombo, name, new ManiaModDoubleTime());
|
||||
|
||||
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset().RulesetInfo, beatmap);
|
||||
|
||||
|
@ -15,15 +15,20 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||
|
||||
[TestCase(6.6972307565739273d, "diffcalc-test")]
|
||||
[TestCase(1.4484754139145539d, "zero-length-sliders")]
|
||||
public void Test(double expected, string name)
|
||||
=> base.Test(expected, name);
|
||||
[TestCase(6.6972307565739273d, 206, "diffcalc-test")]
|
||||
[TestCase(1.4484754139145539d, 45, "zero-length-sliders")]
|
||||
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||
|
||||
[TestCase(8.9382559208689809d, "diffcalc-test")]
|
||||
[TestCase(1.7548875851757628d, "zero-length-sliders")]
|
||||
public void TestClockRateAdjusted(double expected, string name)
|
||||
=> Test(expected, name, new OsuModDoubleTime());
|
||||
[TestCase(8.9382559208689809d, 206, "diffcalc-test")]
|
||||
[TestCase(1.7548875851757628d, 45, "zero-length-sliders")]
|
||||
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime());
|
||||
|
||||
[TestCase(6.6972307218715166d, 239, "diffcalc-test")]
|
||||
[TestCase(1.4484754139145537d, 54, "zero-length-sliders")]
|
||||
public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic());
|
||||
|
||||
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset().RulesetInfo, beatmap);
|
||||
|
||||
|
@ -61,10 +61,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||
double drainRate = beatmap.Difficulty.DrainRate;
|
||||
|
||||
int maxCombo = beatmap.HitObjects.Count;
|
||||
// Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
|
||||
maxCombo += beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
|
||||
int maxCombo = beatmap.GetMaxCombo();
|
||||
|
||||
int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle);
|
||||
int sliderCount = beatmap.HitObjects.Count(h => h is Slider);
|
||||
|
@ -14,15 +14,15 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
|
||||
|
||||
[TestCase(2.2420075288523802d, "diffcalc-test")]
|
||||
[TestCase(2.2420075288523802d, "diffcalc-test-strong")]
|
||||
public void Test(double expected, string name)
|
||||
=> base.Test(expected, name);
|
||||
[TestCase(2.2420075288523802d, 200, "diffcalc-test")]
|
||||
[TestCase(2.2420075288523802d, 200, "diffcalc-test-strong")]
|
||||
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||
|
||||
[TestCase(3.134084469440479d, "diffcalc-test")]
|
||||
[TestCase(3.134084469440479d, "diffcalc-test-strong")]
|
||||
public void TestClockRateAdjusted(double expected, string name)
|
||||
=> Test(expected, name, new TaikoModDoubleTime());
|
||||
[TestCase(3.134084469440479d, 200, "diffcalc-test")]
|
||||
[TestCase(3.134084469440479d, 200, "diffcalc-test-strong")]
|
||||
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> Test(expectedStarRating, expectedMaxCombo, name, new TaikoModDoubleTime());
|
||||
|
||||
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset().RulesetInfo, beatmap);
|
||||
|
||||
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
@ -70,4 +71,27 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
new IReadOnlyList<T> HitObjects { get; }
|
||||
}
|
||||
|
||||
public static class BeatmapExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Finds the maximum achievable combo by hitting all <see cref="HitObject"/>s in a beatmap.
|
||||
/// </summary>
|
||||
public static int GetMaxCombo(this IBeatmap beatmap)
|
||||
{
|
||||
int combo = 0;
|
||||
foreach (var h in beatmap.HitObjects)
|
||||
addCombo(h, ref combo);
|
||||
return combo;
|
||||
|
||||
static void addCombo(HitObject hitObject, ref int combo)
|
||||
{
|
||||
if (hitObject.CreateJudgement().MaxResult.AffectsCombo())
|
||||
combo++;
|
||||
|
||||
foreach (var nested in hitObject.NestedHitObjects)
|
||||
addCombo(nested, ref combo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
@ -119,15 +120,23 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
/// <summary>
|
||||
/// Calculates the difficulty of the beatmap using all mod combinations applicable to the beatmap.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This can only be used to compute difficulties for legacy mod combinations.
|
||||
/// </remarks>
|
||||
/// <returns>A collection of structures describing the difficulty of the beatmap for each mod combination.</returns>
|
||||
public IEnumerable<DifficultyAttributes> CalculateAll(CancellationToken cancellationToken = default)
|
||||
public IEnumerable<DifficultyAttributes> CalculateAllLegacyCombinations(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var rulesetInstance = ruleset.CreateInstance();
|
||||
|
||||
foreach (var combination in CreateDifficultyAdjustmentModCombinations())
|
||||
{
|
||||
if (combination is MultiMod multi)
|
||||
yield return Calculate(multi.Mods, cancellationToken);
|
||||
else
|
||||
yield return Calculate(combination.Yield(), cancellationToken);
|
||||
Mod classicMod = rulesetInstance.CreateAllMods().SingleOrDefault(m => m is ModClassic);
|
||||
|
||||
var finalCombination = ModUtils.FlattenMod(combination);
|
||||
if (classicMod != null)
|
||||
finalCombination = finalCombination.Append(classicMod);
|
||||
|
||||
yield return Calculate(finalCombination.ToArray(), cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,10 +22,13 @@ namespace osu.Game.Tests.Beatmaps
|
||||
|
||||
protected abstract string ResourceAssembly { get; }
|
||||
|
||||
protected void Test(double expected, string name, params Mod[] mods)
|
||||
protected void Test(double expectedStarRating, int expectedMaxCombo, string name, params Mod[] mods)
|
||||
{
|
||||
var attributes = CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods);
|
||||
|
||||
// Platform-dependent math functions (Pow, Cbrt, Exp, etc) may result in minute differences.
|
||||
Assert.That(CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods).StarRating, Is.EqualTo(expected).Within(0.00001));
|
||||
Assert.That(attributes.StarRating, Is.EqualTo(expectedStarRating).Within(0.00001));
|
||||
Assert.That(attributes.MaxCombo, Is.EqualTo(expectedMaxCombo));
|
||||
}
|
||||
|
||||
private IWorkingBeatmap getBeatmap(string name)
|
||||
|
Loading…
Reference in New Issue
Block a user