Skip to content

Commit

Permalink
Merge pull request #96 from Syriiin/new-mod-format
Browse files Browse the repository at this point in the history
Implement new mod format
  • Loading branch information
Syriiin authored Nov 12, 2024
2 parents 9a791e3 + 3962838 commit fc97196
Show file tree
Hide file tree
Showing 15 changed files with 312 additions and 100 deletions.
23 changes: 17 additions & 6 deletions Difficalcy.Catch.Tests/CatchCalculatorServiceTest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Difficalcy.Catch.Models;
using Difficalcy.Catch.Services;
using Difficalcy.Models;
using Difficalcy.Services;
using Difficalcy.Tests;

Expand All @@ -10,23 +11,33 @@ public class CatchCalculatorServiceTest : CalculatorServiceTest<CatchScore, Catc
protected override CalculatorService<CatchScore, CatchDifficulty, CatchPerformance, CatchCalculation> CalculatorService { get; } = new CatchCalculatorService(new InMemoryCache(), new TestBeatmapProvider(typeof(CatchCalculatorService).Assembly.GetName().Name));

[Theory]
[InlineData(4.0505463516206195d, 164.5770866821372d, "diffcalc-test", 0)]
[InlineData(5.1696411260785498d, 291.43480971713944d, "diffcalc-test", 64)]
public void Test(double expectedDifficultyTotal, double expectedPerformanceTotal, string beatmapId, int mods)
=> TestGetCalculationReturnsCorrectValues(expectedDifficultyTotal, expectedPerformanceTotal, new CatchScore { BeatmapId = beatmapId, Mods = mods });
[InlineData(4.0505463516206195d, 164.5770866821372d, "diffcalc-test", new string[] { })]
[InlineData(5.1696411260785498d, 291.43480971713944d, "diffcalc-test", new string[] { "DT" })]
public void Test(double expectedDifficultyTotal, double expectedPerformanceTotal, string beatmapId, string[] mods)
=> TestGetCalculationReturnsCorrectValues(expectedDifficultyTotal, expectedPerformanceTotal, new CatchScore { BeatmapId = beatmapId, Mods = mods.Select(m => new Mod { Acronym = m }).ToArray() });

[Fact]
public void TestAllParameters()
{
var score = new CatchScore
{
BeatmapId = "diffcalc-test",
Mods = 80, // HR, DT
Mods = [
new Mod() { Acronym = "HR" },
new Mod()
{
Acronym = "DT",
Settings = new Dictionary<string, string>
{
{ "speed_change", "2" }
}
}
],
Combo = 100,
Misses = 5,
LargeDroplets = 18,
SmallDroplets = 200,
};
TestGetCalculationReturnsCorrectValues(5.739025024925009d, 241.19384779497875d, score);
TestGetCalculationReturnsCorrectValues(6.9017468199992278, 375.74458599075302, score);
}
}
19 changes: 15 additions & 4 deletions Difficalcy.Catch/Services/CatchCalculatorService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
using Difficalcy.Services;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Online.API;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Catch.Difficulty;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using LazerMod = osu.Game.Rulesets.Mods.Mod;

namespace Difficalcy.Catch.Services
{
Expand Down Expand Up @@ -43,13 +45,13 @@ protected override async Task EnsureBeatmap(string beatmapId)
await beatmapProvider.EnsureBeatmap(beatmapId);
}

