Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ public EmptyFreeformDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap b
{
}

protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills)
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, IReadOnlyList<ISkillAttributes> skills)
{
return new DifficultyAttributes(mods, 0);
}

protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods) => Enumerable.Empty<DifficultyHitObject>();

protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => Array.Empty<Skill>();
protected override ISkill[] CreateSkills(IBeatmap beatmap, Mod[] mods, DifficultyHitObject[] difficultyHitObjects) => Array.Empty<ISkill>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ public PippidonDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatma
{
}

protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills)
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, IReadOnlyList<ISkillAttributes> skills)
{
return new DifficultyAttributes(mods, 0);
}

protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods) => Enumerable.Empty<DifficultyHitObject>();

protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => Array.Empty<Skill>();
protected override ISkill[] CreateSkills(IBeatmap beatmap, Mod[] mods, DifficultyHitObject[] difficultyHitObjects) => Array.Empty<ISkill>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ public EmptyScrollingDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap
{
}

protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills)
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, IReadOnlyList<ISkillAttributes> skills)
{
return new DifficultyAttributes(mods, 0);
}

protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods) => Enumerable.Empty<DifficultyHitObject>();

protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => Array.Empty<Skill>();
protected override ISkill[] CreateSkills(IBeatmap beatmap, Mod[] mods, DifficultyHitObject[] difficultyHitObjects) => Array.Empty<ISkill>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ public PippidonDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatma
{
}

protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills)
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, IReadOnlyList<ISkillAttributes> skills)
{
return new DifficultyAttributes(mods, 0);
}

protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods) => Enumerable.Empty<DifficultyHitObject>();

protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => Array.Empty<Skill>();
protected override ISkill[] CreateSkills(IBeatmap beatmap, Mod[] mods, DifficultyHitObject[] difficultyHitObjects) => Array.Empty<ISkill>();
}
}
12 changes: 12 additions & 0 deletions osu.Game.Benchmarks/BenchmarkDifficultyCalculation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ private WorkingBeatmap readBeatmap(ZipArchiveReader archiveReader, string beatma
[Benchmark]
public void CalculateDifficultyMania() => maniaCalculator.Calculate();

[Benchmark]
public void CalculateTimedDifficultyOsu() => osuCalculator.CalculateTimed();

[Benchmark]
public void CalculateTimedDifficultyTaiko() => taikoCalculator.CalculateTimed();

[Benchmark]
public void CalculateTimedDifficultyCatch() => catchCalculator.CalculateTimed();

[Benchmark]
public void CalculateTimedDifficultyMania() => maniaCalculator.CalculateTimed();

[Benchmark]
public void CalculateDifficultyOsuHundredTimes()
{
Expand Down
11 changes: 5 additions & 6 deletions osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
Expand All @@ -30,14 +29,14 @@ public CatchDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
{
}

protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills)
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, IReadOnlyList<ISkillAttributes> skillAttributes)
{
if (beatmap.HitObjects.Count == 0)
return new CatchDifficultyAttributes { Mods = mods };

CatchDifficultyAttributes attributes = new CatchDifficultyAttributes
{
StarRating = Math.Sqrt(skills.OfType<Movement>().Single().DifficultyValue()) * difficulty_multiplier,
StarRating = Math.Sqrt(skillAttributes[0].Difficulty) * difficulty_multiplier,
Mods = mods,
MaxCombo = beatmap.GetMaxCombo(),
};
Expand Down Expand Up @@ -74,11 +73,11 @@ protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(I
return objects;
}

protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods)
protected override ISkill[] CreateSkills(IBeatmap beatmap, Mod[] mods, DifficultyHitObject[] difficultyHitObjects)
{
return new Skill[]
return new ISkill[]
{
new Movement(mods),
new Movement(mods, difficultyHitObjects),
};
}

Expand Down
4 changes: 2 additions & 2 deletions osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ public class Movement : StrainDecaySkill

protected override int SectionLength => 750;

public Movement(Mod[] mods)
: base(mods)
public Movement(Mod[] mods, DifficultyHitObject[] difficultyHitObjects)
: base(mods, difficultyHitObjects)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public ManiaDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.MatchesOnlineID(ruleset);
}

protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills)
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, IReadOnlyList<ISkillAttributes> skillAttributes)
{
if (beatmap.HitObjects.Count == 0)
return new ManiaDifficultyAttributes { Mods = mods };
Expand All @@ -47,7 +47,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat

ManiaDifficultyAttributes attributes = new ManiaDifficultyAttributes
{
StarRating = skills.OfType<Strain>().Single().DifficultyValue() * difficulty_multiplier,
StarRating = skillAttributes[0].Difficulty * difficulty_multiplier,
Mods = mods,
MaxCombo = beatmap.HitObjects.Sum(maxComboForObject),
};
Expand Down Expand Up @@ -91,9 +91,9 @@ protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(I
// Sorting is done in CreateDifficultyHitObjects, since the full list of hitobjects is required.
protected override IEnumerable<DifficultyHitObject> SortObjects(IEnumerable<DifficultyHitObject> input) => input;

protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
protected override ISkill[] CreateSkills(IBeatmap beatmap, Mod[] mods, DifficultyHitObject[] difficultyHitObjects) => new ISkill[]
{
new Strain(mods, ((ManiaBeatmap)Beatmap).TotalColumns)
new Strain(mods, difficultyHitObjects, ((ManiaBeatmap)Beatmap).TotalColumns)
};

protected override Mod[] DifficultyAdjustmentMods
Expand Down
4 changes: 2 additions & 2 deletions osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ public class Strain : StrainDecaySkill
private double highestIndividualStrain;
private double overallStrain;

public Strain(Mod[] mods, int totalColumns)
: base(mods)
public Strain(Mod[] mods, DifficultyHitObject[] difficultyHitObjects, int totalColumns)
: base(mods, difficultyHitObjects)
{
individualStrains = new double[totalColumns];
overallStrain = 1;
Expand Down
8 changes: 8 additions & 0 deletions osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ public class OsuDifficultyCalculatorTest : DifficultyCalculatorTest
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
=> base.Test(expectedStarRating, expectedMaxCombo, name);

[TestCase(6.5243170265483581d, 239, "diffcalc-test")]
[TestCase(1.3280410795791415d, 54, "zero-length-sliders")]
[TestCase(0.40867325147697559d, 4, "very-fast-slider")]
[TestCase(0.87058175794353554d, 6, "nan-slider")]
[TestCase(6.3059767387139756d, 2359, "801165")] // real world test
public void TestTimed(double expectedStarRating, int expectedMaxCombo, string name)
=> base.TestTimed(expectedStarRating, expectedMaxCombo, name);

[TestCase(9.4677607900646308d, 239, "diffcalc-test")]
[TestCase(1.6856612715618886d, 54, "zero-length-sliders")]
[TestCase(0.53588473186572561d, 4, "very-fast-slider")]
Expand Down
72 changes: 27 additions & 45 deletions osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,58 +28,40 @@ public OsuDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
{
}

protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills)
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, IReadOnlyList<ISkillAttributes> skillAttributes)
{
if (beatmap.HitObjects.Count == 0)
return new OsuDifficultyAttributes { Mods = mods };

var aim = skills.OfType<Aim>().Single(a => a.IncludeSliders);
var aimWithoutSliders = skills.OfType<Aim>().Single(a => !a.IncludeSliders);
var speed = skills.OfType<Speed>().Single();
var flashlight = skills.OfType<Flashlight>().SingleOrDefault();
var reading = skills.OfType<Reading>().Single();
var aimAttributes = skillAttributes.OfType<AimAttributes>().Single(a => a.WithSliders);
var aimWithoutSlidersAttributes = skillAttributes.OfType<AimAttributes>().Single(a => !a.WithSliders);
var speedAttributes = skillAttributes.OfType<SpeedAttributes>().Single();
var flashlightAttributes = skillAttributes.OfType<FlashlightAttributes>().SingleOrDefault();
var readingAttributes = skillAttributes.OfType<ReadingAttributes>().Single();

double aimDifficultyValue = aim.DifficultyValue();
double aimNoSlidersDifficultyValue = aimWithoutSliders.DifficultyValue();
double speedDifficultyValue = speed.DifficultyValue();
double readingDifficultyValue = reading.DifficultyValue();

double aimDifficultStrainCount = aim.CountTopWeightedStrains(aimDifficultyValue);
double speedDifficultStrainCount = speed.CountTopWeightedObjectDifficulties(speedDifficultyValue);
double readingDifficultNoteCount = reading.CountTopWeightedObjectDifficulties(readingDifficultyValue);

double speedNotes = speed.RelevantObjectCount();

double aimNoSlidersTopWeightedSliderCount = aimWithoutSliders.CountTopWeightedSliders(aimNoSlidersDifficultyValue);
double aimNoSlidersDifficultStrainCount = aimWithoutSliders.CountTopWeightedStrains(aimNoSlidersDifficultyValue);

double aimTopWeightedSliderFactor = aimNoSlidersTopWeightedSliderCount / Math.Max(1, aimNoSlidersDifficultStrainCount - aimNoSlidersTopWeightedSliderCount);

double speedTopWeightedSliderCount = speed.CountTopWeightedSliders(speedDifficultyValue);
double speedTopWeightedSliderFactor = speedTopWeightedSliderCount / Math.Max(1, speedDifficultStrainCount - speedTopWeightedSliderCount);

double difficultSliders = aim.GetDifficultSliders();
double aimTopWeightedSliderFactor = aimWithoutSlidersAttributes.TopWeightedSlidersCount / Math.Max(1, aimWithoutSlidersAttributes.TopWeightedStrainsCount - aimWithoutSlidersAttributes.TopWeightedSlidersCount);
double speedTopWeightedSliderFactor = speedAttributes.TopWeightedSlidersCount / Math.Max(1, speedAttributes.TopWeightedObjectDifficultiesCount - speedAttributes.TopWeightedSlidersCount);

int hitCircleCount = beatmap.HitObjects.Count(h => h is HitCircle);
int sliderCount = beatmap.HitObjects.Count(h => h is Slider);
int spinnerCount = beatmap.HitObjects.Count(h => h is Spinner);

int totalHits = beatmap.HitObjects.Count;

double aimRating = calculateAimDifficultyRating(aimDifficultyValue);
double aimNoSlidersRating = calculateAimDifficultyRating(aimNoSlidersDifficultyValue);
double aimRating = calculateAimDifficultyRating(aimAttributes.Difficulty);
double aimNoSlidersRating = calculateAimDifficultyRating(aimWithoutSlidersAttributes.Difficulty);

double sliderFactor = aimDifficultyValue > 0
double sliderFactor = aimAttributes.Difficulty > 0
? aimNoSlidersRating / aimRating
: 1;

double speedRating = calculateDifficultyRating(speedDifficultyValue);
double readingRating = calculateDifficultyRating(readingDifficultyValue);
double speedRating = calculateDifficultyRating(speedAttributes.Difficulty);
double readingRating = calculateDifficultyRating(readingAttributes.Difficulty);

double flashlightRating = 0.0;

if (flashlight is not null)
flashlightRating = calculateDifficultyRating(flashlight.DifficultyValue());
if (flashlightAttributes is not null)
flashlightRating = calculateDifficultyRating(flashlightAttributes.Difficulty);

double sliderNestedScorePerObject = LegacyScoreUtils.CalculateNestedScorePerObject(beatmap, totalHits);
double legacyScoreBaseMultiplier = LegacyScoreUtils.CalculateDifficultyPeppyStars(WorkingBeatmap.Beatmap);
Expand All @@ -102,15 +84,15 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat
StarRating = starRating,
Mods = mods,
AimDifficulty = aimRating,
AimDifficultSliderCount = difficultSliders,
AimDifficultSliderCount = aimAttributes.DifficultSlidersCount,
SpeedDifficulty = speedRating,
SpeedNoteCount = speedNotes,
SpeedNoteCount = speedAttributes.RelevantObjectCount,
FlashlightDifficulty = flashlightRating,
ReadingDifficulty = readingRating,
SliderFactor = sliderFactor,
AimDifficultStrainCount = aimDifficultStrainCount,
SpeedDifficultStrainCount = speedDifficultStrainCount,
ReadingDifficultNoteCount = readingDifficultNoteCount,
AimDifficultStrainCount = aimAttributes.TopWeightedStrainsCount,
SpeedDifficultStrainCount = speedAttributes.TopWeightedObjectDifficultiesCount,
ReadingDifficultNoteCount = readingAttributes.TopWeightedObjectDifficultiesCount,
AimTopWeightedSliderFactor = aimTopWeightedSliderFactor,
SpeedTopWeightedSliderFactor = speedTopWeightedSliderFactor,
MaxCombo = beatmap.GetMaxCombo(),
Expand Down Expand Up @@ -162,18 +144,18 @@ protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(I
return objects;
}

protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods)
protected override ISkill[] CreateSkills(IBeatmap beatmap, Mod[] mods, DifficultyHitObject[] difficultyHitObjects)
{
var skills = new List<Skill>
var skills = new List<ISkill>
{
new Aim(mods, true),
new Aim(mods, false),
new Speed(mods),
new Reading(mods)
new Aim(mods, difficultyHitObjects, true),
new Aim(mods, difficultyHitObjects, false),
new Speed(mods, difficultyHitObjects),
new Reading(mods, difficultyHitObjects)
};

if (mods.Any(h => h is OsuModFlashlight))
skills.Add(new Flashlight(mods, beatmap.HitObjects.Count));
skills.Add(new Flashlight(mods, difficultyHitObjects, beatmap.HitObjects.Count));

return skills.ToArray();
}
Expand Down
Loading
Loading