From 6dbaf0a03084e07ec8c287c7d739bee4453384b2 Mon Sep 17 00:00:00 2001 From: Jay L Date: Thu, 9 Jun 2022 19:22:55 +1000 Subject: [PATCH] Refactor --- .../Difficulty/Evaluators/ColourEvaluator.cs | 7 ++- .../Difficulty/Evaluators/StaminaEvaluator.cs | 1 + .../Preprocessing/TaikoDifficultyHitObject.cs | 23 +++++----- .../TaikoDifficultyHitObjectColour.cs | 44 +++++++++---------- .../Difficulty/Skills/Colour.cs | 3 -- .../Skills/{CombinedStrain.cs => Peaks.cs} | 16 ++++--- .../Difficulty/Skills/Stamina.cs | 2 - .../Difficulty/TaikoDifficultyAttributes.cs | 9 ++-- .../Difficulty/TaikoDifficultyCalculator.cs | 16 ++++--- .../Difficulty/TaikoPerformanceCalculator.cs | 28 ++++++------ 10 files changed, 76 insertions(+), 73 deletions(-) rename osu.Game.Rulesets.Taiko/Difficulty/Skills/{CombinedStrain.cs => Peaks.cs} (94%) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 2ecef3690b..01410af459 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -6,6 +6,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators { public class ColourEvaluator { + // TODO - Share this sigmoid private static double sigmoid(double val, double center, double width) { return Math.Tanh(Math.E * -(val - center) / width); @@ -16,7 +17,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators TaikoDifficultyHitObject taikoCurrent = (TaikoDifficultyHitObject)current; TaikoDifficultyHitObjectColour colour = taikoCurrent.Colour; if (colour == null) return 0; + double objectStrain = 1.8; + if (colour.Delta) { objectStrain *= sigmoid(colour.DeltaRunLength, 6, 4) * 0.5 + 0.5; @@ -25,9 +28,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators { objectStrain *= sigmoid(colour.DeltaRunLength, 2, 2) * 0.5 + 0.5; } + objectStrain *= -sigmoid(colour.RepetitionInterval, 8, 8) * 0.5 + 0.5; - // Console.WriteLine($"{current.StartTime},{colour.GetHashCode()},{colour.Delta},{colour.DeltaRunLength},{colour.RepetitionInterval},{objectStrain}"); return objectStrain; } } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs index 0c33a952a5..6c0c01cb2d 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -33,6 +33,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators // Find the previous hit object hit by the current key, which is two notes of the same colour prior. TaikoDifficultyHitObject taikoCurrent = (TaikoDifficultyHitObject)current; TaikoDifficultyHitObject keyPrevious = taikoCurrent.PreviousMono(1); + if (keyPrevious == null) { // There is no previous hit object hit by the current key diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index 537eafe396..54e314f722 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -53,8 +53,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// TODO: This argument list is getting long, we might want to refactor this into a static method that create /// all s from a . public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate, - List objects, List centreHitObjects, List rimHitObjects, - List noteObjects, int position) + List objects, + List centreHitObjects, + List rimHitObjects, + List noteObjects, int position) : base(hitObject, lastObject, clockRate, objects, position) { var currentHit = hitObject as Hit; @@ -65,26 +67,25 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing if (HitType == Objects.HitType.Centre) { - MonoPosition = centreHitObjects.Count(); + MonoPosition = centreHitObjects.Count; centreHitObjects.Add(this); monoDifficultyHitObjects = centreHitObjects; } else if (HitType == Objects.HitType.Rim) { - MonoPosition = rimHitObjects.Count(); + MonoPosition = rimHitObjects.Count; rimHitObjects.Add(this); monoDifficultyHitObjects = rimHitObjects; } // Need to be done after HitType is set. - if (HitType != null) - { - this.NotePosition = noteObjects.Count(); - noteObjects.Add(this); + if (HitType == null) return; - // Need to be done after NotePosition is set. - Colour = TaikoDifficultyHitObjectColour.GetInstanceFor(this); - } + NotePosition = noteObjects.Count; + noteObjects.Add(this); + + // Need to be done after NotePosition is set. + Colour = TaikoDifficultyHitObjectColour.GetInstanceFor(this); } /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs index f9a586f877..a5ca0964df 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs @@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public class TaikoDifficultyHitObjectColour { - const int max_repetition_interval = 16; + private const int max_repetition_interval = 16; private TaikoDifficultyHitObjectColour previous; @@ -38,54 +38,54 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing TaikoDifficultyHitObject lastObject = hitObject.PreviousNote(0); TaikoDifficultyHitObjectColour previous = lastObject?.Colour; bool delta = lastObject == null || hitObject.HitType != lastObject.HitType; + if (previous != null && delta == previous.Delta) { previous.DeltaRunLength += 1; return previous; } - else - { - // Calculate RepetitionInterval for previous - previous?.FindRepetitionInterval(); - return new TaikoDifficultyHitObjectColour() - { - Delta = delta, - DeltaRunLength = 1, - RepetitionInterval = max_repetition_interval + 1, - previous = previous - }; - } + // Calculate RepetitionInterval for previous + previous?.FindRepetitionInterval(); + + return new TaikoDifficultyHitObjectColour() + { + Delta = delta, + DeltaRunLength = 1, + RepetitionInterval = max_repetition_interval + 1, + previous = previous + }; } /// - /// Finds the closest previous that has the identical delta value + /// Finds the closest previous that has the identical delta value /// and run length with the current instance, and returns the amount of notes between them. /// public void FindRepetitionInterval() { - if (this.previous == null || this.previous.previous == null) + if (previous?.previous == null) { - this.RepetitionInterval = max_repetition_interval + 1; + RepetitionInterval = max_repetition_interval + 1; return; } + int interval = previous.DeltaRunLength; + TaikoDifficultyHitObjectColour other = previous.previous; - int interval = this.previous.DeltaRunLength; - TaikoDifficultyHitObjectColour other = this.previous.previous; while (other != null && interval < max_repetition_interval) { interval += other.DeltaRunLength; - if (other.Delta == this.Delta && other.DeltaRunLength == this.DeltaRunLength) + + if (other.Delta == Delta && other.DeltaRunLength == DeltaRunLength) { - this.RepetitionInterval = Math.Min(interval, max_repetition_interval); + RepetitionInterval = Math.Min(interval, max_repetition_interval); return; } other = other.previous; } - this.RepetitionInterval = max_repetition_interval + 1; + RepetitionInterval = max_repetition_interval + 1; } } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs index 9a8b350e22..1c992df179 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -1,13 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; -using osu.Game.Rulesets.Difficulty.Utils; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; -using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs similarity index 94% rename from osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs rename to osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs index bf62cb1fbd..4d4089cba7 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs @@ -7,22 +7,24 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { - public class CombinedStrain : Skill + public class Peaks : Skill { - private const double final_multiplier = 0.00925; private const double rhythm_skill_multiplier = 1.6 * final_multiplier; private const double colour_skill_multiplier = 1.85 * final_multiplier; private const double stamina_skill_multiplier = 1.85 * final_multiplier; - private Rhythm rhythm; - private Colour colour; - private Stamina stamina; + private const double final_multiplier = 0.00925; + + private readonly Rhythm rhythm; + private readonly Colour colour; + private readonly Stamina stamina; public double ColourDifficultyValue => colour.DifficultyValue() * colour_skill_multiplier; public double RhythmDifficultyValue => rhythm.DifficultyValue() * rhythm_skill_multiplier; public double StaminaDifficultyValue => stamina.DifficultyValue() * stamina_skill_multiplier; - public CombinedStrain(Mod[] mods) : base(mods) + public Peaks(Mod[] mods) + : base(mods) { rhythm = new Rhythm(mods); colour = new Colour(mods); @@ -85,4 +87,4 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills return difficulty; } } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index ee5e257811..57c82bf97b 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -5,8 +5,6 @@ using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; -using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; -using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs index 3dc5438072..c7342554da 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs @@ -28,13 +28,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty public double ColourDifficulty { get; set; } /// - /// The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc). + /// The difficulty corresponding to the hardest parts of the map. /// - /// - /// Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing. - /// - [JsonProperty("approach_rate")] - public double ApproachRate { get; set; } + [JsonProperty("peak_difficulty")] + public double PeakDifficulty { get; set; } /// /// The perceived hit window for a GREAT hit inclusive of rate-adjusting mods (DT/HT/etc). diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index f4a23930b3..91ae1c4ed2 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { public class TaikoDifficultyCalculator : DifficultyCalculator { + private const double difficulty_multiplier = 1.9; + public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { @@ -29,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { return new Skill[] { - new CombinedStrain(mods) + new Peaks(mods) }; } @@ -68,13 +70,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (beatmap.HitObjects.Count == 0) return new TaikoDifficultyAttributes { Mods = mods }; - var combined = (CombinedStrain)skills[0]; + var combined = (Peaks)skills[0]; - double colourRating = combined.ColourDifficultyValue; - double rhythmRating = combined.RhythmDifficultyValue; - double staminaRating = combined.StaminaDifficultyValue; + double colourRating = Math.Sqrt(combined.ColourDifficultyValue * difficulty_multiplier); + double rhythmRating = Math.Sqrt(combined.RhythmDifficultyValue * difficulty_multiplier); + double staminaRating = Math.Sqrt(combined.StaminaDifficultyValue * difficulty_multiplier); - double starRating = rescale(1.9 * combined.DifficultyValue()); + double combinedRating = combined.DifficultyValue(); + double starRating = rescale(combinedRating * difficulty_multiplier); HitWindows hitWindows = new TaikoHitWindows(); hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); @@ -86,6 +89,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty StaminaDifficulty = staminaRating, RhythmDifficulty = rhythmRating, ColourDifficulty = colourRating, + PeakDifficulty = combinedRating, GreatHitWindow = hitWindows.WindowFor(HitResult.Great) / clockRate, MaxCombo = beatmap.HitObjects.Count(h => h is Hit), }; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index 8d99fd3b87..f551d8cd93 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -33,21 +33,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh); countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss); - double multiplier = 1.1; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things - - if (score.Mods.Any(m => m is ModNoFail)) - multiplier *= 0.90; - - if (score.Mods.Any(m => m is ModHidden)) - multiplier *= 1.10; - double difficultyValue = computeDifficultyValue(score, taikoAttributes); double accuracyValue = computeAccuracyValue(score, taikoAttributes); double totalValue = Math.Pow( Math.Pow(difficultyValue, 1.1) + Math.Pow(accuracyValue, 1.1), 1.0 / 1.1 - ) * multiplier; + ) * 1.1; return new TaikoPerformanceAttributes { @@ -59,7 +51,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty private double computeDifficultyValue(ScoreInfo score, TaikoDifficultyAttributes attributes) { - double difficultyValue = Math.Pow(5 * Math.Max(1.0, attributes.StarRating / 0.175) - 4.0, 2.25) / 450.0; + double difficultyValue = Math.Pow(5 * Math.Max(1.0, attributes.StarRating / 0.190) - 4.0, 2.25) / 450.0; double lengthBonus = 1 + 0.1 * Math.Min(1.0, totalHits / 1500.0); difficultyValue *= lengthBonus; @@ -67,7 +59,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty difficultyValue *= Math.Pow(0.985, countMiss); if (score.Mods.Any(m => m is ModHidden)) - difficultyValue *= 1.025; + difficultyValue *= 1.125; if (score.Mods.Any(m => m is ModFlashlight)) difficultyValue *= 1.05 * lengthBonus; @@ -80,10 +72,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (attributes.GreatHitWindow <= 0) return 0; - double accValue = Math.Pow(150.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(score.Accuracy, 15) * 22.0; + double accuracyValue = Math.Pow(150.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(score.Accuracy, 15) * 40.0; - // Bonus for many objects - it's harder to keep good accuracy up for longer - return accValue * Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3)); + double accuracylengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3)); + accuracyValue *= accuracylengthBonus; + + if (score.Mods.Any(m => m is ModHidden)) + accuracyValue *= 1.225; + + if (score.Mods.Any(m => m is ModFlashlight)) + accuracyValue *= 1.15 * accuracylengthBonus; + + return accuracyValue; } private int totalHits => countGreat + countOk + countMeh + countMiss;