protected override (object, string) CalculateDifficultyAttributes(string beatmapId, int bitMods)
protected override (object, string) CalculateDifficultyAttributes(string beatmapId, Mod[] mods)
{
var workingBeatmap = GetWorkingBeatmap(beatmapId);
var mods = CatchRuleset.ConvertFromLegacyMods((LegacyMods)bitMods).ToArray();
var lazerMods = mods.Select(ModToLazerMod).ToArray();

var difficultyCalculator = CatchRuleset.CreateDifficultyCalculator(workingBeatmap);
var difficultyAttributes = difficultyCalculator.Calculate(mods) as CatchDifficultyAttributes;
var difficultyAttributes = difficultyCalculator.Calculate(lazerMods) as CatchDifficultyAttributes;

// Serialising anonymous object with same names because some properties can't be serialised, and the built-in JsonProperty fields aren't on all required fields
return (difficultyAttributes, JsonSerializer.Serialize(new
Expand All @@ -70,7 +72,7 @@ protected override CatchCalculation CalculatePerformance(CatchScore score, objec
var catchDifficultyAttributes = (CatchDifficultyAttributes)difficultyAttributes;

var workingBeatmap = GetWorkingBeatmap(score.BeatmapId);
var mods = CatchRuleset.ConvertFromLegacyMods((LegacyMods)score.Mods).ToArray();
var mods = score.Mods.Select(ModToLazerMod).ToArray();
var beatmap = workingBeatmap.GetPlayableBeatmap(CatchRuleset.RulesetInfo, mods);

var combo = score.Combo ?? beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType<JuiceStream>().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet));
Expand Down Expand Up @@ -103,6 +105,15 @@ private CalculatorWorkingBeatmap GetWorkingBeatmap(string beatmapId)
return new CalculatorWorkingBeatmap(CatchRuleset, beatmapStream);
}

private LazerMod ModToLazerMod(Mod mod)
{
var apiMod = new APIMod { Acronym = mod.Acronym };
foreach (var setting in mod.Settings)
apiMod.Settings.Add(setting.Key, setting.Value);

return apiMod.ToMod(CatchRuleset);
}

private static Dictionary<HitResult, int> GetHitResults(IBeatmap beatmap, int countMiss, int? countDroplet, int? countTinyDroplet)
{
var maxTinyDroplets = beatmap.HitObjects.OfType<JuiceStream>().Sum(s => s.NestedHitObjects.OfType<TinyDroplet>().Count());
Expand Down
22 changes: 16 additions & 6 deletions Difficalcy.Mania.Tests/ManiaCalculatorServiceTest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Difficalcy.Mania.Models;
using Difficalcy.Mania.Services;
using Difficalcy.Models;
using Difficalcy.Services;
using Difficalcy.Tests;

Expand All @@ -10,24 +11,33 @@ public class ManiaCalculatorServiceTest : CalculatorServiceTest<ManiaScore, Mani
protected override CalculatorService<ManiaScore, ManiaDifficulty, ManiaPerformance, ManiaCalculation> CalculatorService { get; } = new ManiaCalculatorService(new InMemoryCache(), new TestBeatmapProvider(typeof(ManiaCalculatorService).Assembly.GetName().Name));

[Theory]
[InlineData(2.3493769750220914d, 45.76140071089439d, "diffcalc-test", 0)]
[InlineData(2.797245912537965d, 68.79984443279172d, "diffcalc-test", 64)]
public void Test(double expectedDifficultyTotal, double expectedPerformanceTotal, string beatmapId, int mods)
=> TestGetCalculationReturnsCorrectValues(expectedDifficultyTotal, expectedPerformanceTotal, new ManiaScore { BeatmapId = beatmapId, Mods = mods });
[InlineData(2.3493769750220914d, 45.76140071089439d, "diffcalc-test", new string[] { })]
[InlineData(2.797245912537965d, 68.79984443279172d, "diffcalc-test", new string[] { "DT" })]
public void Test(double expectedDifficultyTotal, double expectedPerformanceTotal, string beatmapId, string[] mods)
=> TestGetCalculationReturnsCorrectValues(expectedDifficultyTotal, expectedPerformanceTotal, new ManiaScore { BeatmapId = beatmapId, Mods = mods.Select(m => new Mod { Acronym = m }).ToArray() });

[Fact]
public void TestAllParameters()
{
var score = new ManiaScore
{
BeatmapId = "diffcalc-test",
Mods = 64, // DT
Mods = [
new Mod()
{
Acronym = "DT",
Settings = new Dictionary<string, string>
{
{ "speed_change", "2" }
}
}
],
Misses = 5,
Mehs = 4,
Oks = 3,
Goods = 2,
Greats = 1,
};
TestGetCalculationReturnsCorrectValues(2.797245912537965d, 43.17076331130473d, score);
TestGetCalculationReturnsCorrectValues(3.3252153148972425, 64.408516282383957, score);
}
}
19 changes: 15 additions & 4 deletions Difficalcy.Mania/Services/ManiaCalculatorService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
using Difficalcy.Models;
using Difficalcy.Services;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Online.API;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Difficulty;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using LazerMod = osu.Game.Rulesets.Mods.Mod;

namespace Difficalcy.Mania.Services
{
Expand Down Expand Up @@ -43,13 +45,13 @@ protected override async Task EnsureBeatmap(string beatmapId)
await _beatmapProvider.EnsureBeatmap(beatmapId);
}

protected override (object, string) CalculateDifficultyAttributes(string beatmapId, int bitMods)
protected override (object, string) CalculateDifficultyAttributes(string beatmapId, Mod[] mods)
{
var workingBeatmap = GetWorkingBeatmap(beatmapId);
var mods = ManiaRuleset.ConvertFromLegacyMods((LegacyMods)bitMods).ToArray();
var lazerMods = mods.Select(ModToLazerMod).ToArray();

var difficultyCalculator = ManiaRuleset.CreateDifficultyCalculator(workingBeatmap);
var difficultyAttributes = difficultyCalculator.Calculate(mods) as ManiaDifficultyAttributes;
var difficultyAttributes = difficultyCalculator.Calculate(lazerMods) as ManiaDifficultyAttributes;

// Serialising anonymous object with same names because some properties can't be serialised, and the built-in JsonProperty fields aren't on all required fields
return (difficultyAttributes, JsonSerializer.Serialize(new
Expand All @@ -69,7 +71,7 @@ protected override ManiaCalculation CalculatePerformance(ManiaScore score, objec
{
var maniaDifficultyAttributes = (ManiaDifficultyAttributes)difficultyAttributes;
var workingBeatmap = GetWorkingBeatmap(score.BeatmapId);
var mods = ManiaRuleset.ConvertFromLegacyMods((LegacyMods)score.Mods).ToArray();
var mods = score.Mods.Select(ModToLazerMod).ToArray();
var beatmap = workingBeatmap.GetPlayableBeatmap(ManiaRuleset.RulesetInfo, mods);

var hitObjectCount = beatmap.HitObjects.Count;
Expand Down Expand Up @@ -102,6 +104,15 @@ private CalculatorWorkingBeatmap GetWorkingBeatmap(string beatmapId)
return new CalculatorWorkingBeatmap(ManiaRuleset, beatmapStream);
}

private LazerMod ModToLazerMod(Mod mod)
{
var apiMod = new APIMod { Acronym = mod.Acronym };
foreach (var setting in mod.Settings)
apiMod.Settings.Add(setting.Key, setting.Value);

return apiMod.ToMod(ManiaRuleset);
}

private static Dictionary<HitResult, int> GetHitResults(int hitResultCount, int countMiss, int countMeh, int countOk, int countGood, int countGreat)
{
var countPerfect = hitResultCount - (countMiss + countMeh + countOk + countGood + countGreat);
Expand Down
38 changes: 32 additions & 6 deletions Difficalcy.Osu.Tests/OsuCalculatorServiceTest.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Difficalcy.Models;
using Difficalcy.Osu.Models;
using Difficalcy.Osu.Services;
using Difficalcy.Services;
Expand All @@ -10,23 +11,48 @@ public class OsuCalculatorServiceTest : CalculatorServiceTest<OsuScore, OsuDiffi
protected override CalculatorService<OsuScore, OsuDifficulty, OsuPerformance, OsuCalculation> CalculatorService { get; } = new OsuCalculatorService(new InMemoryCache(), new TestBeatmapProvider(typeof(OsuCalculatorService).Assembly.GetName().Name));

[Theory]
[InlineData(6.7171144000821119d, 291.34799376682508d, "diffcalc-test", 0)]
[InlineData(8.9825709931204205d, 717.13844713272601d, "diffcalc-test", 64)]
public void Test(double expectedDifficultyTotal, double expectedPerformanceTotal, string beatmapId, int mods)
=> TestGetCalculationReturnsCorrectValues(expectedDifficultyTotal, expectedPerformanceTotal, new OsuScore { BeatmapId = beatmapId, Mods = mods });
[InlineData(6.7171144000821119d, 291.34799376682508d, "diffcalc-test", new string[] { })]
[InlineData(8.9825709931204205d, 717.13844713272601d, "diffcalc-test", new string[] { "DT" })]
public void Test(double expectedDifficultyTotal, double expectedPerformanceTotal, string beatmapId, string[] mods)
=> TestGetCalculationReturnsCorrectValues(expectedDifficultyTotal, expectedPerformanceTotal, new OsuScore { BeatmapId = beatmapId, Mods = mods.Select(m => new Mod { Acronym = m }).ToArray() });

[Fact]
public void TestAllParameters()
{
var score = new OsuScore
{
BeatmapId = "diffcalc-test",
Mods = 1112, // HD, HR, DT, FL
Mods = [
new Mod() { Acronym = "HD" },
new Mod() { Acronym = "HR" },
new Mod()
{
Acronym = "DT",
Settings = new Dictionary<string, string>
{
{ "speed_change", "2" }
}
},
new Mod() { Acronym = "FL" }
],
Combo = 200,
Misses = 5,
Mehs = 4,
Oks = 3,
};
TestGetCalculationReturnsCorrectValues(10.095171949076231d, 685.8314990408466d, score);
TestGetCalculationReturnsCorrectValues(12.418442356371395, 1415.202990027042, score);
}

[Fact]
public void TestClassicMod()
{
var score = new OsuScore
{
BeatmapId = "diffcalc-test",
Mods = [
new Mod() { Acronym = "CL" }
],
};
TestGetCalculationReturnsCorrectValues(6.7171144000821119d, 289.16416504218972, score);
}
}
20 changes: 15 additions & 5 deletions Difficalcy.Osu/Services/OsuCalculatorService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
using Difficalcy.Models;
using Difficalcy.Osu.Models;
using Difficalcy.Services;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Online.API;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Difficulty;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using LazerMod = osu.Game.Rulesets.Mods.Mod;

namespace Difficalcy.Osu.Services
{
Expand Down Expand Up @@ -41,13 +42,13 @@ protected override async Task EnsureBeatmap(string beatmapId)
await beatmapProvider.EnsureBeatmap(beatmapId);
}

protected override (object, string) CalculateDifficultyAttributes(string beatmapId, int bitMods)
protected override (object, string) CalculateDifficultyAttributes(string beatmapId, Mod[] mods)
{
var workingBeatmap = GetWorkingBeatmap(beatmapId);
var mods = OsuRuleset.ConvertFromLegacyMods((LegacyMods)bitMods).ToArray();
var lazerMods = mods.Select(ModToLazerMod).ToArray();

var difficultyCalculator = OsuRuleset.CreateDifficultyCalculator(workingBeatmap);
var difficultyAttributes = difficultyCalculator.Calculate(mods) as OsuDifficultyAttributes;
var difficultyAttributes = difficultyCalculator.Calculate(lazerMods) as OsuDifficultyAttributes;

// Serialising anonymous object with same names because some properties can't be serialised, and the built-in JsonProperty fields aren't on all required fields
return (difficultyAttributes, JsonSerializer.Serialize(new
Expand Down Expand Up @@ -80,7 +81,7 @@ protected override OsuCalculation CalculatePerformance(OsuScore score, object di
var osuDifficultyAttributes = (OsuDifficultyAttributes)difficultyAttributes;

var workingBeatmap = GetWorkingBeatmap(score.BeatmapId);
var mods = OsuRuleset.ConvertFromLegacyMods((LegacyMods)score.Mods).ToArray();
var mods = score.Mods.Select(ModToLazerMod).ToArray();
var beatmap = workingBeatmap.GetPlayableBeatmap(OsuRuleset.RulesetInfo, mods);

var combo = score.Combo ?? beatmap.HitObjects.Count + beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
Expand Down Expand Up @@ -113,6 +114,15 @@ private CalculatorWorkingBeatmap GetWorkingBeatmap(string beatmapId)
return new CalculatorWorkingBeatmap(OsuRuleset, beatmapStream);
}

private LazerMod ModToLazerMod(Mod mod)
{
var apiMod = new APIMod { Acronym = mod.Acronym };
foreach (var setting in mod.Settings)
apiMod.Settings.Add(setting.Key, setting.Value);

return apiMod.ToMod(OsuRuleset);
}

private static Dictionary<HitResult, int> GetHitResults(int hitResultCount, int countMiss, int countMeh, int countOk)
{
var countGreat = hitResultCount - countOk - countMeh - countMiss;
Expand Down
23 changes: 17 additions & 6 deletions Difficalcy.Taiko.Tests/TaikoCalculatorServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Difficalcy.Taiko.Services;
using Difficalcy.Services;
using Difficalcy.Tests;
using Difficalcy.Models;

namespace Difficalcy.Taiko.Tests;

Expand All @@ -10,22 +11,32 @@ public class TaikoCalculatorServiceTest : CalculatorServiceTest<TaikoScore, Taik
protected override CalculatorService<TaikoScore, TaikoDifficulty, TaikoPerformance, TaikoCalculation> CalculatorService { get; } = new TaikoCalculatorService(new InMemoryCache(), new TestBeatmapProvider(typeof(TaikoCalculatorService).Assembly.GetName().Name));

[Theory]
[InlineData(3.092021259435121d, 137.80325540434842d, "diffcalc-test", 0)]
[InlineData(4.0789820318081444d, 248.8310568362074d, "diffcalc-test", 64)]
public void Test(double expectedDifficultyTotal, double expectedPerformanceTotal, string beatmapId, int mods)
=> TestGetCalculationReturnsCorrectValues(expectedDifficultyTotal, expectedPerformanceTotal, new TaikoScore { BeatmapId = beatmapId, Mods = mods });
[InlineData(3.092021259435121d, 137.80325540434842d, "diffcalc-test", new string[] { })]
[InlineData(4.0789820318081444d, 248.8310568362074d, "diffcalc-test", new string[] { "DT" })]
public void Test(double expectedDifficultyTotal, double expectedPerformanceTotal, string beatmapId, string[] mods)
=> TestGetCalculationReturnsCorrectValues(expectedDifficultyTotal, expectedPerformanceTotal, new TaikoScore { BeatmapId = beatmapId, Mods = mods.Select(m => new Mod { Acronym = m }).ToArray() });

[Fact]
public void TestAllParameters()
{
var score = new TaikoScore
{
BeatmapId = "diffcalc-test",
Mods = 80, // HR, DT
Mods = [
new Mod() { Acronym = "HR" },
new Mod()
{
Acronym = "DT",
Settings = new Dictionary<string, string>
{
{ "speed_change", "2" }
}
}
],
Combo = 150,
Misses = 5,
Oks = 3,
};
TestGetCalculationReturnsCorrectValues(4.0789820318081444d, 240.24516772998618d, score);
TestGetCalculationReturnsCorrectValues(4.922364692298034, 359.95282202016443, score);
}
}
Loading

0 comments on commit fc97196

Please sign in to comment.