From f01deae42819902b0b1920c3caa5070243a6a9ac Mon Sep 17 00:00:00 2001 From: vun Date: Tue, 24 May 2022 17:38:52 +0800 Subject: [PATCH 01/60] Colour compression preprocessing implementation --- .../TaikoDifficultyHitObjectColour.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs new file mode 100644 index 0000000000..425f5a16a9 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs @@ -0,0 +1,78 @@ +namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing +{ + /// + /// Stores colour compression information for a . + /// + public class TaikoDifficultyHitObjectColour + { + const int max_repetition_interval = 16; + + private TaikoDifficultyHitObjectColour previous; + + /// + /// True if the current colour is different from the previous colour. + /// + public bool Delta { get; private set; } + + /// + /// How many notes are Delta repeated + /// + public int DeltaRunLength { get; private set; } + + /// + /// How many notes between the current and previous identical . + /// Negative number means that there is no repetition in range. + /// + public int RepetitionInterval { get; private set; } + + /// + /// Get the instance for the given hitObject. This is implemented + /// as a static function instead of constructor to allow for reusing existing instances. + /// TODO: findRepetitionInterval needs to be called a final time after all hitObjects have been processed. + /// + public static TaikoDifficultyHitObjectColour GetInstanceFor( + TaikoDifficultyHitObject hitObject, TaikoDifficultyHitObject lastObject, TaikoDifficultyHitObjectColour previous) + { + bool delta = lastObject == null || hitObject.HitType != lastObject.HitType; + if (delta == previous.Delta) + { + previous.DeltaRunLength += 1; + return previous; + } + else + { + // Calculate RepetitionInterval for previous + previous.RepetitionInterval = findRepetitionInterval(previous); + + return new TaikoDifficultyHitObjectColour() + { + Delta = delta, + DeltaRunLength = 1, + RepetitionInterval = -1, + previous = previous + }; + } + } + + /// + /// Finds the closest previous that has the identical delta value + /// and run length to target, and returns the amount of notes between them. + /// + private static int findRepetitionInterval(TaikoDifficultyHitObjectColour target) { + if (target.previous == null || target.previous.previous == null) + return -1; + + int interval = target.previous.DeltaRunLength; + TaikoDifficultyHitObjectColour other = target.previous.previous; + while(other != null && interval < max_repetition_interval) { + if (other.Delta == target.Delta && other.DeltaRunLength == target.DeltaRunLength) + return interval; + else + interval += other.DeltaRunLength; + other = other.previous; + } + + return -1; + } + } +} \ No newline at end of file From 1972bdd6c7750b7a4f0170beeceebbe08c425906 Mon Sep 17 00:00:00 2001 From: vun Date: Thu, 26 May 2022 18:04:25 +0800 Subject: [PATCH 02/60] Working colour encoding --- .../Preprocessing/TaikoDifficultyHitObject.cs | 15 ++++--- .../TaikoDifficultyHitObjectColour.cs | 39 ++++++++++++------- .../Difficulty/TaikoDifficultyCalculator.cs | 11 +++++- 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index ae33c184d0..97b79f5eea 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public readonly TaikoDifficultyHitObjectRhythm Rhythm; + public readonly TaikoDifficultyHitObjectColour Colour; + /// /// The hit type of this hit object. /// @@ -29,29 +31,26 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public readonly int ObjectIndex; - /// - /// Whether the object should carry a penalty due to being hittable using special techniques - /// making it easier to do so. - /// - public bool StaminaCheese; - /// /// Creates a new difficulty hit object. /// /// The gameplay associated with this difficulty object. /// The gameplay preceding . /// The gameplay preceding . + /// The for . /// The rate of the gameplay clock. Modified by speed-changing mods. /// The index of the object in the beatmap. - public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate, int objectIndex) + public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, TaikoDifficultyHitObject lastDifficulty, double clockRate, int objectIndex) : base(hitObject, lastObject, clockRate) { var currentHit = hitObject as Hit; Rhythm = getClosestRhythm(lastObject, lastLastObject, clockRate); HitType = currentHit?.Type; - ObjectIndex = objectIndex; + + // Need to be done after HitType is set. + Colour = TaikoDifficultyHitObjectColour.GetInstanceFor(this, lastDifficulty); } /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs index 425f5a16a9..2fada1c543 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs @@ -31,10 +31,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// TODO: findRepetitionInterval needs to be called a final time after all hitObjects have been processed. /// public static TaikoDifficultyHitObjectColour GetInstanceFor( - TaikoDifficultyHitObject hitObject, TaikoDifficultyHitObject lastObject, TaikoDifficultyHitObjectColour previous) + TaikoDifficultyHitObject hitObject, TaikoDifficultyHitObject lastObject) { + TaikoDifficultyHitObjectColour previous = lastObject?.Colour; bool delta = lastObject == null || hitObject.HitType != lastObject.HitType; - if (delta == previous.Delta) + if (previous != null && delta == previous.Delta) { previous.DeltaRunLength += 1; return previous; @@ -42,7 +43,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing else { // Calculate RepetitionInterval for previous - previous.RepetitionInterval = findRepetitionInterval(previous); + previous?.FindRepetitionInterval(); return new TaikoDifficultyHitObjectColour() { @@ -56,23 +57,35 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// /// Finds the closest previous that has the identical delta value - /// and run length to target, and returns the amount of notes between them. + /// and run length with the current instance, and returns the amount of notes between them. /// - private static int findRepetitionInterval(TaikoDifficultyHitObjectColour target) { - if (target.previous == null || target.previous.previous == null) - return -1; + public void FindRepetitionInterval() + { + if (this.previous == null || this.previous.previous == null) + { + this.RepetitionInterval = -1; + return; + } - int interval = target.previous.DeltaRunLength; - TaikoDifficultyHitObjectColour other = target.previous.previous; - while(other != null && interval < max_repetition_interval) { - if (other.Delta == target.Delta && other.DeltaRunLength == target.DeltaRunLength) - return interval; + + int interval = this.previous.DeltaRunLength; + TaikoDifficultyHitObjectColour other = this.previous.previous; + while (other != null && interval < max_repetition_interval) + { + if (other.Delta == this.Delta && other.DeltaRunLength == this.DeltaRunLength) + { + this.RepetitionInterval = interval; + return; + } else + { interval += other.DeltaRunLength; + } + other = other.previous; } - return -1; + this.RepetitionInterval = -1; } } } \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 1aa31c6fe4..6697ad0509 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -52,11 +52,20 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { taikoDifficultyHitObjects.Add( new TaikoDifficultyHitObject( - beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, i + beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], taikoDifficultyHitObjects.DefaultIfEmpty(null).LastOrDefault(), clockRate, i ) ); } + // Find repetition interval for the final TaikoDifficultyHitObjectColour + // TODO: Might be a good idea to refactor this + taikoDifficultyHitObjects.Last().Colour.FindRepetitionInterval(); + + taikoDifficultyHitObjects.ForEach((item) => + { + Console.WriteLine($"{item.StartTime}, {item.Colour.GetHashCode()}, {item.Colour.Delta}, {item.Colour.DeltaRunLength}, {item.Colour.RepetitionInterval}"); + }); + return taikoDifficultyHitObjects; } From 86ffa810a9628ace6daaee54244e41dde0297563 Mon Sep 17 00:00:00 2001 From: vun Date: Tue, 31 May 2022 23:17:39 +0800 Subject: [PATCH 03/60] Implement stamina evaluator (untested yet) --- .../Difficulty/Evaluators/StaminaEvaluator.cs | 64 +++++++++++++++++++ .../Difficulty/Skills/SingleKeyStamina.cs | 42 ------------ .../Difficulty/Skills/Stamina.cs | 48 +------------- 3 files changed, 66 insertions(+), 88 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs delete mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs new file mode 100644 index 0000000000..a2f6b860f6 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -0,0 +1,64 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators +{ + public class StaminaEvaluator + { + /// + /// Applies a speed bonus dependent on the time since the last hit performed using this key. + /// + /// The duration between the current and previous note hit using the same key. + private static double speedBonus(double notePairDuration) + { + return 175 / (notePairDuration + 100); + } + + /// + /// Evaluates the minimum mechanical stamina required to play the current object. This is calculated using the + /// maximum possible interval between two hits using the same key, by alternating 2 keys for each colour. + /// + public static double EvaluateDifficultyOf(DifficultyHitObject current) + { + if (!(current.BaseObject is Hit)) + { + return 0.0; + } + + // Find the previous hit object hit by the current key, which is two notes of the same colour prior. + // TODO: This could result in potential performance issue where it has to check the colour of a large amount + // of objects due to previous objects being mono of the other colour. A potential fix for this would be + // to store two separate lists of previous objects, one for each colour. + TaikoDifficultyHitObject taikoCurrent = (TaikoDifficultyHitObject)current; + TaikoDifficultyHitObject previous = taikoCurrent; + int monoNoteInterval = 2; // The amount of same-colour notes to go back + double currentKeyInterval = 0; // Interval of the current key being pressed + do + { + previous = (TaikoDifficultyHitObject)previous.Previous(1); + if (previous.BaseObject is Hit && previous.HitType == taikoCurrent.HitType) + { + --monoNoteInterval; + } + currentKeyInterval += previous.DeltaTime; + + } while (previous != null && monoNoteInterval > 0); + + // This note is the first press of the current key + if (monoNoteInterval > 0) + { + return 0; + } + + double objectStrain = 0.5; + objectStrain += speedBonus(currentKeyInterval); + return objectStrain; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs deleted file mode 100644 index cabfd231d8..0000000000 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Difficulty.Preprocessing; -using osu.Game.Rulesets.Difficulty.Skills; - -namespace osu.Game.Rulesets.Taiko.Difficulty.Skills -{ - /// - /// Stamina of a single key, calculated based on repetition speed. - /// - public class SingleKeyStamina - { - private double? previousHitTime; - - /// - /// Similar to - /// - public double StrainValueOf(DifficultyHitObject current) - { - if (previousHitTime == null) - { - previousHitTime = current.StartTime; - return 0; - } - - double objectStrain = 0.5; - objectStrain += speedBonus(current.StartTime - previousHitTime.Value); - previousHitTime = current.StartTime; - return objectStrain; - } - - /// - /// Applies a speed bonus dependent on the time since the last hit performed using this key. - /// - /// The duration between the current and previous note hit using the same key. - private double speedBonus(double notePairDuration) - { - return 175 / (notePairDuration + 100); - } - } -} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 61bcbfa59d..71713bcd56 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -4,6 +4,7 @@ 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; @@ -20,28 +21,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills protected override double SkillMultiplier => 1; protected override double StrainDecayBase => 0.4; - private readonly SingleKeyStamina[] centreKeyStamina = - { - new SingleKeyStamina(), - new SingleKeyStamina() - }; - - private readonly SingleKeyStamina[] rimKeyStamina = - { - new SingleKeyStamina(), - new SingleKeyStamina() - }; - - /// - /// Current index into for a centre hit. - /// - private int centreKeyIndex; - - /// - /// Current index into for a rim hit. - /// - private int rimKeyIndex; - /// /// Creates a skill. /// @@ -51,32 +30,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { } - /// - /// Get the next to use for the given . - /// - /// The current . - private SingleKeyStamina getNextSingleKeyStamina(TaikoDifficultyHitObject current) - { - // Alternate key for the same color. - if (current.HitType == HitType.Centre) - { - centreKeyIndex = (centreKeyIndex + 1) % 2; - return centreKeyStamina[centreKeyIndex]; - } - - rimKeyIndex = (rimKeyIndex + 1) % 2; - return rimKeyStamina[rimKeyIndex]; - } - protected override double StrainValueOf(DifficultyHitObject current) { - if (!(current.BaseObject is Hit)) - { - return 0.0; - } - - TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)current; - return getNextSingleKeyStamina(hitObject).StrainValueOf(hitObject); + return StaminaEvaluator.EvaluateDifficultyOf(current); } } } From 8bbe70bff0ad69fc2cde3fe24281617dc18d3840 Mon Sep 17 00:00:00 2001 From: vun Date: Wed, 1 Jun 2022 04:33:37 +0800 Subject: [PATCH 04/60] Fix NullPointerReference --- .../Difficulty/Evaluators/StaminaEvaluator.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs index a2f6b860f6..59d3e61a27 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Difficulty.Preprocessing; -using osu.Game.Rulesets.Difficulty.Skills; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Objects; @@ -42,19 +40,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators do { previous = (TaikoDifficultyHitObject)previous.Previous(1); + if (previous == null) return 0; // No previous (The note is the first press of the current key) if (previous.BaseObject is Hit && previous.HitType == taikoCurrent.HitType) { --monoNoteInterval; } currentKeyInterval += previous.DeltaTime; - } while (previous != null && monoNoteInterval > 0); - - // This note is the first press of the current key - if (monoNoteInterval > 0) - { - return 0; - } + } while (monoNoteInterval > 0); double objectStrain = 0.5; objectStrain += speedBonus(currentKeyInterval); From 0a21f7c30dd15dbde01690de9a240b6e11e6b8a5 Mon Sep 17 00:00:00 2001 From: vun Date: Wed, 1 Jun 2022 05:20:08 +0800 Subject: [PATCH 05/60] Implement mono history in TaikoDifficultyHitObject --- .../Difficulty/Evaluators/StaminaEvaluator.cs | 23 +++++------------ .../Preprocessing/TaikoDifficultyHitObject.cs | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs index 59d3e61a27..6f141f50ae 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -30,27 +30,16 @@ 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. - // TODO: This could result in potential performance issue where it has to check the colour of a large amount - // of objects due to previous objects being mono of the other colour. A potential fix for this would be - // to store two separate lists of previous objects, one for each colour. TaikoDifficultyHitObject taikoCurrent = (TaikoDifficultyHitObject)current; - TaikoDifficultyHitObject previous = taikoCurrent; - int monoNoteInterval = 2; // The amount of same-colour notes to go back - double currentKeyInterval = 0; // Interval of the current key being pressed - do + TaikoDifficultyHitObject keyPrevious = taikoCurrent.PreviousMono(1); + if (keyPrevious == null) { - previous = (TaikoDifficultyHitObject)previous.Previous(1); - if (previous == null) return 0; // No previous (The note is the first press of the current key) - if (previous.BaseObject is Hit && previous.HitType == taikoCurrent.HitType) - { - --monoNoteInterval; - } - currentKeyInterval += previous.DeltaTime; - - } while (monoNoteInterval > 0); + // There is no previous hit object hit by the current key + return 0.0; + } double objectStrain = 0.5; - objectStrain += speedBonus(currentKeyInterval); + objectStrain += speedBonus(taikoCurrent.StartTime - keyPrevious.StartTime); return objectStrain; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index 450eb63636..699d08e7bc 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -15,6 +15,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public class TaikoDifficultyHitObject : DifficultyHitObject { + // TODO: Review this - these was originally handled in TaikodifficultyCalculator.CreateDifficultyHitObjects, but + // it might be a good idea to encapsulate as much detail within the class as possible. + private static List centreHitObjects = new List(); + private static List rimHitObjects = new List(); + + private readonly IReadOnlyList monoDifficultyHitObjects; + public readonly int MonoPosition; + /// /// The rhythm required to hit this hit object. /// @@ -47,6 +55,19 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing Rhythm = getClosestRhythm(lastObject, lastLastObject, clockRate); HitType = currentHit?.Type; + + if (HitType == Objects.HitType.Centre) + { + MonoPosition = centreHitObjects.Count(); + centreHitObjects.Add(this); + monoDifficultyHitObjects = centreHitObjects; + } + else if (HitType == Objects.HitType.Rim) + { + MonoPosition = rimHitObjects.Count(); + rimHitObjects.Add(this); + monoDifficultyHitObjects = rimHitObjects; + } } /// @@ -85,5 +106,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing return common_rhythms.OrderBy(x => Math.Abs(x.Ratio - ratio)).First(); } + + public TaikoDifficultyHitObject PreviousMono(int backwardsIndex) => monoDifficultyHitObjects.ElementAtOrDefault(MonoPosition - (backwardsIndex + 1)); + + public TaikoDifficultyHitObject NextMono(int forwardsIndex) => monoDifficultyHitObjects.ElementAtOrDefault(MonoPosition + (forwardsIndex + 1)); } } From 56a4034c2260f8f6d4c2e0e37b4119e912fed021 Mon Sep 17 00:00:00 2001 From: vun Date: Thu, 2 Jun 2022 18:48:36 +0800 Subject: [PATCH 06/60] Change RepetitionInterval to have max_repetition_interval + 1 when no repetition is found. --- .../Preprocessing/TaikoDifficultyHitObjectColour.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs index 2fada1c543..a55fdc6e9f 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs @@ -1,3 +1,5 @@ +using System; + namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing { /// @@ -22,6 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// /// How many notes between the current and previous identical . /// Negative number means that there is no repetition in range. + /// If no repetition is found this will have a value of + 1. /// public int RepetitionInterval { get; private set; } @@ -49,7 +52,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing { Delta = delta, DeltaRunLength = 1, - RepetitionInterval = -1, + RepetitionInterval = max_repetition_interval + 1, previous = previous }; } @@ -63,7 +66,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing { if (this.previous == null || this.previous.previous == null) { - this.RepetitionInterval = -1; + this.RepetitionInterval = max_repetition_interval + 1; return; } @@ -74,7 +77,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing { if (other.Delta == this.Delta && other.DeltaRunLength == this.DeltaRunLength) { - this.RepetitionInterval = interval; + this.RepetitionInterval = Math.Max(interval, max_repetition_interval); return; } else @@ -85,7 +88,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing other = other.previous; } - this.RepetitionInterval = -1; + this.RepetitionInterval = max_repetition_interval + 1; } } } \ No newline at end of file From 3dd0c4aec87c66c0527f9444a0a9bc9a80dbbfff Mon Sep 17 00:00:00 2001 From: vun Date: Mon, 6 Jun 2022 12:42:49 +0800 Subject: [PATCH 07/60] [WIP] Colour rework --- .../Difficulty/Evaluators/ColourEvaluator.cs | 33 +++++ .../Preprocessing/TaikoDifficultyHitObject.cs | 18 ++- .../TaikoDifficultyHitObjectColour.cs | 4 +- .../Difficulty/Skills/Colour.cs | 113 +----------------- .../Difficulty/Skills/SingleKeyStamina.cs | 20 +++- .../Difficulty/Skills/Stamina.cs | 9 +- .../Difficulty/TaikoDifficultyCalculator.cs | 26 ++-- 7 files changed, 88 insertions(+), 135 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs new file mode 100644 index 0000000000..159f9a4508 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -0,0 +1,33 @@ +using System; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; + +namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators +{ + public class ColourEvaluator + { + private static double sigmoid(double val, double center, double width) + { + return Math.Tanh(Math.E * -(val - center) / width); + } + + public static double EvaluateDifficultyOf(DifficultyHitObject current) + { + TaikoDifficultyHitObject taikoCurrent = (TaikoDifficultyHitObject)current; + TaikoDifficultyHitObjectColour colour = taikoCurrent.Colour; + if (colour == null) return 0; + double objectStrain = 1; + if (colour.Delta) + { + objectStrain /= Math.Pow(colour.DeltaRunLength, 0.25); + } + else + { + objectStrain *= sigmoid(colour.DeltaRunLength, 3, 3) * 0.3 + 0.3; + } + objectStrain *= -sigmoid(colour.RepetitionInterval, 8, 8); + // 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/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index b34e87bc83..8d2eadafe1 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -20,6 +20,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public readonly TaikoDifficultyHitObjectRhythm Rhythm; + /// + /// Colour data for this hit object. This is used by colour evaluator to calculate colour, but can be used + /// differently by other skills in the future. + /// public readonly TaikoDifficultyHitObjectColour Colour; /// @@ -45,7 +49,19 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing HitType = currentHit?.Type; // Need to be done after HitType is set. - Colour = TaikoDifficultyHitObjectColour.GetInstanceFor(this, (TaikoDifficultyHitObject) objects.LastOrDefault()); + if (HitType != null) + { + // Get previous hit object, while skipping one that does not have defined colour (sliders and spinners). + // Without skipping through these, sliders and spinners would have contributed to a colour change for the next note. + TaikoDifficultyHitObject previousHitObject = (TaikoDifficultyHitObject)objects.LastOrDefault(); + while (previousHitObject != null && previousHitObject.Colour == null) + { + previousHitObject = (TaikoDifficultyHitObject)previousHitObject.Previous(0); + } + + 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 a55fdc6e9f..ce65fd0552 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs @@ -33,9 +33,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// as a static function instead of constructor to allow for reusing existing instances. /// TODO: findRepetitionInterval needs to be called a final time after all hitObjects have been processed. /// - public static TaikoDifficultyHitObjectColour GetInstanceFor( - TaikoDifficultyHitObject hitObject, TaikoDifficultyHitObject lastObject) + public static TaikoDifficultyHitObjectColour GetInstanceFor(TaikoDifficultyHitObject hitObject) { + TaikoDifficultyHitObject lastObject = (TaikoDifficultyHitObject) hitObject.Previous(0); TaikoDifficultyHitObjectColour previous = lastObject?.Colour; bool delta = lastObject == null || hitObject.HitType != lastObject.HitType; if (previous != null && delta == previous.Delta) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs index 0c17ca66b9..9a8b350e22 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -6,7 +6,7 @@ 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.Preprocessing; +using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills @@ -19,27 +19,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills protected override double SkillMultiplier => 1; protected override double StrainDecayBase => 0.4; - /// - /// Maximum number of entries to keep in . - /// - private const int mono_history_max_length = 5; - - /// - /// Queue with the lengths of the last most recent mono (single-colour) patterns, - /// with the most recent value at the end of the queue. - /// - private readonly LimitedCapacityQueue monoHistory = new LimitedCapacityQueue(mono_history_max_length); - - /// - /// The of the last object hit before the one being considered. - /// - private HitType? previousHitType; - - /// - /// Length of the current mono pattern. - /// - private int currentMonoLength; - public Colour(Mod[] mods) : base(mods) { @@ -47,95 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { - // changing from/to a drum roll or a swell does not constitute a colour change. - // hits spaced more than a second apart are also exempt from colour strain. - if (!(current.LastObject is Hit && current.BaseObject is Hit && current.DeltaTime < 1000)) - { - monoHistory.Clear(); - - var currentHit = current.BaseObject as Hit; - currentMonoLength = currentHit != null ? 1 : 0; - previousHitType = currentHit?.Type; - - return 0.0; - } - - var taikoCurrent = (TaikoDifficultyHitObject)current; - - double objectStrain = 0.0; - - if (previousHitType != null && taikoCurrent.HitType != previousHitType) - { - // The colour has changed. - objectStrain = 1.0; - - if (monoHistory.Count < 2) - { - // There needs to be at least two streaks to determine a strain. - objectStrain = 0.0; - } - else if ((monoHistory[^1] + currentMonoLength) % 2 == 0) - { - // The last streak in the history is guaranteed to be a different type to the current streak. - // If the total number of notes in the two streaks is even, nullify this object's strain. - objectStrain = 0.0; - } - - objectStrain *= repetitionPenalties(); - currentMonoLength = 1; - } - else - { - currentMonoLength += 1; - } - - previousHitType = taikoCurrent.HitType; - return objectStrain; + return ColourEvaluator.EvaluateDifficultyOf(current); } - - /// - /// The penalty to apply due to the length of repetition in colour streaks. - /// - private double repetitionPenalties() - { - const int most_recent_patterns_to_compare = 2; - double penalty = 1.0; - - monoHistory.Enqueue(currentMonoLength); - - for (int start = monoHistory.Count - most_recent_patterns_to_compare - 1; start >= 0; start--) - { - if (!isSamePattern(start, most_recent_patterns_to_compare)) - continue; - - int notesSince = 0; - for (int i = start; i < monoHistory.Count; i++) notesSince += monoHistory[i]; - penalty *= repetitionPenalty(notesSince); - break; - } - - return penalty; - } - - /// - /// Determines whether the last patterns have repeated in the history - /// of single-colour note sequences, starting from . - /// - private bool isSamePattern(int start, int mostRecentPatternsToCompare) - { - for (int i = 0; i < mostRecentPatternsToCompare; i++) - { - if (monoHistory[start + i] != monoHistory[monoHistory.Count - mostRecentPatternsToCompare + i]) - return false; - } - - return true; - } - - /// - /// Calculates the strain penalty for a colour pattern repetition. - /// - /// The number of notes since the last repetition of the pattern. - private double repetitionPenalty(int notesSince) => Math.Min(1.0, 0.032 * notesSince); } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs index cabfd231d8..4b8a8033a4 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs @@ -1,8 +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.Taiko.Difficulty.Evaluators; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { @@ -11,6 +13,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// public class SingleKeyStamina { + private const double StrainDecayBase = 0.4; + + private double CurrentStrain = 0; + private double? previousHitTime; /// @@ -24,19 +30,23 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills return 0; } - double objectStrain = 0.5; - objectStrain += speedBonus(current.StartTime - previousHitTime.Value); + // CurrentStrain += strainDecay(current.StartTime - current.Previous(0).StartTime); + // CurrentStrain += 0.5 + 0.5 * strainDecay(current.StartTime - current.Previous(0).StartTime); + CurrentStrain += 1; + CurrentStrain *= ColourEvaluator.EvaluateDifficultyOf(current) * 0.1 + 0.9; + CurrentStrain *= strainDecay(current.StartTime - previousHitTime.Value); previousHitTime = current.StartTime; - return objectStrain; + return CurrentStrain; } /// /// Applies a speed bonus dependent on the time since the last hit performed using this key. /// /// The duration between the current and previous note hit using the same key. - private double speedBonus(double notePairDuration) + private double strainDecay(double notePairDuration) { - return 175 / (notePairDuration + 100); + return Math.Pow(StrainDecayBase, notePairDuration / 1000); + // return 175 / (notePairDuration + 100); } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 61bcbfa59d..7196b68df2 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -5,6 +5,7 @@ using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; +using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills @@ -17,8 +18,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// public class Stamina : StrainDecaySkill { - protected override double SkillMultiplier => 1; - protected override double StrainDecayBase => 0.4; + protected override double SkillMultiplier => 3.6; + protected override double StrainDecayBase => 0; private readonly SingleKeyStamina[] centreKeyStamina = { @@ -76,7 +77,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills } TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)current; - return getNextSingleKeyStamina(hitObject).StrainValueOf(hitObject); + double objectStrain = getNextSingleKeyStamina(hitObject).StrainValueOf(hitObject); + // objectStrain *= ColourEvaluator.EvaluateDifficultyOf(current) * 0.3 + 0.7; + return objectStrain; } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 4dd3b0b8cc..423903db2f 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -20,8 +20,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { public class TaikoDifficultyCalculator : DifficultyCalculator { - private const double rhythm_skill_multiplier = 0.014; - private const double colour_skill_multiplier = 0.01; + private const double rhythm_skill_multiplier = 0.017; + private const double colour_skill_multiplier = 0.028; private const double stamina_skill_multiplier = 0.021; public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty // Find repetition interval for the final TaikoDifficultyHitObjectColour // TODO: Might be a good idea to refactor this - ((TaikoDifficultyHitObject)difficultyHitObject.Last()).Colour.FindRepetitionInterval(); + ((TaikoDifficultyHitObject)difficultyHitObject.Last()).Colour?.FindRepetitionInterval(); return difficultyHitObject; } @@ -76,18 +76,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty double rhythmRating = rhythm.DifficultyValue() * rhythm_skill_multiplier; double staminaRating = stamina.DifficultyValue() * stamina_skill_multiplier; - double staminaPenalty = simpleColourPenalty(staminaRating, colourRating); - staminaRating *= staminaPenalty; + // double staminaPenalty = simpleColourPenalty(staminaRating, colourRating); + // staminaRating *= staminaPenalty; //TODO : This is a temporary fix for the stamina rating of converts, due to their low colour variance. - if (beatmap.BeatmapInfo.Ruleset.OnlineID == 0 && colourRating < 0.05) - { - staminaPenalty *= 0.25; - } + // if (beatmap.BeatmapInfo.Ruleset.OnlineID == 0 && colourRating < 0.05) + // { + // staminaPenalty *= 0.25; + // } - double combinedRating = locallyCombinedDifficulty(colour, rhythm, stamina, staminaPenalty); + double combinedRating = locallyCombinedDifficulty(colour, rhythm, stamina); double separatedRating = norm(1.5, colourRating, rhythmRating, staminaRating); - double starRating = 1.4 * separatedRating + 0.5 * combinedRating; + double starRating = 1.9 * combinedRating; starRating = rescale(starRating); HitWindows hitWindows = new TaikoHitWindows(); @@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty /// For each section, the peak strains of all separate skills are combined into a single peak strain for the section. /// The resulting partial rating of the beatmap is a weighted sum of the combined peaks (higher peaks are weighted more). /// - private double locallyCombinedDifficulty(Colour colour, Rhythm rhythm, Stamina stamina, double staminaPenalty) + private double locallyCombinedDifficulty(Colour colour, Rhythm rhythm, Stamina stamina) { List peaks = new List(); @@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { double colourPeak = colourPeaks[i] * colour_skill_multiplier; double rhythmPeak = rhythmPeaks[i] * rhythm_skill_multiplier; - double staminaPeak = staminaPeaks[i] * stamina_skill_multiplier * staminaPenalty; + double staminaPeak = staminaPeaks[i] * stamina_skill_multiplier; double peak = norm(2, colourPeak, rhythmPeak, staminaPeak); From 07d3a7bd2ecd9dd585539285a2fb374847929ebf Mon Sep 17 00:00:00 2001 From: vun Date: Mon, 6 Jun 2022 16:11:26 +0800 Subject: [PATCH 08/60] Fix logic error, minor stamina changes --- .../Difficulty/Evaluators/StaminaEvaluator.cs | 6 ++-- .../Preprocessing/TaikoDifficultyHitObject.cs | 29 ++++++++++++++----- .../Difficulty/Skills/Stamina.cs | 4 +-- .../Difficulty/TaikoDifficultyCalculator.cs | 11 ++++--- 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs index 6f141f50ae..0c33a952a5 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -1,6 +1,7 @@ // 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.Taiko.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Objects; @@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// The duration between the current and previous note hit using the same key. private static double speedBonus(double notePairDuration) { - return 175 / (notePairDuration + 100); + return Math.Pow(0.4, notePairDuration / 1000); } /// @@ -38,7 +39,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators return 0.0; } - double objectStrain = 0.5; + double objectStrain = 0; + // Console.WriteLine(speedBonus(taikoCurrent.StartTime - keyPrevious.StartTime)); objectStrain += speedBonus(taikoCurrent.StartTime - keyPrevious.StartTime); return objectStrain; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index 0949b8349a..cb57c7bccc 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Objects; @@ -15,13 +16,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public class TaikoDifficultyHitObject : DifficultyHitObject { - // TODO: Review this - these was originally handled in TaikodifficultyCalculator.CreateDifficultyHitObjects, but - // it might be a good idea to encapsulate as much detail within the class as possible. - private static List centreHitObjects = new List(); - private static List rimHitObjects = new List(); - private readonly IReadOnlyList monoDifficultyHitObjects; public readonly int MonoPosition; + private readonly IReadOnlyList noteObjects; + public readonly int NotePosition; /// /// The rhythm required to hit this hit object. @@ -46,12 +44,21 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// The gameplay preceding . /// The gameplay preceding . /// The rate of the gameplay clock. Modified by speed-changing mods. - /// The list of s in the current beatmap. - /// /// The position of this in the list. - public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate, List objects, int position) + /// The list of all s in the current beatmap. + /// The list of centre (don) s in the current beatmap. + /// The list of rim (kat) s in the current beatmap. + /// The list of s that is a hit (i.e. not a slider or spinner) in the current beatmap. + /// The position of this in the list. + /// + /// 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) : base(hitObject, lastObject, clockRate, objects, position) { var currentHit = hitObject as Hit; + this.noteObjects = noteObjects; Rhythm = getClosestRhythm(lastObject, lastLastObject, clockRate); HitType = currentHit?.Type; @@ -68,6 +75,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing } Colour = TaikoDifficultyHitObjectColour.GetInstanceFor(this); + this.NotePosition = noteObjects.Count(); + noteObjects.Add(this); } if (HitType == Objects.HitType.Centre) @@ -124,5 +133,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing public TaikoDifficultyHitObject PreviousMono(int backwardsIndex) => monoDifficultyHitObjects.ElementAtOrDefault(MonoPosition - (backwardsIndex + 1)); public TaikoDifficultyHitObject NextMono(int forwardsIndex) => monoDifficultyHitObjects.ElementAtOrDefault(MonoPosition + (forwardsIndex + 1)); + + public TaikoDifficultyHitObject PreviousNote(int backwardsIndex) => noteObjects.ElementAtOrDefault(NotePosition - (backwardsIndex + 1)); + + public TaikoDifficultyHitObject NextNote(int forwardsIndex) => noteObjects.ElementAtOrDefault(NotePosition + (forwardsIndex + 1)); } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 713944a6e0..ee5e257811 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -18,8 +18,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// public class Stamina : StrainDecaySkill { - protected override double SkillMultiplier => 3.6; - protected override double StrainDecayBase => 0; + protected override double SkillMultiplier => 1.2; + protected override double StrainDecayBase => 0.4; /// /// Creates a skill. diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 423903db2f..187fa4a070 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -21,8 +21,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty public class TaikoDifficultyCalculator : DifficultyCalculator { private const double rhythm_skill_multiplier = 0.017; - private const double colour_skill_multiplier = 0.028; - private const double stamina_skill_multiplier = 0.021; + private const double colour_skill_multiplier = 0.027; + private const double stamina_skill_multiplier = 0.017; public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) @@ -47,13 +47,16 @@ namespace osu.Game.Rulesets.Taiko.Difficulty protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { List difficultyHitObject = new List(); + List centreObjects = new List(); + List rimObjects = new List(); + List noteObjects = new List(); for (int i = 2; i < beatmap.HitObjects.Count; i++) { difficultyHitObject.Add( new TaikoDifficultyHitObject( - beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, difficultyHitObject, difficultyHitObject.Count - ) + beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, difficultyHitObject, + centreObjects, rimObjects, noteObjects, difficultyHitObject.Count) ); } From fd49a27cf99729b9e463ad34ea85082be789550b Mon Sep 17 00:00:00 2001 From: vun Date: Tue, 7 Jun 2022 13:30:24 +0800 Subject: [PATCH 09/60] Fix encoding repetition, parameter adjustments --- .../Difficulty/Evaluators/ColourEvaluator.cs | 8 +++--- .../Preprocessing/TaikoDifficultyHitObject.cs | 26 +++++++------------ .../TaikoDifficultyHitObjectColour.cs | 9 +++---- .../Difficulty/TaikoDifficultyCalculator.cs | 6 ++--- 4 files changed, 20 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 159f9a4508..c534b5bd9f 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -16,16 +16,16 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators TaikoDifficultyHitObject taikoCurrent = (TaikoDifficultyHitObject)current; TaikoDifficultyHitObjectColour colour = taikoCurrent.Colour; if (colour == null) return 0; - double objectStrain = 1; + double objectStrain = 1.6; if (colour.Delta) { - objectStrain /= Math.Pow(colour.DeltaRunLength, 0.25); + objectStrain /= Math.Pow(colour.DeltaRunLength, 0.7); } else { - objectStrain *= sigmoid(colour.DeltaRunLength, 3, 3) * 0.3 + 0.3; + objectStrain *= sigmoid(colour.DeltaRunLength, 4, 4) * 0.5 + 0.5; } - objectStrain *= -sigmoid(colour.RepetitionInterval, 8, 8); + 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; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index cb57c7bccc..537eafe396 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -63,22 +63,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing Rhythm = getClosestRhythm(lastObject, lastLastObject, clockRate); HitType = currentHit?.Type; - // Need to be done after HitType is set. - if (HitType != null) - { - // Get previous hit object, while skipping one that does not have defined colour (sliders and spinners). - // Without skipping through these, sliders and spinners would have contributed to a colour change for the next note. - TaikoDifficultyHitObject previousHitObject = (TaikoDifficultyHitObject)objects.LastOrDefault(); - while (previousHitObject != null && previousHitObject.Colour == null) - { - previousHitObject = (TaikoDifficultyHitObject)previousHitObject.Previous(0); - } - - Colour = TaikoDifficultyHitObjectColour.GetInstanceFor(this); - this.NotePosition = noteObjects.Count(); - noteObjects.Add(this); - } - if (HitType == Objects.HitType.Centre) { MonoPosition = centreHitObjects.Count(); @@ -91,6 +75,16 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing rimHitObjects.Add(this); monoDifficultyHitObjects = rimHitObjects; } + + // Need to be done after HitType is set. + if (HitType != null) + { + 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 ce65fd0552..f9a586f877 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public static TaikoDifficultyHitObjectColour GetInstanceFor(TaikoDifficultyHitObject hitObject) { - TaikoDifficultyHitObject lastObject = (TaikoDifficultyHitObject) hitObject.Previous(0); + TaikoDifficultyHitObject lastObject = hitObject.PreviousNote(0); TaikoDifficultyHitObjectColour previous = lastObject?.Colour; bool delta = lastObject == null || hitObject.HitType != lastObject.HitType; if (previous != null && delta == previous.Delta) @@ -75,15 +75,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing TaikoDifficultyHitObjectColour other = this.previous.previous; while (other != null && interval < max_repetition_interval) { + interval += other.DeltaRunLength; if (other.Delta == this.Delta && other.DeltaRunLength == this.DeltaRunLength) { - this.RepetitionInterval = Math.Max(interval, max_repetition_interval); + this.RepetitionInterval = Math.Min(interval, max_repetition_interval); return; } - else - { - interval += other.DeltaRunLength; - } other = other.previous; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 187fa4a070..706fde77d2 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -21,8 +21,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty public class TaikoDifficultyCalculator : DifficultyCalculator { private const double rhythm_skill_multiplier = 0.017; - private const double colour_skill_multiplier = 0.027; - private const double stamina_skill_multiplier = 0.017; + private const double colour_skill_multiplier = 0.026; + private const double stamina_skill_multiplier = 0.018; public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) @@ -89,7 +89,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty // } double combinedRating = locallyCombinedDifficulty(colour, rhythm, stamina); - double separatedRating = norm(1.5, colourRating, rhythmRating, staminaRating); + // double separatedRating = norm(2, colourRating, rhythmRating, staminaRating); double starRating = 1.9 * combinedRating; starRating = rescale(starRating); From d8d4ac431e804bfa8d644e1ca78c58591bfbe7a3 Mon Sep 17 00:00:00 2001 From: vun Date: Wed, 8 Jun 2022 13:24:51 +0800 Subject: [PATCH 10/60] Refactor LocallyCombinedDifficulty to an external skill --- .../Difficulty/Skills/CombinedStrain.cs | 86 +++++++++++++++ .../Difficulty/TaikoDifficultyCalculator.cs | 101 ++---------------- 2 files changed, 97 insertions(+), 90 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs new file mode 100644 index 0000000000..e5052c3359 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Taiko.Difficulty.Skills +{ + public class CombinedStrain : Skill + { + private const double rhythm_skill_multiplier = 0.017; + private const double colour_skill_multiplier = 0.026; + private const double stamina_skill_multiplier = 0.018; + + private Rhythm rhythm; + private Colour colour; + private 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) + { + rhythm = new Rhythm(mods); + colour = new Colour(mods); + stamina = new Stamina(mods); + } + + /// + /// Returns the p-norm of an n-dimensional vector. + /// + /// The value of p to calculate the norm for. + /// The coefficients of the vector. + private double norm(double p, params double[] values) => Math.Pow(values.Sum(x => Math.Pow(x, p)), 1 / p); + + public override void Process(DifficultyHitObject current) + { + rhythm.Process(current); + colour.Process(current); + stamina.Process(current); + } + + /// + /// Returns the combined star rating of the beatmap, calculated using peak strains from all sections of the map. + /// + /// + /// For each section, the peak strains of all separate skills are combined into a single peak strain for the section. + /// The resulting partial rating of the beatmap is a weighted sum of the combined peaks (higher peaks are weighted more). + /// + public override double DifficultyValue() + { + List peaks = new List(); + + var colourPeaks = colour.GetCurrentStrainPeaks().ToList(); + var rhythmPeaks = rhythm.GetCurrentStrainPeaks().ToList(); + var staminaPeaks = stamina.GetCurrentStrainPeaks().ToList(); + + for (int i = 0; i < colourPeaks.Count; i++) + { + double colourPeak = colourPeaks[i] * colour_skill_multiplier; + double rhythmPeak = rhythmPeaks[i] * rhythm_skill_multiplier; + double staminaPeak = staminaPeaks[i] * stamina_skill_multiplier; + + double peak = norm(2, colourPeak, rhythmPeak, staminaPeak); + + // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). + // These sections will not contribute to the difficulty. + if (peak > 0) + peaks.Add(peak); + } + + double difficulty = 0; + double weight = 1; + + foreach (double strain in peaks.OrderByDescending(d => d)) + { + difficulty += strain * weight; + weight *= 0.9; + } + + return difficulty; + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 706fde77d2..f4a23930b3 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -20,21 +20,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { public class TaikoDifficultyCalculator : DifficultyCalculator { - private const double rhythm_skill_multiplier = 0.017; - private const double colour_skill_multiplier = 0.026; - private const double stamina_skill_multiplier = 0.018; - public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) { - new Colour(mods), - new Rhythm(mods), - new Stamina(mods) - }; + return new Skill[] + { + new CombinedStrain(mods) + }; + } protected override Mod[] DifficultyAdjustmentMods => new Mod[] { @@ -71,27 +68,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (beatmap.HitObjects.Count == 0) return new TaikoDifficultyAttributes { Mods = mods }; - var colour = (Colour)skills[0]; - var rhythm = (Rhythm)skills[1]; - var stamina = (Stamina)skills[2]; + var combined = (CombinedStrain)skills[0]; - double colourRating = colour.DifficultyValue() * colour_skill_multiplier; - double rhythmRating = rhythm.DifficultyValue() * rhythm_skill_multiplier; - double staminaRating = stamina.DifficultyValue() * stamina_skill_multiplier; + double colourRating = combined.ColourDifficultyValue; + double rhythmRating = combined.RhythmDifficultyValue; + double staminaRating = combined.StaminaDifficultyValue; - // double staminaPenalty = simpleColourPenalty(staminaRating, colourRating); - // staminaRating *= staminaPenalty; - - //TODO : This is a temporary fix for the stamina rating of converts, due to their low colour variance. - // if (beatmap.BeatmapInfo.Ruleset.OnlineID == 0 && colourRating < 0.05) - // { - // staminaPenalty *= 0.25; - // } - - double combinedRating = locallyCombinedDifficulty(colour, rhythm, stamina); - // double separatedRating = norm(2, colourRating, rhythmRating, staminaRating); - double starRating = 1.9 * combinedRating; - starRating = rescale(starRating); + double starRating = rescale(1.9 * combined.DifficultyValue()); HitWindows hitWindows = new TaikoHitWindows(); hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); @@ -108,68 +91,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty }; } - /// - /// Calculates the penalty for the stamina skill for maps with low colour difficulty. - /// - /// - /// Some maps (especially converts) can be easy to read despite a high note density. - /// This penalty aims to reduce the star rating of such maps by factoring in colour difficulty to the stamina skill. - /// - private double simpleColourPenalty(double staminaDifficulty, double colorDifficulty) - { - if (colorDifficulty <= 0) return 0.79 - 0.25; - - return 0.79 - Math.Atan(staminaDifficulty / colorDifficulty - 12) / Math.PI / 2; - } - - /// - /// Returns the p-norm of an n-dimensional vector. - /// - /// The value of p to calculate the norm for. - /// The coefficients of the vector. - private double norm(double p, params double[] values) => Math.Pow(values.Sum(x => Math.Pow(x, p)), 1 / p); - - /// - /// Returns the partial star rating of the beatmap, calculated using peak strains from all sections of the map. - /// - /// - /// For each section, the peak strains of all separate skills are combined into a single peak strain for the section. - /// The resulting partial rating of the beatmap is a weighted sum of the combined peaks (higher peaks are weighted more). - /// - private double locallyCombinedDifficulty(Colour colour, Rhythm rhythm, Stamina stamina) - { - List peaks = new List(); - - var colourPeaks = colour.GetCurrentStrainPeaks().ToList(); - var rhythmPeaks = rhythm.GetCurrentStrainPeaks().ToList(); - var staminaPeaks = stamina.GetCurrentStrainPeaks().ToList(); - - for (int i = 0; i < colourPeaks.Count; i++) - { - double colourPeak = colourPeaks[i] * colour_skill_multiplier; - double rhythmPeak = rhythmPeaks[i] * rhythm_skill_multiplier; - double staminaPeak = staminaPeaks[i] * stamina_skill_multiplier; - - double peak = norm(2, colourPeak, rhythmPeak, staminaPeak); - - // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). - // These sections will not contribute to the difficulty. - if (peak > 0) - peaks.Add(peak); - } - - double difficulty = 0; - double weight = 1; - - foreach (double strain in peaks.OrderByDescending(d => d)) - { - difficulty += strain * weight; - weight *= 0.9; - } - - return difficulty; - } - /// /// Applies a final re-scaling of the star rating to bring maps with recorded full combos below 9.5 stars. /// From 5793ca5534e9a9d17f9e2bc94581051fbf082012 Mon Sep 17 00:00:00 2001 From: vun Date: Thu, 9 Jun 2022 12:35:26 +0800 Subject: [PATCH 11/60] Parameter tweaks --- .../Difficulty/Evaluators/ColourEvaluator.cs | 6 +++--- .../Difficulty/Skills/CombinedStrain.cs | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index c534b5bd9f..2ecef3690b 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -16,14 +16,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators TaikoDifficultyHitObject taikoCurrent = (TaikoDifficultyHitObject)current; TaikoDifficultyHitObjectColour colour = taikoCurrent.Colour; if (colour == null) return 0; - double objectStrain = 1.6; + double objectStrain = 1.8; if (colour.Delta) { - objectStrain /= Math.Pow(colour.DeltaRunLength, 0.7); + objectStrain *= sigmoid(colour.DeltaRunLength, 6, 4) * 0.5 + 0.5; } else { - objectStrain *= sigmoid(colour.DeltaRunLength, 4, 4) * 0.5 + 0.5; + 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}"); diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs index e5052c3359..bf62cb1fbd 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs @@ -9,9 +9,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { public class CombinedStrain : Skill { - private const double rhythm_skill_multiplier = 0.017; - private const double colour_skill_multiplier = 0.026; - private const double stamina_skill_multiplier = 0.018; + 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; @@ -63,7 +64,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills double rhythmPeak = rhythmPeaks[i] * rhythm_skill_multiplier; double staminaPeak = staminaPeaks[i] * stamina_skill_multiplier; - double peak = norm(2, colourPeak, rhythmPeak, staminaPeak); + double peak = norm(1.5, colourPeak, staminaPeak); + peak = norm(2, peak, rhythmPeak); // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). // These sections will not contribute to the difficulty. From 6dbaf0a03084e07ec8c287c7d739bee4453384b2 Mon Sep 17 00:00:00 2001 From: Jay L Date: Thu, 9 Jun 2022 19:22:55 +1000 Subject: [PATCH 12/60] 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; From 4c574eb044ed64b595145169bb1a6d8a561d028e Mon Sep 17 00:00:00 2001 From: vun Date: Thu, 9 Jun 2022 17:31:54 +0800 Subject: [PATCH 13/60] Rescale multipliers (values unaffected) --- .../Difficulty/Skills/CombinedStrain.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs index bf62cb1fbd..265f526367 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/CombinedStrain.cs @@ -9,10 +9,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { public class CombinedStrain : 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 const double final_multiplier = 0.04625; + private const double rhythm_skill_multiplier = 0.32 * final_multiplier; + private const double colour_skill_multiplier = 0.37 * final_multiplier; + private const double stamina_skill_multiplier = 0.37 * final_multiplier; private Rhythm rhythm; private Colour colour; From 2881406f6b2912c708a186f1de0e1ebc4f0739dc Mon Sep 17 00:00:00 2001 From: vun Date: Thu, 9 Jun 2022 19:41:59 +0800 Subject: [PATCH 14/60] Nerf alternating pattern slightly, value rescale --- .../Difficulty/Evaluators/ColourEvaluator.cs | 5 +++-- osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 01410af459..8c225c8459 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -18,11 +18,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators TaikoDifficultyHitObjectColour colour = taikoCurrent.Colour; if (colour == null) return 0; - double objectStrain = 1.8; + double objectStrain = 2.1; if (colour.Delta) { - objectStrain *= sigmoid(colour.DeltaRunLength, 6, 4) * 0.5 + 0.5; + objectStrain *= sigmoid(colour.DeltaRunLength, 4, 4) * 0.5 + 0.5; } else { @@ -30,6 +30,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators } objectStrain *= -sigmoid(colour.RepetitionInterval, 8, 8) * 0.5 + 0.5; + // Console.WriteLine($"{current.StartTime},{colour.Delta},{colour.RepetitionInterval},{objectStrain}"); return objectStrain; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs index d11aaef230..171278b4dd 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills private const double colour_skill_multiplier = 0.37 * final_multiplier; private const double stamina_skill_multiplier = 0.37 * final_multiplier; - private const double final_multiplier = 0.04625; + private const double final_multiplier = 0.047; private readonly Rhythm rhythm; private readonly Colour colour; From 2b2150ac04d93e86ba285a52fda237160f76d6a9 Mon Sep 17 00:00:00 2001 From: vun Date: Fri, 10 Jun 2022 14:58:50 +0800 Subject: [PATCH 15/60] Refactor TaikoDifficultyHitObject creation into the class as a static method --- .../Preprocessing/TaikoDifficultyHitObject.cs | 32 ++++++++++++++++++- .../Difficulty/TaikoDifficultyCalculator.cs | 19 +---------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index 54e314f722..1da4a6e994 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -37,6 +37,36 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public readonly HitType? HitType; + /// + /// Creates a list of s from a s. + /// TODO: Review this - this is moved here from TaikoDifficultyCalculator so that TaikoDifficultyCalculator can + /// have less knowledge of implementation details (i.e. creating all the different hitObject lists, and + /// calling FindRepetitionInterval for the final object). The down side of this is + /// TaikoDifficultyHitObejct.CreateDifficultyHitObjects is now pretty much a proxy for this. + /// + /// The beatmap from which the list of is created. + /// The rate at which the gameplay clock is run at. + public static List Create(IBeatmap beatmap, double clockRate) + { + List difficultyHitObject = new List(); + List centreObjects = new List(); + List rimObjects = new List(); + List noteObjects = new List(); + + for (int i = 2; i < beatmap.HitObjects.Count; i++) + { + difficultyHitObject.Add( + new TaikoDifficultyHitObject( + beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, difficultyHitObject, + centreObjects, rimObjects, noteObjects, difficultyHitObject.Count) + ); + } + + // Find repetition interval for the final TaikoDifficultyHitObjectColour + ((TaikoDifficultyHitObject)difficultyHitObject.Last()).Colour?.FindRepetitionInterval(); + return difficultyHitObject; + } + /// /// Creates a new difficulty hit object. /// @@ -52,7 +82,7 @@ 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, + private TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate, List objects, List centreHitObjects, List rimHitObjects, diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 91ae1c4ed2..7898b7994e 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -45,24 +45,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { - List difficultyHitObject = new List(); - List centreObjects = new List(); - List rimObjects = new List(); - List noteObjects = new List(); - - for (int i = 2; i < beatmap.HitObjects.Count; i++) - { - difficultyHitObject.Add( - new TaikoDifficultyHitObject( - beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, difficultyHitObject, - centreObjects, rimObjects, noteObjects, difficultyHitObject.Count) - ); - } - - // Find repetition interval for the final TaikoDifficultyHitObjectColour - // TODO: Might be a good idea to refactor this - ((TaikoDifficultyHitObject)difficultyHitObject.Last()).Colour?.FindRepetitionInterval(); - return difficultyHitObject; + return TaikoDifficultyHitObject.Create(beatmap, clockRate); } protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) From da1d99d5b66aad2fb2fc8ed4fe096309784eed62 Mon Sep 17 00:00:00 2001 From: vun Date: Sun, 19 Jun 2022 17:14:31 +0800 Subject: [PATCH 16/60] Parameter tweaks, change repetition interval definition --- .../Difficulty/Evaluators/ColourEvaluator.cs | 6 +++--- .../Preprocessing/TaikoDifficultyHitObjectColour.cs | 6 ++++-- osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs | 5 +++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 8c225c8459..32bc8429b8 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -18,18 +18,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators TaikoDifficultyHitObjectColour colour = taikoCurrent.Colour; if (colour == null) return 0; - double objectStrain = 2.1; + double objectStrain = 1.85; if (colour.Delta) { - objectStrain *= sigmoid(colour.DeltaRunLength, 4, 4) * 0.5 + 0.5; + objectStrain *= sigmoid(colour.DeltaRunLength, 3, 3) * 0.5 + 0.5; } else { objectStrain *= sigmoid(colour.DeltaRunLength, 2, 2) * 0.5 + 0.5; } - objectStrain *= -sigmoid(colour.RepetitionInterval, 8, 8) * 0.5 + 0.5; + objectStrain *= -sigmoid(colour.RepetitionInterval, 1, 8); // * 0.5 + 0.5; // Console.WriteLine($"{current.StartTime},{colour.Delta},{colour.RepetitionInterval},{objectStrain}"); return objectStrain; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs index a5ca0964df..8be803fd05 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public int RepetitionInterval { get; private set; } + public TaikoDifficultyHitObjectColour repeatedColour { get; private set; } + /// /// Get the instance for the given hitObject. This is implemented /// as a static function instead of constructor to allow for reusing existing instances. @@ -74,14 +76,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing while (other != null && interval < max_repetition_interval) { - interval += other.DeltaRunLength; - if (other.Delta == Delta && other.DeltaRunLength == DeltaRunLength) { RepetitionInterval = Math.Min(interval, max_repetition_interval); + repeatedColour = other; return; } + interval += other.DeltaRunLength; other = other.previous; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs index 171278b4dd..ebbe027f9e 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs @@ -4,14 +4,15 @@ using System.Linq; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { public class Peaks : Skill { private const double rhythm_skill_multiplier = 0.32 * final_multiplier; - private const double colour_skill_multiplier = 0.37 * final_multiplier; - private const double stamina_skill_multiplier = 0.37 * final_multiplier; + private const double colour_skill_multiplier = 0.33 * final_multiplier; + private const double stamina_skill_multiplier = 0.4 * final_multiplier; private const double final_multiplier = 0.047; From 57964311be3410c7df1b563b3fb5b1daa19a752b Mon Sep 17 00:00:00 2001 From: vun Date: Sun, 19 Jun 2022 17:20:53 +0800 Subject: [PATCH 17/60] Revert performance calculator to upstream --- .../Difficulty/TaikoPerformanceCalculator.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index 9eca59bfd7..a9cde62f44 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -35,13 +35,21 @@ 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 - ) * 1.1; + ) * multiplier; return new TaikoPerformanceAttributes { @@ -53,7 +61,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.190) - 4.0, 2.25) / 450.0; + double difficultyValue = Math.Pow(5 * Math.Max(1.0, attributes.StarRating / 0.175) - 4.0, 2.25) / 450.0; double lengthBonus = 1 + 0.1 * Math.Min(1.0, totalHits / 1500.0); difficultyValue *= lengthBonus; @@ -61,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty difficultyValue *= Math.Pow(0.985, countMiss); if (score.Mods.Any(m => m is ModHidden)) - difficultyValue *= 1.125; + difficultyValue *= 1.025; if (score.Mods.Any(m => m is ModFlashlight)) difficultyValue *= 1.05 * lengthBonus; @@ -74,18 +82,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (attributes.GreatHitWindow <= 0) return 0; - double accuracyValue = Math.Pow(150.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(score.Accuracy, 15) * 40.0; + double accValue = Math.Pow(150.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(score.Accuracy, 15) * 22.0; - 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; + // 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)); } private int totalHits => countGreat + countOk + countMeh + countMiss; From 3529514587a5099a54b9e02c6d62321dcd66444e Mon Sep 17 00:00:00 2001 From: vun Date: Sun, 19 Jun 2022 17:26:11 +0800 Subject: [PATCH 18/60] Disablle nullable in TaikoDifficultyHitObjectColour --- .../Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs index 8be803fd05..8304c0b126 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing From c5fd48372ba8d94f39ecfb62d899474ccc32eca7 Mon Sep 17 00:00:00 2001 From: vun Date: Wed, 22 Jun 2022 17:17:19 +0800 Subject: [PATCH 19/60] Flatten speed bonus for stamina --- .../Difficulty/Evaluators/StaminaEvaluator.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs index 6c0c01cb2d..be1514891c 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -13,10 +13,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// /// Applies a speed bonus dependent on the time since the last hit performed using this key. /// - /// The duration between the current and previous note hit using the same key. - private static double speedBonus(double notePairDuration) + /// The interval between the current and previous note hit using the same key. + private static double speedBonus(double interval) { - return Math.Pow(0.4, notePairDuration / 1000); + return Math.Pow(0.8, interval / 1000); } /// @@ -40,9 +40,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators return 0.0; } - double objectStrain = 0; - // Console.WriteLine(speedBonus(taikoCurrent.StartTime - keyPrevious.StartTime)); - objectStrain += speedBonus(taikoCurrent.StartTime - keyPrevious.StartTime); + double objectStrain = 0.85; + objectStrain *= speedBonus(taikoCurrent.StartTime - keyPrevious.StartTime); return objectStrain; } } From f42aac99546889a66303521674283e92e0a240bf Mon Sep 17 00:00:00 2001 From: vun Date: Thu, 23 Jun 2022 17:10:30 +0800 Subject: [PATCH 20/60] TAIKO-6 Pre-evaluate colour to avoid per-note evaluation --- .../Difficulty/Evaluators/ColourEvaluator.cs | 12 ++-- .../Preprocessing/TaikoDifficultyHitObject.cs | 21 +++--- .../TaikoDifficultyHitObjectColour.cs | 65 ++++++++++++------- .../Difficulty/Skills/Colour.cs | 11 +++- 4 files changed, 72 insertions(+), 37 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 32bc8429b8..14f95fbdd5 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -12,10 +12,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators return Math.Tanh(Math.E * -(val - center) / width); } - public static double EvaluateDifficultyOf(DifficultyHitObject current) + public static double EvaluateDifficultyOf(TaikoDifficultyHitObjectColour colour) { - TaikoDifficultyHitObject taikoCurrent = (TaikoDifficultyHitObject)current; - TaikoDifficultyHitObjectColour colour = taikoCurrent.Colour; if (colour == null) return 0; double objectStrain = 1.85; @@ -29,9 +27,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators objectStrain *= sigmoid(colour.DeltaRunLength, 2, 2) * 0.5 + 0.5; } - objectStrain *= -sigmoid(colour.RepetitionInterval, 1, 8); // * 0.5 + 0.5; - // Console.WriteLine($"{current.StartTime},{colour.Delta},{colour.RepetitionInterval},{objectStrain}"); + objectStrain *= -sigmoid(colour.RepetitionInterval, 1, 8); return objectStrain; } + + public static double EvaluateDifficultyOf(DifficultyHitObject current) + { + return EvaluateDifficultyOf(((TaikoDifficultyHitObject)current).Colour); + } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index ed93595e93..f35cf1d8b9 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -10,6 +10,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing { @@ -29,10 +30,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing public readonly TaikoDifficultyHitObjectRhythm Rhythm; /// - /// Colour data for this hit object. This is used by colour evaluator to calculate colour, but can be used - /// differently by other skills in the future. + /// Colour data for this hit object. This is used by colour evaluator to calculate colour difficulty, but can be used + /// by other skills in the future. + /// This need to be writeable by TaikoDifficultyHitObjectColour so that it can assign potentially reused instances /// - public readonly TaikoDifficultyHitObjectColour Colour; + public TaikoDifficultyHitObjectColour Colour; /// /// The hit type of this hit object. @@ -64,8 +66,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing ); } - // Find repetition interval for the final TaikoDifficultyHitObjectColour - ((TaikoDifficultyHitObject)difficultyHitObject.Last()).Colour?.FindRepetitionInterval(); + List colours = TaikoDifficultyHitObjectColour.CreateColoursFor(difficultyHitObject); + + // Pre-evaluate colours + for (int i = 0; i < colours.Count; i++) + { + colours[i].EvaluatedDifficulty = ColourEvaluator.EvaluateDifficultyOf(colours[i]); + } + return difficultyHitObject; } @@ -115,9 +123,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing NoteIndex = noteObjects.Count; noteObjects.Add(this); - - // Need to be done after NoteIndex 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 8304c0b126..74b8899c60 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs @@ -1,6 +1,8 @@ #nullable disable using System; +using System.Collections.Generic; +using osu.Game.Rulesets.Difficulty.Preprocessing; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing { @@ -11,7 +13,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing { private const int max_repetition_interval = 16; - private TaikoDifficultyHitObjectColour previous; + public TaikoDifficultyHitObjectColour Previous { get; private set; } /// /// True if the current colour is different from the previous colour. @@ -30,35 +32,54 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public int RepetitionInterval { get; private set; } + /// + /// Evaluated colour difficulty is cached here, as difficulty don't need to be calculated per-note. + /// + /// TODO: Consider having all evaluated difficulty cached in TaikoDifficultyHitObject instead, since we may be + /// reusing evaluator results in the future. + public double EvaluatedDifficulty; + public TaikoDifficultyHitObjectColour repeatedColour { get; private set; } /// /// Get the instance for the given hitObject. This is implemented /// as a static function instead of constructor to allow for reusing existing instances. - /// TODO: findRepetitionInterval needs to be called a final time after all hitObjects have been processed. /// - public static TaikoDifficultyHitObjectColour GetInstanceFor(TaikoDifficultyHitObject hitObject) + public static List CreateColoursFor(List hitObjects) { - TaikoDifficultyHitObject lastObject = hitObject.PreviousNote(0); - TaikoDifficultyHitObjectColour previous = lastObject?.Colour; - bool delta = lastObject == null || hitObject.HitType != lastObject.HitType; + List colours = new List(); - if (previous != null && delta == previous.Delta) + for (int i = 0; i < hitObjects.Count; i++) { - previous.DeltaRunLength += 1; - return previous; + TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)hitObjects[i]; + 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; + hitObject.Colour = previous; + continue; + } + + TaikoDifficultyHitObjectColour colour = new TaikoDifficultyHitObjectColour() + { + Delta = delta, + DeltaRunLength = 1, + RepetitionInterval = max_repetition_interval + 1, + Previous = previous + }; + hitObject.Colour = colour; + colours.Add(colour); } - // Calculate RepetitionInterval for previous - previous?.FindRepetitionInterval(); - - return new TaikoDifficultyHitObjectColour() + for (int i = 0; i < colours.Count; i++) { - Delta = delta, - DeltaRunLength = 1, - RepetitionInterval = max_repetition_interval + 1, - previous = previous - }; + colours[i].FindRepetitionInterval(); + } + + return colours; } /// @@ -67,14 +88,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public void FindRepetitionInterval() { - if (previous?.previous == null) + if (Previous?.Previous == null) { RepetitionInterval = max_repetition_interval + 1; return; } - int interval = previous.DeltaRunLength; - TaikoDifficultyHitObjectColour other = previous.previous; + int interval = Previous.DeltaRunLength; + TaikoDifficultyHitObjectColour other = Previous.Previous; while (other != null && interval < max_repetition_interval) { @@ -86,7 +107,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing } interval += other.DeltaRunLength; - other = other.previous; + other = other.Previous; } RepetitionInterval = max_repetition_interval + 1; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs index a4f3f460c7..0d69104a58 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -6,7 +6,7 @@ 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; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { @@ -25,7 +25,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { - return ColourEvaluator.EvaluateDifficultyOf(current); + TaikoDifficultyHitObjectColour colour = ((TaikoDifficultyHitObject)current).Colour; + double difficulty = colour == null ? 0 : colour.EvaluatedDifficulty; + // if (current != null && colour != null) + // { + // System.Console.WriteLine($"{current.StartTime},{colour.Delta},{colour.RepetitionInterval},{difficulty}"); + // } + + return difficulty; } } } From 15372267e17c78c919c764362d0d32c5c98d99cd Mon Sep 17 00:00:00 2001 From: vun Date: Sat, 25 Jun 2022 22:42:56 +0800 Subject: [PATCH 21/60] Implement new colour encoding --- .../Difficulty/Evaluators/ColourEvaluator.cs | 16 +--- .../Preprocessing/ColourEncoding.cs | 68 ++++++++++++++++ .../Preprocessing/CoupledColourEncoding.cs | 73 +++++++++++++++++ .../Preprocessing/TaikoDifficultyHitObject.cs | 12 +-- .../TaikoDifficultyHitObjectColour.cs | 81 +++++++------------ .../Difficulty/Skills/Colour.cs | 15 +++- 6 files changed, 188 insertions(+), 77 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/ColourEncoding.cs create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/CoupledColourEncoding.cs diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 14f95fbdd5..1627833e8a 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -14,21 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators public static double EvaluateDifficultyOf(TaikoDifficultyHitObjectColour colour) { - if (colour == null) return 0; - - double objectStrain = 1.85; - - if (colour.Delta) - { - objectStrain *= sigmoid(colour.DeltaRunLength, 3, 3) * 0.5 + 0.5; - } - else - { - objectStrain *= sigmoid(colour.DeltaRunLength, 2, 2) * 0.5 + 0.5; - } - - objectStrain *= -sigmoid(colour.RepetitionInterval, 1, 8); - return objectStrain; + return 1; } public static double EvaluateDifficultyOf(DifficultyHitObject current) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/ColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/ColourEncoding.cs new file mode 100644 index 0000000000..e2ac40170e --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/ColourEncoding.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using osu.Game.Rulesets.Difficulty.Preprocessing; + +namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing +{ + public class ColourEncoding + { + /// + /// Amount consecutive notes of the same colour + /// + public int MonoRunLength = 1; + + /// + /// Amount of consecutive encoding with the same + /// + public int EncodingRunLength = 1; + + /// + /// Beginning index in the data that this encodes + /// + public int StartIndex = 0; + + public bool isIdenticalTo(ColourEncoding other) + { + return other.MonoRunLength == MonoRunLength && other.EncodingRunLength == EncodingRunLength; + } + + public static List Encode(List data) + { + // Encoding mono lengths + List firstPass = new List(); + ColourEncoding? lastEncoded = null; + for (int i = 0; i < data.Count; i++) + { + TaikoDifficultyHitObject taikoObject = (TaikoDifficultyHitObject)data[i]; + // This ignores all non-note objects, which may or may not be the desired behaviour + TaikoDifficultyHitObject previousObject = (TaikoDifficultyHitObject)taikoObject.PreviousNote(0); + + if (previousObject == null || lastEncoded == null || taikoObject.HitType != previousObject.HitType) + { + lastEncoded = new ColourEncoding(); + lastEncoded.StartIndex = i; + firstPass.Add(lastEncoded); + continue; + } + + lastEncoded.MonoRunLength += 1; + } + + // Encode encoding lengths + List secondPass = new List(); + lastEncoded = null; + for (int i = 0; i < firstPass.Count; i++) + { + if (i == 0 || lastEncoded == null || firstPass[i].MonoRunLength != firstPass[i - 1].MonoRunLength) + { + lastEncoded = firstPass[i]; + secondPass.Add(firstPass[i]); + continue; + } + + lastEncoded.EncodingRunLength += 1; + } + + return secondPass; + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/CoupledColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/CoupledColourEncoding.cs new file mode 100644 index 0000000000..81e8ae006f --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/CoupledColourEncoding.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing +{ + public class CoupledColourEncoding + { + public int RunLength = 1; + + public ColourEncoding[] Payload; + + /// + /// Beginning index in the data that this encodes + /// + public int StartIndex { get; private set; } = 0; + + public int EndIndex { get; private set; } = 0; + + private CoupledColourEncoding(ColourEncoding[] payload) + { + Payload = payload; + } + + public static List Encode(List data) + { + List encoded = new List(); + + CoupledColourEncoding? lastEncoded = null; + for (int i = 0; i < data.Count; i++) + { + if (lastEncoded != null) lastEncoded.EndIndex = data[i].StartIndex - 1; + + if (i >= data.Count - 2 || !data[i].isIdenticalTo(data[i + 2])) + { + lastEncoded = new CoupledColourEncoding(new ColourEncoding[] { data[i] }); + lastEncoded.StartIndex = data[i].StartIndex; + } + else + { + lastEncoded = new CoupledColourEncoding(new ColourEncoding[] { data[i], data[i + 1] }); + lastEncoded.StartIndex = data[i].StartIndex; + lastEncoded.RunLength = 3; + i++; + + // Peek 2 indices ahead + while (i < data.Count - 2 && data[i].isIdenticalTo(data[i + 2])) + { + lastEncoded.RunLength += 1; + i++; + } + + // Skip over peeked data + i++; + } + + encoded.Add(lastEncoded); + } + + return encoded; + } + + public bool hasIdenticalPayload(CoupledColourEncoding other) + { + if (this.Payload.Length != other.Payload.Length) return false; + + for (int i = 0; i < this.Payload.Length; i++) + { + if (!this.Payload[i].isIdenticalTo(other.Payload[i])) return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index f35cf1d8b9..1ee905d94c 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -52,21 +52,21 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// The rate at which the gameplay clock is run at. public static List Create(IBeatmap beatmap, double clockRate) { - List difficultyHitObject = new List(); + List difficultyHitObjects = new List(); List centreObjects = new List(); List rimObjects = new List(); List noteObjects = new List(); for (int i = 2; i < beatmap.HitObjects.Count; i++) { - difficultyHitObject.Add( + difficultyHitObjects.Add( new TaikoDifficultyHitObject( - beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, difficultyHitObject, - centreObjects, rimObjects, noteObjects, difficultyHitObject.Count) + beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, difficultyHitObjects, + centreObjects, rimObjects, noteObjects, difficultyHitObjects.Count) ); } - List colours = TaikoDifficultyHitObjectColour.CreateColoursFor(difficultyHitObject); + List colours = TaikoDifficultyHitObjectColour.EncodeAndAssign(difficultyHitObjects); // Pre-evaluate colours for (int i = 0; i < colours.Count; i++) @@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing colours[i].EvaluatedDifficulty = ColourEvaluator.EvaluateDifficultyOf(colours[i]); } - return difficultyHitObject; + return difficultyHitObjects; } /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs index 74b8899c60..6bd99550be 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using osu.Game.Rulesets.Difficulty.Preprocessing; @@ -11,72 +9,51 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public class TaikoDifficultyHitObjectColour { + public CoupledColourEncoding Encoding { get; private set; } + private const int max_repetition_interval = 16; - public TaikoDifficultyHitObjectColour Previous { get; private set; } - - /// - /// True if the current colour is different from the previous colour. - /// - public bool Delta { get; private set; } - - /// - /// How many notes are Delta repeated - /// - public int DeltaRunLength { get; private set; } - /// /// How many notes between the current and previous identical . /// Negative number means that there is no repetition in range. /// If no repetition is found this will have a value of + 1. /// - public int RepetitionInterval { get; private set; } + public int RepetitionInterval { get; private set; } = max_repetition_interval + 1; /// /// Evaluated colour difficulty is cached here, as difficulty don't need to be calculated per-note. /// /// TODO: Consider having all evaluated difficulty cached in TaikoDifficultyHitObject instead, since we may be /// reusing evaluator results in the future. - public double EvaluatedDifficulty; + public double EvaluatedDifficulty = 0; - public TaikoDifficultyHitObjectColour repeatedColour { get; private set; } + public TaikoDifficultyHitObjectColour? Previous { get; private set; } = null; - /// - /// Get the instance for the given hitObject. This is implemented - /// as a static function instead of constructor to allow for reusing existing instances. - /// - public static List CreateColoursFor(List hitObjects) + public TaikoDifficultyHitObjectColour? repeatedColour { get; private set; } = null; + + public TaikoDifficultyHitObjectColour(CoupledColourEncoding encoding) + { + Encoding = encoding; + } + + public static List EncodeAndAssign(List hitObjects) { List colours = new List(); - - for (int i = 0; i < hitObjects.Count; i++) + List encodings = CoupledColourEncoding.Encode(ColourEncoding.Encode(hitObjects)); + TaikoDifficultyHitObjectColour? lastColour = null; + for (int i = 0; i < encodings.Count; i++) { - TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)hitObjects[i]; - TaikoDifficultyHitObject lastObject = hitObject.PreviousNote(0); - TaikoDifficultyHitObjectColour previous = lastObject?.Colour; - bool delta = lastObject == null || hitObject.HitType != lastObject.HitType; - - if (previous != null && delta == previous.Delta) + lastColour = new TaikoDifficultyHitObjectColour(encodings[i]) { - previous.DeltaRunLength += 1; - hitObject.Colour = previous; - continue; - } - - TaikoDifficultyHitObjectColour colour = new TaikoDifficultyHitObjectColour() - { - Delta = delta, - DeltaRunLength = 1, - RepetitionInterval = max_repetition_interval + 1, - Previous = previous + Previous = lastColour }; - hitObject.Colour = colour; - colours.Add(colour); + colours.Add(lastColour); } - for (int i = 0; i < colours.Count; i++) + foreach (TaikoDifficultyHitObjectColour colour in colours) { - colours[i].FindRepetitionInterval(); + colour.FindRepetitionInterval(); + ((TaikoDifficultyHitObject)hitObjects[colour.Encoding.StartIndex]).Colour = colour; } return colours; @@ -94,23 +71,23 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing return; } - int interval = Previous.DeltaRunLength; - TaikoDifficultyHitObjectColour other = Previous.Previous; - - while (other != null && interval < max_repetition_interval) + TaikoDifficultyHitObjectColour? other = Previous.Previous; + int interval = this.Encoding.StartIndex - other.Encoding.EndIndex; + while (interval < max_repetition_interval) { - if (other.Delta == Delta && other.DeltaRunLength == DeltaRunLength) + if (Encoding.hasIdenticalPayload(other.Encoding)) { RepetitionInterval = Math.Min(interval, max_repetition_interval); repeatedColour = other; return; } - interval += other.DeltaRunLength; other = other.Previous; + if (other == null) break; + interval = this.Encoding.StartIndex - other.Encoding.EndIndex; } 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 0d69104a58..13d5cd673e 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -27,10 +27,17 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { TaikoDifficultyHitObjectColour colour = ((TaikoDifficultyHitObject)current).Colour; double difficulty = colour == null ? 0 : colour.EvaluatedDifficulty; - // if (current != null && colour != null) - // { - // System.Console.WriteLine($"{current.StartTime},{colour.Delta},{colour.RepetitionInterval},{difficulty}"); - // } + if (current != null && colour != null) + { + ColourEncoding[] payload = colour.Encoding.Payload; + string payloadDisplay = ""; + for (int i = 0; i < payload.Length; ++i) + { + payloadDisplay += $",({payload[i].MonoRunLength},{payload[i].EncodingRunLength})"; + } + + System.Console.WriteLine($"{current.StartTime},{colour.RepetitionInterval},{colour.Encoding.RunLength}{payloadDisplay}"); + } return difficulty; } From 8c162585b8ebcf37f50009d17e44cff55d732784 Mon Sep 17 00:00:00 2001 From: vun Date: Sat, 25 Jun 2022 22:49:19 +0800 Subject: [PATCH 22/60] Comment out logging for debugging purposes --- .../Difficulty/Skills/Colour.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs index 13d5cd673e..bf359f0d64 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -27,17 +27,17 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { TaikoDifficultyHitObjectColour colour = ((TaikoDifficultyHitObject)current).Colour; double difficulty = colour == null ? 0 : colour.EvaluatedDifficulty; - if (current != null && colour != null) - { - ColourEncoding[] payload = colour.Encoding.Payload; - string payloadDisplay = ""; - for (int i = 0; i < payload.Length; ++i) - { - payloadDisplay += $",({payload[i].MonoRunLength},{payload[i].EncodingRunLength})"; - } + // if (current != null && colour != null) + // { + // ColourEncoding[] payload = colour.Encoding.Payload; + // string payloadDisplay = ""; + // for (int i = 0; i < payload.Length; ++i) + // { + // payloadDisplay += $",({payload[i].MonoRunLength},{payload[i].EncodingRunLength})"; + // } - System.Console.WriteLine($"{current.StartTime},{colour.RepetitionInterval},{colour.Encoding.RunLength}{payloadDisplay}"); - } + // System.Console.WriteLine($"{current.StartTime},{colour.RepetitionInterval},{colour.Encoding.RunLength}{payloadDisplay}"); + // } return difficulty; } From cba47f82020b6c283e3b01c341c41949d5fb88da Mon Sep 17 00:00:00 2001 From: vun Date: Tue, 28 Jun 2022 10:38:58 +0800 Subject: [PATCH 23/60] [WIP] Colour evaluator for new colour encoding --- .../Difficulty/Evaluators/ColourEvaluator.cs | 14 +++++++++-- .../Difficulty/Evaluators/StaminaEvaluator.cs | 5 ++-- .../Preprocessing/ColourEncoding.cs | 17 ++++++++++--- .../Preprocessing/TaikoDifficultyHitObject.cs | 15 +++-------- .../TaikoDifficultyHitObjectColour.cs | 19 ++++++-------- .../Difficulty/Skills/Colour.cs | 25 +++++++++++++------ .../Difficulty/Skills/Peaks.cs | 9 +++---- 7 files changed, 61 insertions(+), 43 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 1627833e8a..60898fe92d 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -12,9 +12,19 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators return Math.Tanh(Math.E * -(val - center) / width); } - public static double EvaluateDifficultyOf(TaikoDifficultyHitObjectColour colour) + public static double EvaluateDifficultyOf(TaikoDifficultyHitObjectColour? colour) { - return 1; + if (colour == null) return 0; + + double difficulty = 7.5 * Math.Log(colour.Encoding.Payload.Length + 1, 10); + // foreach (ColourEncoding encoding in colour.Encoding.Payload) + // { + // difficulty += sigmoid(encoding.MonoRunLength, 1, 1) * 0.4 + 0.6; + // } + difficulty *= -sigmoid(colour.RepetitionInterval, 1, 7); + // difficulty *= -sigmoid(colour.RepetitionInterval, 2, 2) * 0.5 + 0.5; + + return difficulty; } public static double EvaluateDifficultyOf(DifficultyHitObject current) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs index be1514891c..9ebdc90eb8 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -16,7 +16,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// The interval between the current and previous note hit using the same key. private static double speedBonus(double interval) { - return Math.Pow(0.8, interval / 1000); + // return 10 / Math.Pow(interval, 0.6); + return Math.Pow(0.1, interval / 1000); } /// @@ -40,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators return 0.0; } - double objectStrain = 0.85; + double objectStrain = 1; objectStrain *= speedBonus(taikoCurrent.StartTime - keyPrevious.StartTime); return objectStrain; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/ColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/ColourEncoding.cs index e2ac40170e..c090e7aada 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/ColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/ColourEncoding.cs @@ -14,7 +14,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// Amount of consecutive encoding with the same /// public int EncodingRunLength = 1; - + + /// + /// How many notes are encoded with this encoding + /// + public int NoteLength => MonoRunLength + EncodingRunLength; + /// /// Beginning index in the data that this encodes /// @@ -27,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing public static List Encode(List data) { - // Encoding mono lengths + // Compute mono lengths List firstPass = new List(); ColourEncoding? lastEncoded = null; for (int i = 0; i < data.Count; i++) @@ -36,7 +41,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing // This ignores all non-note objects, which may or may not be the desired behaviour TaikoDifficultyHitObject previousObject = (TaikoDifficultyHitObject)taikoObject.PreviousNote(0); - if (previousObject == null || lastEncoded == null || taikoObject.HitType != previousObject.HitType) + if ( + previousObject == null || + lastEncoded == null || + taikoObject.HitType != previousObject.HitType || + taikoObject.Rhythm.Ratio > 1.9) // Reset colour after a slow down of 2x (set as 1.9x for margin of error) { lastEncoded = new ColourEncoding(); lastEncoded.StartIndex = i; @@ -47,7 +56,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing lastEncoded.MonoRunLength += 1; } - // Encode encoding lengths + // Compute encoding lengths List secondPass = new List(); lastEncoded = null; for (int i = 0; i < firstPass.Count; i++) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index 1ee905d94c..7c9188b100 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; @@ -19,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public class TaikoDifficultyHitObject : DifficultyHitObject { - private readonly IReadOnlyList monoDifficultyHitObjects; + private readonly IReadOnlyList? monoDifficultyHitObjects; public readonly int MonoIndex; private readonly IReadOnlyList noteObjects; public readonly int NoteIndex; @@ -34,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// by other skills in the future. /// This need to be writeable by TaikoDifficultyHitObjectColour so that it can assign potentially reused instances /// - public TaikoDifficultyHitObjectColour Colour; + public TaikoDifficultyHitObjectColour? Colour; /// /// The hit type of this hit object. @@ -65,14 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing centreObjects, rimObjects, noteObjects, difficultyHitObjects.Count) ); } - - List colours = TaikoDifficultyHitObjectColour.EncodeAndAssign(difficultyHitObjects); - - // Pre-evaluate colours - for (int i = 0; i < colours.Count; i++) - { - colours[i].EvaluatedDifficulty = ColourEvaluator.EvaluateDifficultyOf(colours[i]); - } + TaikoDifficultyHitObjectColour.EncodeAndAssign(difficultyHitObjects); return difficultyHitObjects; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs index 6bd99550be..7e18332fab 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs @@ -5,12 +5,11 @@ using osu.Game.Rulesets.Difficulty.Preprocessing; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing { /// - /// Stores colour compression information for a . + /// Stores colour compression information for a . This is only present for the + /// first in a chunk. /// public class TaikoDifficultyHitObjectColour { - public CoupledColourEncoding Encoding { get; private set; } - private const int max_repetition_interval = 16; /// @@ -21,11 +20,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing public int RepetitionInterval { get; private set; } = max_repetition_interval + 1; /// - /// Evaluated colour difficulty is cached here, as difficulty don't need to be calculated per-note. + /// Encoding information of . /// - /// TODO: Consider having all evaluated difficulty cached in TaikoDifficultyHitObject instead, since we may be - /// reusing evaluator results in the future. - public double EvaluatedDifficulty = 0; + public CoupledColourEncoding Encoding { get; private set; } public TaikoDifficultyHitObjectColour? Previous { get; private set; } = null; @@ -60,8 +57,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing } /// - /// 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. + /// Finds the closest previous that has the identical . + /// Interval is defined as the amount of chunks between the current and repeated encoding. /// public void FindRepetitionInterval() { @@ -72,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing } TaikoDifficultyHitObjectColour? other = Previous.Previous; - int interval = this.Encoding.StartIndex - other.Encoding.EndIndex; + int interval = 2; while (interval < max_repetition_interval) { if (Encoding.hasIdenticalPayload(other.Encoding)) @@ -84,7 +81,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing other = other.Previous; if (other == null) break; - interval = this.Encoding.StartIndex - other.Encoding.EndIndex; + ++interval; } RepetitionInterval = max_repetition_interval + 1; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs index bf359f0d64..c0dafc73b5 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -1,12 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - +using System; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; +using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { @@ -18,6 +18,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills protected override double SkillMultiplier => 1; protected override double StrainDecayBase => 0.4; + /// + /// Applies a speed bonus dependent on the time since the last hit. + /// + /// The interval between the current and previous note hit using the same key. + private static double speedBonus(double interval) + { + return Math.Pow(0.4, interval / 1000); + } + public Colour(Mod[] mods) : base(mods) { @@ -25,18 +34,20 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { - TaikoDifficultyHitObjectColour colour = ((TaikoDifficultyHitObject)current).Colour; - double difficulty = colour == null ? 0 : colour.EvaluatedDifficulty; - // if (current != null && colour != null) + double difficulty = ColourEvaluator.EvaluateDifficultyOf(current); + // difficulty *= speedBonus(current.DeltaTime); + // TaikoDifficultyHitObject? taikoCurrent = (TaikoDifficultyHitObject)current; + // TaikoDifficultyHitObjectColour? colour = taikoCurrent?.Colour; + // if (taikoCurrent != null && colour != null) // { // ColourEncoding[] payload = colour.Encoding.Payload; // string payloadDisplay = ""; // for (int i = 0; i < payload.Length; ++i) // { - // payloadDisplay += $",({payload[i].MonoRunLength},{payload[i].EncodingRunLength})"; + // payloadDisplay += $",({payload[i].MonoRunLength}|{payload[i].EncodingRunLength})"; // } - // System.Console.WriteLine($"{current.StartTime},{colour.RepetitionInterval},{colour.Encoding.RunLength}{payloadDisplay}"); + // System.Console.WriteLine($"{current.StartTime},{difficulty},{colour.RepetitionInterval},{colour.Encoding.RunLength}{payloadDisplay}"); // } return difficulty; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs index ebbe027f9e..7b6fb7d102 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs @@ -4,17 +4,16 @@ using System.Linq; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { public class Peaks : Skill { - private const double rhythm_skill_multiplier = 0.32 * final_multiplier; - private const double colour_skill_multiplier = 0.33 * final_multiplier; - private const double stamina_skill_multiplier = 0.4 * final_multiplier; + private const double rhythm_skill_multiplier = 0.3 * final_multiplier; + private const double colour_skill_multiplier = 0.39 * final_multiplier; + private const double stamina_skill_multiplier = 0.33 * final_multiplier; - private const double final_multiplier = 0.047; + private const double final_multiplier = 0.06; private readonly Rhythm rhythm; private readonly Colour colour; From 5f8d21f33d60c422eb44d5736e6a66aad41290f2 Mon Sep 17 00:00:00 2001 From: vun Date: Fri, 1 Jul 2022 14:27:23 +0800 Subject: [PATCH 24/60] Per encoding evaluation --- .../Difficulty/Evaluators/ColourEvaluator.cs | 22 +++++++++-- .../Difficulty/Skills/Colour.cs | 39 ++++++++++++------- .../Difficulty/Skills/Peaks.cs | 27 +++++++++++-- .../Difficulty/TaikoDifficultyCalculator.cs | 2 +- 4 files changed, 68 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 60898fe92d..8d9f50eee5 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -12,17 +12,33 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators return Math.Tanh(Math.E * -(val - center) / width); } + public static double EvaluateDifficultyOf(ColourEncoding encoding) + { + return 1 / Math.Pow(encoding.MonoRunLength, 0.5); + } + + public static double EvaluateDifficultyOf(CoupledColourEncoding coupledEncoding) + { + double difficulty = 0; + for (int i = 0; i < coupledEncoding.Payload.Length; i++) + { + difficulty += EvaluateDifficultyOf(coupledEncoding.Payload[i]); + } + return difficulty; + } + public static double EvaluateDifficultyOf(TaikoDifficultyHitObjectColour? colour) { if (colour == null) return 0; - double difficulty = 7.5 * Math.Log(colour.Encoding.Payload.Length + 1, 10); + // double difficulty = 9.5 * Math.Log(colour.Encoding.Payload.Length + 1, 10); + double difficulty = 3 * EvaluateDifficultyOf(colour.Encoding); // foreach (ColourEncoding encoding in colour.Encoding.Payload) // { // difficulty += sigmoid(encoding.MonoRunLength, 1, 1) * 0.4 + 0.6; // } - difficulty *= -sigmoid(colour.RepetitionInterval, 1, 7); - // difficulty *= -sigmoid(colour.RepetitionInterval, 2, 2) * 0.5 + 0.5; + // difficulty *= -sigmoid(colour.RepetitionInterval, 1, 7); + difficulty *= -sigmoid(colour.RepetitionInterval, 6, 5) * 0.5 + 0.5; return difficulty; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs index c0dafc73b5..6b7138fa92 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -35,22 +35,31 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { double difficulty = ColourEvaluator.EvaluateDifficultyOf(current); - // difficulty *= speedBonus(current.DeltaTime); - // TaikoDifficultyHitObject? taikoCurrent = (TaikoDifficultyHitObject)current; - // TaikoDifficultyHitObjectColour? colour = taikoCurrent?.Colour; - // if (taikoCurrent != null && colour != null) - // { - // ColourEncoding[] payload = colour.Encoding.Payload; - // string payloadDisplay = ""; - // for (int i = 0; i < payload.Length; ++i) - // { - // payloadDisplay += $",({payload[i].MonoRunLength}|{payload[i].EncodingRunLength})"; - // } - - // System.Console.WriteLine($"{current.StartTime},{difficulty},{colour.RepetitionInterval},{colour.Encoding.RunLength}{payloadDisplay}"); - // } - return difficulty; } + + // TODO: Remove befor pr + public string GetDebugString(DifficultyHitObject current) + { + double difficulty = ColourEvaluator.EvaluateDifficultyOf(current); + difficulty *= speedBonus(current.DeltaTime); + TaikoDifficultyHitObject? taikoCurrent = (TaikoDifficultyHitObject)current; + TaikoDifficultyHitObjectColour? colour = taikoCurrent?.Colour; + if (taikoCurrent != null && colour != null) + { + ColourEncoding[] payload = colour.Encoding.Payload; + string payloadDisplay = ""; + for (int i = 0; i < payload.Length; ++i) + { + payloadDisplay += $"({payload[i].MonoRunLength}|{payload[i].EncodingRunLength})"; + } + + return $"{current.StartTime},{difficulty},{CurrentStrain},{colour.RepetitionInterval},{colour.Encoding.RunLength},{payloadDisplay}"; + } + else + { + return $"{current.StartTime},{difficulty},{CurrentStrain},0,0,0"; + } + } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs index 7b6fb7d102..1086cf5f72 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs @@ -1,17 +1,19 @@ using System; +using System.IO; using System.Collections.Generic; using System.Linq; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; +using osu.Game.Beatmaps; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { public class Peaks : Skill { private const double rhythm_skill_multiplier = 0.3 * final_multiplier; - private const double colour_skill_multiplier = 0.39 * final_multiplier; - private const double stamina_skill_multiplier = 0.33 * final_multiplier; + private const double colour_skill_multiplier = 0.375 * final_multiplier; + private const double stamina_skill_multiplier = 0.375 * final_multiplier; private const double final_multiplier = 0.06; @@ -23,12 +25,25 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills public double RhythmDifficultyValue => rhythm.DifficultyValue() * rhythm_skill_multiplier; public double StaminaDifficultyValue => stamina.DifficultyValue() * stamina_skill_multiplier; - public Peaks(Mod[] mods) + // TODO: remove before pr + private StreamWriter? colourDebugOutput; + bool debugColour = false; + + public Peaks(Mod[] mods, IBeatmap beatmap) : base(mods) { rhythm = new Rhythm(mods); colour = new Colour(mods); stamina = new Stamina(mods); + + if (debugColour) + { + String filename = $"{beatmap.BeatmapInfo.Metadata.Title}[{beatmap.BeatmapInfo.DifficultyName}].csv"; + filename = filename.Replace('/', '_'); + colourDebugOutput = new StreamWriter(File.OpenWrite($"/run/mount/secondary/workspace/osu/output/colour-debug/{filename}")); + colourDebugOutput.WriteLine("StartTime,Raw,Decayed,RepetitionInterval,EncodingRunLength,Payload"); + } + } /// @@ -43,6 +58,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills rhythm.Process(current); colour.Process(current); stamina.Process(current); + + if (debugColour && colourDebugOutput != null) + { + colourDebugOutput.WriteLine(colour.GetDebugString(current)); + colourDebugOutput.Flush(); + } } /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index ace3599f28..2982861e0b 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { return new Skill[] { - new Peaks(mods) + new Peaks(mods, beatmap) }; } From 505a24a68ef4239df119aec3227f8a9afa15be95 Mon Sep 17 00:00:00 2001 From: vun Date: Tue, 5 Jul 2022 14:41:40 +0800 Subject: [PATCH 25/60] Implement new colour encoding and evaluator --- .../Difficulty/Evaluators/ColourEvaluator.cs | 79 ++++++++---- .../Preprocessing/Colour/ColourEncoding.cs | 42 ++++++ .../Colour/CoupledColourEncoding.cs | 122 ++++++++++++++++++ .../Preprocessing/Colour/MonoEncoding.cs | 40 ++++++ .../Colour/TaikoDifficultyHitObjectColour.cs | 50 +++++++ .../Preprocessing/ColourEncoding.cs | 77 ----------- .../Preprocessing/CoupledColourEncoding.cs | 73 ----------- .../Preprocessing/TaikoDifficultyHitObject.cs | 3 +- .../TaikoDifficultyHitObjectColour.cs | 90 ------------- .../Difficulty/Skills/Colour.cs | 19 ++- .../Difficulty/Skills/Peaks.cs | 4 +- 11 files changed, 326 insertions(+), 273 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/MonoEncoding.cs create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs delete mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/ColourEncoding.cs delete mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/CoupledColourEncoding.cs delete mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 8d9f50eee5..d33f2a85b9 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -1,6 +1,7 @@ using System; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; +using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators { @@ -12,40 +13,70 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators return Math.Tanh(Math.E * -(val - center) / width); } - public static double EvaluateDifficultyOf(ColourEncoding encoding) + private static double sigmoid(double val, double center, double width, double middle, double height) { - return 1 / Math.Pow(encoding.MonoRunLength, 0.5); + return sigmoid(val, center, width) * (height / 2) + middle; } - public static double EvaluateDifficultyOf(CoupledColourEncoding coupledEncoding) + /// + /// Evaluate the difficulty of the first note of a . + /// The encoding to evaluate. + /// The index of the mono encoding within it's parent . + /// + public static double EvaluateDifficultyOf(MonoEncoding encoding, int i) { - double difficulty = 0; - for (int i = 0; i < coupledEncoding.Payload.Length; i++) + return sigmoid(i, 2, 2, 0.5, 1); + } + + /// + /// Evaluate the difficulty of the first note of a . + /// + /// The encoding to evaluate. + /// The index of the colour encoding within it's parent . + public static double EvaluateDifficultyOf(ColourEncoding encoding, int i) + { + return sigmoid(i, 2, 2, 0.5, 1); + } + + /// + /// Evaluate the difficulty of the first note of a . + /// + public static double EvaluateDifficultyOf(CoupledColourEncoding encoding) + { + return 1 - sigmoid(encoding.RepetitionInterval, 2, 2, 0.5, 1); + } + + /// + /// Pre-evaluate and *assign* difficulty values of all hit objects encoded in a . + /// Difficulty values are assigned to of each + /// encoded within. + /// + public static void PreEvaluateDifficulties(CoupledColourEncoding encoding) + { + double coupledEncodingDifficulty = EvaluateDifficultyOf(encoding); + encoding.Payload[0].Payload[0].EncodedData[0].Colour!.EvaluatedDifficulty += coupledEncodingDifficulty; + for (int i = 0; i < encoding.Payload.Count; i++) { - difficulty += EvaluateDifficultyOf(coupledEncoding.Payload[i]); + ColourEncoding colourEncoding = encoding.Payload[i]; + double colourEncodingDifficulty = EvaluateDifficultyOf(colourEncoding, i) * coupledEncodingDifficulty; + colourEncoding.Payload[0].EncodedData[0].Colour!.EvaluatedDifficulty += colourEncodingDifficulty; + for (int j = 0; j < colourEncoding.Payload.Count; j++) + { + MonoEncoding monoEncoding = colourEncoding.Payload[j]; + monoEncoding.EncodedData[0].Colour!.EvaluatedDifficulty += EvaluateDifficultyOf(monoEncoding, j) * colourEncodingDifficulty; + } } - return difficulty; - } - - public static double EvaluateDifficultyOf(TaikoDifficultyHitObjectColour? colour) - { - if (colour == null) return 0; - - // double difficulty = 9.5 * Math.Log(colour.Encoding.Payload.Length + 1, 10); - double difficulty = 3 * EvaluateDifficultyOf(colour.Encoding); - // foreach (ColourEncoding encoding in colour.Encoding.Payload) - // { - // difficulty += sigmoid(encoding.MonoRunLength, 1, 1) * 0.4 + 0.6; - // } - // difficulty *= -sigmoid(colour.RepetitionInterval, 1, 7); - difficulty *= -sigmoid(colour.RepetitionInterval, 6, 5) * 0.5 + 0.5; - - return difficulty; } public static double EvaluateDifficultyOf(DifficultyHitObject current) { - return EvaluateDifficultyOf(((TaikoDifficultyHitObject)current).Colour); + TaikoDifficultyHitObject? taikoObject = current as TaikoDifficultyHitObject; + if (taikoObject != null && taikoObject.Colour != null) + { + return taikoObject.Colour.EvaluatedDifficulty; + } + + return 0; } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs new file mode 100644 index 0000000000..bc427d87ae --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using osu.Game.Rulesets.Difficulty.Preprocessing; + +namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour +{ + public class ColourEncoding + { + public List Payload { get; private set; } = new List(); + + public bool isIdenticalTo(ColourEncoding other) + { + return other.Payload[0].RunLength == Payload[0].RunLength && + other.Payload[0].EncodedData[0].HitType == Payload[0].EncodedData[0].HitType; + } + + public bool hasIdenticalMonoLength(ColourEncoding other) + { + return other.Payload[0].RunLength == Payload[0].RunLength; + } + + public static List Encode(List data) + { + // Compute encoding lengths + List encoded = new List(); + ColourEncoding? lastEncoded = null; + for (int i = 0; i < data.Count; i++) + { + if (i == 0 || lastEncoded == null || data[i].RunLength != data[i - 1].RunLength) + { + lastEncoded = new ColourEncoding(); + lastEncoded.Payload.Add(data[i]); + encoded.Add(lastEncoded); + continue; + } + + lastEncoded.Payload.Add(data[i]); + } + + return encoded; + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs new file mode 100644 index 0000000000..7bdcf50055 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using osu.Game.Rulesets.Difficulty.Preprocessing; + +namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour +{ + public class CoupledColourEncoding + { + private const int max_repetition_interval = 16; + + public List Payload = new List(); + + public CoupledColourEncoding? Previous { get; private set; } = null; + + /// + /// How many notes between the current and previous identical . + /// Negative number means that there is no repetition in range. + /// If no repetition is found this will have a value of + 1. + /// + public int RepetitionInterval { get; private set; } = max_repetition_interval + 1; + + public static List Encode(List data) + { + List firstPass = MonoEncoding.Encode(data); + List secondPass = ColourEncoding.Encode(firstPass); + List thirdPass = CoupledColourEncoding.Encode(secondPass); + + return thirdPass; + } + + public static List Encode(List data) + { + List encoded = new List(); + + CoupledColourEncoding? lastEncoded = null; + for (int i = 0; i < data.Count; i++) + { + lastEncoded = new CoupledColourEncoding() + { + Previous = lastEncoded + }; + + bool isCoupled = i < data.Count - 2 && data[i].isIdenticalTo(data[i + 2]); + if (!isCoupled) + { + lastEncoded.Payload.Add(data[i]); + } + else + { + while (isCoupled) + { + lastEncoded.Payload.Add(data[i]); + i++; + + isCoupled = i < data.Count - 2 && data[i].isIdenticalTo(data[i + 2]); + } + + // Skip over peeked data and add the rest to the payload + lastEncoded.Payload.Add(data[i]); + lastEncoded.Payload.Add(data[i + 1]); + i++; + } + + encoded.Add(lastEncoded); + } + + // Final pass to find repetition interval + for (int i = 0; i < encoded.Count; i++) + { + encoded[i].FindRepetitionInterval(); + } + + return encoded; + } + + /// + /// Returns true if other is considered a repetition of this encoding. This is true if other's first two payload + /// identical mono lengths. + /// + public bool isRepetitionOf(CoupledColourEncoding other) + { + if (this.Payload.Count != other.Payload.Count) return false; + + for (int i = 0; i < Math.Min(this.Payload.Count, 2); i++) + { + if (!this.Payload[i].hasIdenticalMonoLength(other.Payload[i])) return false; + } + + return true; + } + + /// + /// Finds the closest previous that has the identical . + /// Interval is defined as the amount of chunks between the current and repeated encoding. + /// + public void FindRepetitionInterval() + { + if (Previous?.Previous == null) + { + RepetitionInterval = max_repetition_interval + 1; + return; + } + + CoupledColourEncoding? other = Previous.Previous; + int interval = 2; + while (interval < max_repetition_interval) + { + if (this.isRepetitionOf(other)) + { + RepetitionInterval = Math.Min(interval, max_repetition_interval); + return; + } + + other = other.Previous; + if (other == null) break; + ++interval; + } + + RepetitionInterval = max_repetition_interval + 1; + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/MonoEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/MonoEncoding.cs new file mode 100644 index 0000000000..92cdb0667b --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/MonoEncoding.cs @@ -0,0 +1,40 @@ +using osu.Game.Rulesets.Difficulty.Preprocessing; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour +{ + public class MonoEncoding + { + public List EncodedData { get; private set; } = new List(); + + public int RunLength => EncodedData.Count; + + public static List Encode(List data) + { + List encoded = new List(); + + MonoEncoding? lastEncoded = null; + for (int i = 0; i < data.Count; i++) + { + TaikoDifficultyHitObject taikoObject = (TaikoDifficultyHitObject)data[i]; + // This ignores all non-note objects, which may or may not be the desired behaviour + TaikoDifficultyHitObject previousObject = (TaikoDifficultyHitObject)taikoObject.PreviousNote(0); + + if ( + previousObject == null || + lastEncoded == null || + taikoObject.HitType != previousObject.HitType) + { + lastEncoded = new MonoEncoding(); + lastEncoded.EncodedData.Add(taikoObject); + encoded.Add(lastEncoded); + continue; + } + + lastEncoded.EncodedData.Add(taikoObject); + } + + return encoded; + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs new file mode 100644 index 0000000000..7dfd0fdbd4 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; + +namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour +{ + /// + /// Stores colour compression information for a . This is only present for the + /// first in a chunk. + /// + public class TaikoDifficultyHitObjectColour + { + public CoupledColourEncoding Encoding { get; private set; } + + public double EvaluatedDifficulty = 0; + + private TaikoDifficultyHitObjectColour(CoupledColourEncoding encoding) + { + Encoding = encoding; + } + + // TODO: Might wanna move this somewhere else as it is introducing circular references + public static List EncodeAndAssign(List hitObjects) + { + List colours = new List(); + List encodings = CoupledColourEncoding.Encode(hitObjects); + + // Assign colour to objects + encodings.ForEach(coupledEncoding => + { + coupledEncoding.Payload.ForEach(encoding => + { + encoding.Payload.ForEach(mono => + { + mono.EncodedData.ForEach(hitObject => + { + hitObject.Colour = new TaikoDifficultyHitObjectColour(coupledEncoding); + }); + }); + }); + + // Preevaluate and assign difficulty values + ColourEvaluator.PreEvaluateDifficulties(coupledEncoding); + }); + + return colours; + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/ColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/ColourEncoding.cs deleted file mode 100644 index c090e7aada..0000000000 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/ColourEncoding.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Collections.Generic; -using osu.Game.Rulesets.Difficulty.Preprocessing; - -namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing -{ - public class ColourEncoding - { - /// - /// Amount consecutive notes of the same colour - /// - public int MonoRunLength = 1; - - /// - /// Amount of consecutive encoding with the same - /// - public int EncodingRunLength = 1; - - /// - /// How many notes are encoded with this encoding - /// - public int NoteLength => MonoRunLength + EncodingRunLength; - - /// - /// Beginning index in the data that this encodes - /// - public int StartIndex = 0; - - public bool isIdenticalTo(ColourEncoding other) - { - return other.MonoRunLength == MonoRunLength && other.EncodingRunLength == EncodingRunLength; - } - - public static List Encode(List data) - { - // Compute mono lengths - List firstPass = new List(); - ColourEncoding? lastEncoded = null; - for (int i = 0; i < data.Count; i++) - { - TaikoDifficultyHitObject taikoObject = (TaikoDifficultyHitObject)data[i]; - // This ignores all non-note objects, which may or may not be the desired behaviour - TaikoDifficultyHitObject previousObject = (TaikoDifficultyHitObject)taikoObject.PreviousNote(0); - - if ( - previousObject == null || - lastEncoded == null || - taikoObject.HitType != previousObject.HitType || - taikoObject.Rhythm.Ratio > 1.9) // Reset colour after a slow down of 2x (set as 1.9x for margin of error) - { - lastEncoded = new ColourEncoding(); - lastEncoded.StartIndex = i; - firstPass.Add(lastEncoded); - continue; - } - - lastEncoded.MonoRunLength += 1; - } - - // Compute encoding lengths - List secondPass = new List(); - lastEncoded = null; - for (int i = 0; i < firstPass.Count; i++) - { - if (i == 0 || lastEncoded == null || firstPass[i].MonoRunLength != firstPass[i - 1].MonoRunLength) - { - lastEncoded = firstPass[i]; - secondPass.Add(firstPass[i]); - continue; - } - - lastEncoded.EncodingRunLength += 1; - } - - return secondPass; - } - } -} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/CoupledColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/CoupledColourEncoding.cs deleted file mode 100644 index 81e8ae006f..0000000000 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/CoupledColourEncoding.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Collections.Generic; - -namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing -{ - public class CoupledColourEncoding - { - public int RunLength = 1; - - public ColourEncoding[] Payload; - - /// - /// Beginning index in the data that this encodes - /// - public int StartIndex { get; private set; } = 0; - - public int EndIndex { get; private set; } = 0; - - private CoupledColourEncoding(ColourEncoding[] payload) - { - Payload = payload; - } - - public static List Encode(List data) - { - List encoded = new List(); - - CoupledColourEncoding? lastEncoded = null; - for (int i = 0; i < data.Count; i++) - { - if (lastEncoded != null) lastEncoded.EndIndex = data[i].StartIndex - 1; - - if (i >= data.Count - 2 || !data[i].isIdenticalTo(data[i + 2])) - { - lastEncoded = new CoupledColourEncoding(new ColourEncoding[] { data[i] }); - lastEncoded.StartIndex = data[i].StartIndex; - } - else - { - lastEncoded = new CoupledColourEncoding(new ColourEncoding[] { data[i], data[i + 1] }); - lastEncoded.StartIndex = data[i].StartIndex; - lastEncoded.RunLength = 3; - i++; - - // Peek 2 indices ahead - while (i < data.Count - 2 && data[i].isIdenticalTo(data[i + 2])) - { - lastEncoded.RunLength += 1; - i++; - } - - // Skip over peeked data - i++; - } - - encoded.Add(lastEncoded); - } - - return encoded; - } - - public bool hasIdenticalPayload(CoupledColourEncoding other) - { - if (this.Payload.Length != other.Payload.Length) return false; - - for (int i = 0; i < this.Payload.Length; i++) - { - if (!this.Payload[i].isIdenticalTo(other.Payload[i])) return false; - } - - return true; - } - } -} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index 7c9188b100..919770afc8 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -8,6 +8,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing @@ -63,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing centreObjects, rimObjects, noteObjects, difficultyHitObjects.Count) ); } - TaikoDifficultyHitObjectColour.EncodeAndAssign(difficultyHitObjects); + var encoded = TaikoDifficultyHitObjectColour.EncodeAndAssign(difficultyHitObjects); return difficultyHitObjects; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs deleted file mode 100644 index 7e18332fab..0000000000 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectColour.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using osu.Game.Rulesets.Difficulty.Preprocessing; - -namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing -{ - /// - /// Stores colour compression information for a . This is only present for the - /// first in a chunk. - /// - public class TaikoDifficultyHitObjectColour - { - private const int max_repetition_interval = 16; - - /// - /// How many notes between the current and previous identical . - /// Negative number means that there is no repetition in range. - /// If no repetition is found this will have a value of + 1. - /// - public int RepetitionInterval { get; private set; } = max_repetition_interval + 1; - - /// - /// Encoding information of . - /// - public CoupledColourEncoding Encoding { get; private set; } - - public TaikoDifficultyHitObjectColour? Previous { get; private set; } = null; - - public TaikoDifficultyHitObjectColour? repeatedColour { get; private set; } = null; - - public TaikoDifficultyHitObjectColour(CoupledColourEncoding encoding) - { - Encoding = encoding; - } - - public static List EncodeAndAssign(List hitObjects) - { - List colours = new List(); - List encodings = CoupledColourEncoding.Encode(ColourEncoding.Encode(hitObjects)); - TaikoDifficultyHitObjectColour? lastColour = null; - for (int i = 0; i < encodings.Count; i++) - { - lastColour = new TaikoDifficultyHitObjectColour(encodings[i]) - { - Previous = lastColour - }; - colours.Add(lastColour); - } - - foreach (TaikoDifficultyHitObjectColour colour in colours) - { - colour.FindRepetitionInterval(); - ((TaikoDifficultyHitObject)hitObjects[colour.Encoding.StartIndex]).Colour = colour; - } - - return colours; - } - - /// - /// Finds the closest previous that has the identical . - /// Interval is defined as the amount of chunks between the current and repeated encoding. - /// - public void FindRepetitionInterval() - { - if (Previous?.Previous == null) - { - RepetitionInterval = max_repetition_interval + 1; - return; - } - - TaikoDifficultyHitObjectColour? other = Previous.Previous; - int interval = 2; - while (interval < max_repetition_interval) - { - if (Encoding.hasIdenticalPayload(other.Encoding)) - { - RepetitionInterval = Math.Min(interval, max_repetition_interval); - repeatedColour = other; - return; - } - - other = other.Previous; - if (other == null) break; - ++interval; - } - - 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 6b7138fa92..ba4c066a67 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; +using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills @@ -15,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// public class Colour : StrainDecaySkill { - protected override double SkillMultiplier => 1; + protected override double SkillMultiplier => 0.7; protected override double StrainDecayBase => 0.4; /// @@ -38,6 +40,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills return difficulty; } + public static String GetDebugHeaderLabels() + { + return "StartTime,Raw,Decayed,CoupledRunLength,RepetitionInterval,EncodingRunLength,Payload(MonoRunLength|MonoCount)"; + } + // TODO: Remove befor pr public string GetDebugString(DifficultyHitObject current) { @@ -47,18 +54,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills TaikoDifficultyHitObjectColour? colour = taikoCurrent?.Colour; if (taikoCurrent != null && colour != null) { - ColourEncoding[] payload = colour.Encoding.Payload; + List payload = colour.Encoding.Payload; string payloadDisplay = ""; - for (int i = 0; i < payload.Length; ++i) + for (int i = 0; i < payload.Count; ++i) { - payloadDisplay += $"({payload[i].MonoRunLength}|{payload[i].EncodingRunLength})"; + payloadDisplay += $"({payload[i].Payload[0].RunLength}|{payload[i].Payload.Count})"; } - return $"{current.StartTime},{difficulty},{CurrentStrain},{colour.RepetitionInterval},{colour.Encoding.RunLength},{payloadDisplay}"; + return $"{current.StartTime},{difficulty},{CurrentStrain},{colour.Encoding.Payload[0].Payload.Count},{colour.Encoding.RepetitionInterval},{colour.Encoding.Payload.Count},{payloadDisplay}"; } else { - return $"{current.StartTime},{difficulty},{CurrentStrain},0,0,0"; + return $"{current.StartTime},{difficulty},{CurrentStrain},0,0,0,0,0"; } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs index 1086cf5f72..434474055e 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs @@ -38,10 +38,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills if (debugColour) { - String filename = $"{beatmap.BeatmapInfo.Metadata.Title}[{beatmap.BeatmapInfo.DifficultyName}].csv"; + String filename = $"{beatmap.BeatmapInfo.OnlineID} - {beatmap.BeatmapInfo.Metadata.Title}[{beatmap.BeatmapInfo.DifficultyName}].csv"; filename = filename.Replace('/', '_'); colourDebugOutput = new StreamWriter(File.OpenWrite($"/run/mount/secondary/workspace/osu/output/colour-debug/{filename}")); - colourDebugOutput.WriteLine("StartTime,Raw,Decayed,RepetitionInterval,EncodingRunLength,Payload"); + colourDebugOutput.WriteLine(Colour.GetDebugHeaderLabels()); } } From f6dedc77fbc892339c642b6aa154e69ae3dd206b Mon Sep 17 00:00:00 2001 From: vun Date: Tue, 5 Jul 2022 17:01:11 +0800 Subject: [PATCH 26/60] Fixed encoding logic, parameter adjustments --- .../Difficulty/Preprocessing/Colour/ColourEncoding.cs | 3 ++- osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs | 4 ++-- osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs index bc427d87ae..47523189e1 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs @@ -9,7 +9,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour public bool isIdenticalTo(ColourEncoding other) { - return other.Payload[0].RunLength == Payload[0].RunLength && + return hasIdenticalMonoLength(other) && + other.Payload.Count == Payload.Count && other.Payload[0].EncodedData[0].HitType == Payload[0].EncodedData[0].HitType; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs index ba4c066a67..5f9185a547 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -17,8 +17,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// public class Colour : StrainDecaySkill { - protected override double SkillMultiplier => 0.7; - protected override double StrainDecayBase => 0.4; + protected override double SkillMultiplier => 0.2; + protected override double StrainDecayBase => 0.8; /// /// Applies a speed bonus dependent on the time since the last hit. diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs index 434474055e..16e5306d5d 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs @@ -12,8 +12,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills public class Peaks : Skill { private const double rhythm_skill_multiplier = 0.3 * final_multiplier; - private const double colour_skill_multiplier = 0.375 * final_multiplier; - private const double stamina_skill_multiplier = 0.375 * final_multiplier; + private const double colour_skill_multiplier = 0.4 * final_multiplier; + private const double stamina_skill_multiplier = 0.35 * final_multiplier; private const double final_multiplier = 0.06; From 6660379a0eedb15fef1b6fccedcbd58b8c4cfd98 Mon Sep 17 00:00:00 2001 From: vun Date: Thu, 7 Jul 2022 16:04:46 +0800 Subject: [PATCH 27/60] TAIKO-6 Tweak encoding and parameters, reduce rhythm weight --- .../Difficulty/Evaluators/ColourEvaluator.cs | 4 +- .../Difficulty/Evaluators/StaminaEvaluator.cs | 4 +- .../Preprocessing/Colour/ColourEncoding.cs | 2 +- .../Colour/CoupledColourEncoding.cs | 4 +- .../Difficulty/Skills/Colour.cs | 58 +++++++++---------- .../Difficulty/Skills/Peaks.cs | 36 ++++++------ .../Difficulty/Skills/Stamina.cs | 2 +- 7 files changed, 54 insertions(+), 56 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index d33f2a85b9..388858a337 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// public static void PreEvaluateDifficulties(CoupledColourEncoding encoding) { - double coupledEncodingDifficulty = EvaluateDifficultyOf(encoding); + double coupledEncodingDifficulty = 2 * EvaluateDifficultyOf(encoding); encoding.Payload[0].Payload[0].EncodedData[0].Colour!.EvaluatedDifficulty += coupledEncodingDifficulty; for (int i = 0; i < encoding.Payload.Count; i++) { @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators for (int j = 0; j < colourEncoding.Payload.Count; j++) { MonoEncoding monoEncoding = colourEncoding.Payload[j]; - monoEncoding.EncodedData[0].Colour!.EvaluatedDifficulty += EvaluateDifficultyOf(monoEncoding, j) * colourEncodingDifficulty; + monoEncoding.EncodedData[0].Colour!.EvaluatedDifficulty += EvaluateDifficultyOf(monoEncoding, j) * colourEncodingDifficulty * 0.5; } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs index 9ebdc90eb8..6889f0f5e9 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -16,8 +16,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// The interval between the current and previous note hit using the same key. private static double speedBonus(double interval) { - // return 10 / Math.Pow(interval, 0.6); - return Math.Pow(0.1, interval / 1000); + // return 15 / Math.Pow(interval, 0.6); + return Math.Pow(0.2, interval / 1000); } /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs index 47523189e1..18f9d4cf7d 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs @@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { public List Payload { get; private set; } = new List(); - public bool isIdenticalTo(ColourEncoding other) + public bool isRepetitionOf(ColourEncoding other) { return hasIdenticalMonoLength(other) && other.Payload.Count == Payload.Count && diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs index 7bdcf50055..83fd75efa0 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour Previous = lastEncoded }; - bool isCoupled = i < data.Count - 2 && data[i].isIdenticalTo(data[i + 2]); + bool isCoupled = i < data.Count - 2 && data[i].isRepetitionOf(data[i + 2]); if (!isCoupled) { lastEncoded.Payload.Add(data[i]); @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour lastEncoded.Payload.Add(data[i]); i++; - isCoupled = i < data.Count - 2 && data[i].isIdenticalTo(data[i + 2]); + isCoupled = i < data.Count - 2 && data[i].isRepetitionOf(data[i + 2]); } // Skip over peeked data and add the rest to the payload diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs index 5f9185a547..d8f445f37c 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -2,12 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; -using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills @@ -17,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// public class Colour : StrainDecaySkill { - protected override double SkillMultiplier => 0.2; + protected override double SkillMultiplier => 0.12; protected override double StrainDecayBase => 0.8; /// @@ -40,33 +37,34 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills return difficulty; } - public static String GetDebugHeaderLabels() - { - return "StartTime,Raw,Decayed,CoupledRunLength,RepetitionInterval,EncodingRunLength,Payload(MonoRunLength|MonoCount)"; - } + // TODO: Remove before pr + // public static String GetDebugHeaderLabels() + // { + // return "StartTime,Raw,Decayed,CoupledRunLength,RepetitionInterval,EncodingRunLength,Payload(MonoRunLength|MonoCount)"; + // } - // TODO: Remove befor pr - public string GetDebugString(DifficultyHitObject current) - { - double difficulty = ColourEvaluator.EvaluateDifficultyOf(current); - difficulty *= speedBonus(current.DeltaTime); - TaikoDifficultyHitObject? taikoCurrent = (TaikoDifficultyHitObject)current; - TaikoDifficultyHitObjectColour? colour = taikoCurrent?.Colour; - if (taikoCurrent != null && colour != null) - { - List payload = colour.Encoding.Payload; - string payloadDisplay = ""; - for (int i = 0; i < payload.Count; ++i) - { - payloadDisplay += $"({payload[i].Payload[0].RunLength}|{payload[i].Payload.Count})"; - } + // // TODO: Remove before pr + // public string GetDebugString(DifficultyHitObject current) + // { + // double difficulty = ColourEvaluator.EvaluateDifficultyOf(current); + // difficulty *= speedBonus(current.DeltaTime); + // TaikoDifficultyHitObject? taikoCurrent = (TaikoDifficultyHitObject)current; + // TaikoDifficultyHitObjectColour? colour = taikoCurrent?.Colour; + // if (taikoCurrent != null && colour != null) + // { + // List payload = colour.Encoding.Payload; + // string payloadDisplay = ""; + // for (int i = 0; i < payload.Count; ++i) + // { + // payloadDisplay += $"({payload[i].Payload[0].RunLength}|{payload[i].Payload.Count})"; + // } - return $"{current.StartTime},{difficulty},{CurrentStrain},{colour.Encoding.Payload[0].Payload.Count},{colour.Encoding.RepetitionInterval},{colour.Encoding.Payload.Count},{payloadDisplay}"; - } - else - { - return $"{current.StartTime},{difficulty},{CurrentStrain},0,0,0,0,0"; - } - } + // return $"{current.StartTime},{difficulty},{CurrentStrain},{colour.Encoding.Payload[0].Payload.Count},{colour.Encoding.RepetitionInterval},{colour.Encoding.Payload.Count},{payloadDisplay}"; + // } + // else + // { + // return $"{current.StartTime},{difficulty},{CurrentStrain},0,0,0,0,0"; + // } + // } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs index 16e5306d5d..09d9720a4f 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs @@ -11,11 +11,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { public class Peaks : Skill { - private const double rhythm_skill_multiplier = 0.3 * final_multiplier; - private const double colour_skill_multiplier = 0.4 * final_multiplier; - private const double stamina_skill_multiplier = 0.35 * final_multiplier; + private const double rhythm_skill_multiplier = 0.2 * final_multiplier; + private const double colour_skill_multiplier = 0.375 * final_multiplier; + private const double stamina_skill_multiplier = 0.375 * final_multiplier; - private const double final_multiplier = 0.06; + private const double final_multiplier = 0.0625; private readonly Rhythm rhythm; private readonly Colour colour; @@ -26,8 +26,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills public double StaminaDifficultyValue => stamina.DifficultyValue() * stamina_skill_multiplier; // TODO: remove before pr - private StreamWriter? colourDebugOutput; - bool debugColour = false; + // private StreamWriter? colourDebugOutput; + // bool debugColour = false; public Peaks(Mod[] mods, IBeatmap beatmap) : base(mods) @@ -36,13 +36,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills colour = new Colour(mods); stamina = new Stamina(mods); - if (debugColour) - { - String filename = $"{beatmap.BeatmapInfo.OnlineID} - {beatmap.BeatmapInfo.Metadata.Title}[{beatmap.BeatmapInfo.DifficultyName}].csv"; - filename = filename.Replace('/', '_'); - colourDebugOutput = new StreamWriter(File.OpenWrite($"/run/mount/secondary/workspace/osu/output/colour-debug/{filename}")); - colourDebugOutput.WriteLine(Colour.GetDebugHeaderLabels()); - } + // if (debugColour) + // { + // String filename = $"{beatmap.BeatmapInfo.OnlineID} - {beatmap.BeatmapInfo.Metadata.Title}[{beatmap.BeatmapInfo.DifficultyName}].csv"; + // filename = filename.Replace('/', '_'); + // colourDebugOutput = new StreamWriter(File.OpenWrite($"/run/mount/secondary/workspace/osu/output/colour-debug/{filename}")); + // colourDebugOutput.WriteLine(Colour.GetDebugHeaderLabels()); + // } } @@ -59,11 +59,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills colour.Process(current); stamina.Process(current); - if (debugColour && colourDebugOutput != null) - { - colourDebugOutput.WriteLine(colour.GetDebugString(current)); - colourDebugOutput.Flush(); - } + // if (debugColour && colourDebugOutput != null) + // { + // colourDebugOutput.WriteLine(colour.GetDebugString(current)); + // colourDebugOutput.Flush(); + // } } /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 777bd97f81..344004bcf6 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// public class Stamina : StrainDecaySkill { - protected override double SkillMultiplier => 1.2; + protected override double SkillMultiplier => 1.1; protected override double StrainDecayBase => 0.4; /// From 1cb18f84743840a43ced5cc5cae9f9cce78807e1 Mon Sep 17 00:00:00 2001 From: vun Date: Thu, 14 Jul 2022 16:29:23 +0800 Subject: [PATCH 28/60] Refactor colour encoding to avoid circular dependencies --- .../Difficulty/Evaluators/ColourEvaluator.cs | 12 -- .../Difficulty/Evaluators/StaminaEvaluator.cs | 14 +- .../Preprocessing/Colour/ColourEncoding.cs | 41 ++--- .../Colour/CoupledColourEncoding.cs | 75 ++------ .../Preprocessing/Colour/MonoEncoding.cs | 38 ++-- .../TaikoColourDifficultyPreprocessor.cs | 170 ++++++++++++++++++ .../Colour/TaikoDifficultyHitObjectColour.cs | 34 +--- .../Preprocessing/TaikoDifficultyHitObject.cs | 49 +---- .../TaikoDifficultyPreprocessor.cs | 38 ++++ .../Difficulty/Skills/Colour.cs | 70 ++++---- .../Difficulty/Skills/Peaks.cs | 33 ++-- .../Difficulty/TaikoDifficultyCalculator.cs | 2 +- 12 files changed, 326 insertions(+), 250 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 388858a337..2193184355 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -1,5 +1,4 @@ using System; -using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; @@ -67,16 +66,5 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators } } } - - public static double EvaluateDifficultyOf(DifficultyHitObject current) - { - TaikoDifficultyHitObject? taikoObject = current as TaikoDifficultyHitObject; - if (taikoObject != null && taikoObject.Colour != null) - { - return taikoObject.Colour.EvaluatedDifficulty; - } - - return 0; - } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs index 6889f0f5e9..e47e131350 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -16,8 +16,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// The interval between the current and previous note hit using the same key. private static double speedBonus(double interval) { + // Cap to 300bpm 1/4, 50ms note interval, 100ms key interval + // This is a temporary measure to prevent absurdly high speed mono convert maps being rated too high + // There is a plan to replace this with detecting mono that can be hit by special techniques, and this will + // be removed when that is implemented. + interval = Math.Max(interval, 100); + // return 15 / Math.Pow(interval, 0.6); - return Math.Pow(0.2, interval / 1000); + // return Math.Pow(0.2, interval / 1000); + return 30 / interval; } /// @@ -41,8 +48,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators return 0.0; } - double objectStrain = 1; - objectStrain *= speedBonus(taikoCurrent.StartTime - keyPrevious.StartTime); + double objectStrain = 0.5; // Add a base strain to all objects + // double objectStrain = 0; + objectStrain += speedBonus(taikoCurrent.StartTime - keyPrevious.StartTime); return objectStrain; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs index 18f9d4cf7d..052af7a2d1 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs @@ -1,43 +1,38 @@ using System.Collections.Generic; -using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { + /// + /// Encodes a list of s. + /// s with the same are grouped together. + /// public class ColourEncoding { + /// + /// s that are grouped together within this . + /// public List Payload { get; private set; } = new List(); + /// + /// Determine if this is a repetition of another . This + /// is a strict comparison and is true if and only if the colour sequence is exactly the same. + /// This does not require the s to have the same amount of s. + /// public bool isRepetitionOf(ColourEncoding other) { return hasIdenticalMonoLength(other) && other.Payload.Count == Payload.Count && - other.Payload[0].EncodedData[0].HitType == Payload[0].EncodedData[0].HitType; + (other.Payload[0].EncodedData[0].BaseObject as Hit)?.Type == + (Payload[0].EncodedData[0].BaseObject as Hit)?.Type; } + /// + /// Determine if this has the same mono length of another . + /// public bool hasIdenticalMonoLength(ColourEncoding other) { return other.Payload[0].RunLength == Payload[0].RunLength; } - - public static List Encode(List data) - { - // Compute encoding lengths - List encoded = new List(); - ColourEncoding? lastEncoded = null; - for (int i = 0; i < data.Count; i++) - { - if (i == 0 || lastEncoded == null || data[i].RunLength != data[i - 1].RunLength) - { - lastEncoded = new ColourEncoding(); - lastEncoded.Payload.Add(data[i]); - encoded.Add(lastEncoded); - continue; - } - - lastEncoded.Payload.Add(data[i]); - } - - return encoded; - } } } \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs index 83fd75efa0..85a5f14a5d 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs @@ -1,78 +1,35 @@ using System; using System.Collections.Generic; -using osu.Game.Rulesets.Difficulty.Preprocessing; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { + /// + /// Encodes a list of s, grouped together by back and forth repetition of the same + /// . Also stores the repetition interval between this and the previous . + /// public class CoupledColourEncoding { + /// + /// Maximum amount of s to look back to find a repetition. + /// private const int max_repetition_interval = 16; + /// + /// The s that are grouped together within this . + /// public List Payload = new List(); - public CoupledColourEncoding? Previous { get; private set; } = null; + /// + /// The previous . This is used to determine the repetition interval. + /// + public CoupledColourEncoding? Previous = null; /// - /// How many notes between the current and previous identical . - /// Negative number means that there is no repetition in range. + /// How many between the current and previous identical . /// If no repetition is found this will have a value of + 1. /// public int RepetitionInterval { get; private set; } = max_repetition_interval + 1; - public static List Encode(List data) - { - List firstPass = MonoEncoding.Encode(data); - List secondPass = ColourEncoding.Encode(firstPass); - List thirdPass = CoupledColourEncoding.Encode(secondPass); - - return thirdPass; - } - - public static List Encode(List data) - { - List encoded = new List(); - - CoupledColourEncoding? lastEncoded = null; - for (int i = 0; i < data.Count; i++) - { - lastEncoded = new CoupledColourEncoding() - { - Previous = lastEncoded - }; - - bool isCoupled = i < data.Count - 2 && data[i].isRepetitionOf(data[i + 2]); - if (!isCoupled) - { - lastEncoded.Payload.Add(data[i]); - } - else - { - while (isCoupled) - { - lastEncoded.Payload.Add(data[i]); - i++; - - isCoupled = i < data.Count - 2 && data[i].isRepetitionOf(data[i + 2]); - } - - // Skip over peeked data and add the rest to the payload - lastEncoded.Payload.Add(data[i]); - lastEncoded.Payload.Add(data[i + 1]); - i++; - } - - encoded.Add(lastEncoded); - } - - // Final pass to find repetition interval - for (int i = 0; i < encoded.Count; i++) - { - encoded[i].FindRepetitionInterval(); - } - - return encoded; - } - /// /// Returns true if other is considered a repetition of this encoding. This is true if other's first two payload /// identical mono lengths. @@ -90,7 +47,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour } /// - /// Finds the closest previous that has the identical . + /// Finds the closest previous that has the identical . /// Interval is defined as the amount of chunks between the current and repeated encoding. /// public void FindRepetitionInterval() diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/MonoEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/MonoEncoding.cs index 92cdb0667b..98d66f0aa2 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/MonoEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/MonoEncoding.cs @@ -1,40 +1,22 @@ using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Taiko.Objects; using System.Collections.Generic; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { + /// + /// Encode colour information for a sequence of s. Consecutive s + /// of the same are encoded within the same . + /// public class MonoEncoding { + /// + /// List of s that are encoded within this . + /// This is not declared as to avoid circular dependencies. + /// TODO: Review this, are circular dependencies within data-only classes are acceptable? + /// public List EncodedData { get; private set; } = new List(); public int RunLength => EncodedData.Count; - - public static List Encode(List data) - { - List encoded = new List(); - - MonoEncoding? lastEncoded = null; - for (int i = 0; i < data.Count; i++) - { - TaikoDifficultyHitObject taikoObject = (TaikoDifficultyHitObject)data[i]; - // This ignores all non-note objects, which may or may not be the desired behaviour - TaikoDifficultyHitObject previousObject = (TaikoDifficultyHitObject)taikoObject.PreviousNote(0); - - if ( - previousObject == null || - lastEncoded == null || - taikoObject.HitType != previousObject.HitType) - { - lastEncoded = new MonoEncoding(); - lastEncoded.EncodedData.Add(taikoObject); - encoded.Add(lastEncoded); - continue; - } - - lastEncoded.EncodedData.Add(taikoObject); - } - - return encoded; - } } } \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs new file mode 100644 index 0000000000..17337281e2 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -0,0 +1,170 @@ +using System.Collections.Generic; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour +{ + /// + /// Utility class to perform various encodings. This is separated out from the encoding classes to prevent circular + /// dependencies. + /// + public class TaikoColourDifficultyPreprocessor + { + /// + /// Process and encode a list of s into a list of s, + /// assign the appropriate s to each , + /// and preevaluate colour difficulty of each . + /// + public static List ProcessAndAssign(List hitObjects) + { + List colours = new List(); + List encodings = Encode(hitObjects); + + // Assign colour to objects + encodings.ForEach(coupledEncoding => + { + coupledEncoding.Payload.ForEach(encoding => + { + encoding.Payload.ForEach(mono => + { + mono.EncodedData.ForEach(hitObject => + { + hitObject.Colour = new TaikoDifficultyHitObjectColour(coupledEncoding); + }); + }); + }); + + // Preevaluate and assign difficulty values + ColourEvaluator.PreEvaluateDifficulties(coupledEncoding); + }); + + return colours; + } + + /// + /// Encodes a list of s into a list of s. + /// + public static List EncodeMono(List data) + { + List encoded = new List(); + + MonoEncoding? lastEncoded = null; + for (int i = 0; i < data.Count; i++) + { + TaikoDifficultyHitObject taikoObject = (TaikoDifficultyHitObject)data[i]; + // This ignores all non-note objects, which may or may not be the desired behaviour + TaikoDifficultyHitObject previousObject = taikoObject.PreviousNote(0); + + // If the colour changed, or if this is the first object in the run, create a new mono encoding + if ( + previousObject == null || // First object in the list + (taikoObject.BaseObject as Hit)?.Type != (previousObject.BaseObject as Hit)?.Type) + { + lastEncoded = new MonoEncoding(); + lastEncoded.EncodedData.Add(taikoObject); + encoded.Add(lastEncoded); + continue; + } + + // If we're here, we're in the same encoding as the previous object, thus lastEncoded is not null. Add + // the current object to the encoded payload. + lastEncoded!.EncodedData.Add(taikoObject); + } + + return encoded; + } + + /// + /// Encodes a list of s into a list of s. + /// + public static List EncodeColour(List data) + { + List encoded = new List(); + ColourEncoding? lastEncoded = null; + for (int i = 0; i < data.Count; i++) + { + // Starts a new ColourEncoding if the previous MonoEncoding has a different mono length, or if this is + // the first MonoEncoding in the list. + if (lastEncoded == null || data[i].RunLength != data[i - 1].RunLength) + { + lastEncoded = new ColourEncoding(); + lastEncoded.Payload.Add(data[i]); + encoded.Add(lastEncoded); + continue; + } + + // If we're here, we're in the same encoding as the previous object. Add the current MonoEncoding to the + // encoded payload. + lastEncoded.Payload.Add(data[i]); + } + + return encoded; + } + + /// + /// Encodes a list of s into a list of s. + /// + public static List Encode(List data) + { + List firstPass = EncodeMono(data); + List secondPass = EncodeColour(firstPass); + List thirdPass = EncodeCoupledColour(secondPass); + + return thirdPass; + } + + /// + /// Encodes a list of s into a list of s. + /// + public static List EncodeCoupledColour(List data) + { + List encoded = new List(); + CoupledColourEncoding? lastEncoded = null; + for (int i = 0; i < data.Count; i++) + { + // Starts a new CoupledColourEncoding. ColourEncodings that should be grouped together will be handled + // later within this loop. + lastEncoded = new CoupledColourEncoding() + { + Previous = lastEncoded + }; + + // Determine if future ColourEncodings should be grouped. + bool isCoupled = i < data.Count - 2 && data[i].isRepetitionOf(data[i + 2]); + if (!isCoupled) + { + // If not, add the current ColourEncoding to the encoded payload and continue. + lastEncoded.Payload.Add(data[i]); + } + else + { + // If so, add the current ColourEncoding to the encoded payload and start repeatedly checking if + // subsequent ColourEncodings should be grouped by increasing i and doing the appropriate isCoupled + // check; + while (isCoupled) + { + lastEncoded.Payload.Add(data[i]); + i++; + isCoupled = i < data.Count - 2 && data[i].isRepetitionOf(data[i + 2]); + } + + // Skip over peeked data and add the rest to the payload + lastEncoded.Payload.Add(data[i]); + lastEncoded.Payload.Add(data[i + 1]); + i++; + } + + encoded.Add(lastEncoded); + } + + // Final pass to find repetition intervals + for (int i = 0; i < encoded.Count; i++) + { + encoded[i].FindRepetitionInterval(); + } + + return encoded; + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs index 7dfd0fdbd4..9c2e0cc206 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using osu.Game.Rulesets.Difficulty.Preprocessing; -using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; - namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { /// @@ -15,36 +10,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour public double EvaluatedDifficulty = 0; - private TaikoDifficultyHitObjectColour(CoupledColourEncoding encoding) + public TaikoDifficultyHitObjectColour(CoupledColourEncoding encoding) { Encoding = encoding; } - - // TODO: Might wanna move this somewhere else as it is introducing circular references - public static List EncodeAndAssign(List hitObjects) - { - List colours = new List(); - List encodings = CoupledColourEncoding.Encode(hitObjects); - - // Assign colour to objects - encodings.ForEach(coupledEncoding => - { - coupledEncoding.Payload.ForEach(encoding => - { - encoding.Payload.ForEach(mono => - { - mono.EncodedData.ForEach(hitObject => - { - hitObject.Colour = new TaikoDifficultyHitObjectColour(coupledEncoding); - }); - }); - }); - - // Preevaluate and assign difficulty values - ColourEvaluator.PreEvaluateDifficulties(coupledEncoding); - }); - - return colours; - } } } \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index 919770afc8..e490d310fd 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -4,12 +4,10 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; -using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing { @@ -35,40 +33,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public TaikoDifficultyHitObjectColour? Colour; - /// - /// The hit type of this hit object. - /// - public readonly HitType? HitType; - - /// - /// Creates a list of s from a s. - /// TODO: Review this - this is moved here from TaikoDifficultyCalculator so that TaikoDifficultyCalculator can - /// have less knowledge of implementation details (i.e. creating all the different hitObject lists, and - /// calling FindRepetitionInterval for the final object). The down side of this is - /// TaikoDifficultyHitObejct.CreateDifficultyHitObjects is now pretty much a proxy for this. - /// - /// The beatmap from which the list of is created. - /// The rate at which the gameplay clock is run at. - public static List Create(IBeatmap beatmap, double clockRate) - { - List difficultyHitObjects = new List(); - List centreObjects = new List(); - List rimObjects = new List(); - List noteObjects = new List(); - - for (int i = 2; i < beatmap.HitObjects.Count; i++) - { - difficultyHitObjects.Add( - new TaikoDifficultyHitObject( - beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, difficultyHitObjects, - centreObjects, rimObjects, noteObjects, difficultyHitObjects.Count) - ); - } - var encoded = TaikoDifficultyHitObjectColour.EncodeAndAssign(difficultyHitObjects); - - return difficultyHitObjects; - } - /// /// Creates a new difficulty hit object. /// @@ -81,10 +45,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// The list of rim (kat) s in the current beatmap. /// The list of s that is a hit (i.e. not a slider or spinner) in the current beatmap. /// The position of this in the list. - /// - /// TODO: This argument list is getting long, we might want to refactor this into a static method that create - /// all s from a . - private TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate, + public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate, List objects, List centreHitObjects, List rimHitObjects, @@ -95,15 +56,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing this.noteObjects = noteObjects; Rhythm = getClosestRhythm(lastObject, lastLastObject, clockRate); - HitType = currentHit?.Type; + HitType? hitType = currentHit?.Type; - if (HitType == Objects.HitType.Centre) + if (hitType == Objects.HitType.Centre) { MonoIndex = centreHitObjects.Count; centreHitObjects.Add(this); monoDifficultyHitObjects = centreHitObjects; } - else if (HitType == Objects.HitType.Rim) + else if (hitType == Objects.HitType.Rim) { MonoIndex = rimHitObjects.Count; rimHitObjects.Add(this); @@ -111,7 +72,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing } // Need to be done after HitType is set. - if (HitType == null) return; + if (hitType == null) return; NoteIndex = noteObjects.Count; noteObjects.Add(this); diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs new file mode 100644 index 0000000000..e37690d7e8 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; + +namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing +{ + public class TaikoDifficultyPreprocessor + { + /// + /// Creates a list of s from a s. + /// This is placed here in a separate class to avoid having to know + /// too much implementation details of the preprocessing, and avoid + /// having circular dependencies with various preprocessing and evaluator classes. + /// + /// The beatmap from which the list of is created. + /// The rate at which the gameplay clock is run at. + public static List CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) + { + List difficultyHitObjects = new List(); + List centreObjects = new List(); + List rimObjects = new List(); + List noteObjects = new List(); + + for (int i = 2; i < beatmap.HitObjects.Count; i++) + { + difficultyHitObjects.Add( + new TaikoDifficultyHitObject( + beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, difficultyHitObjects, + centreObjects, rimObjects, noteObjects, difficultyHitObjects.Count) + ); + } + var encoded = TaikoColourDifficultyPreprocessor.ProcessAndAssign(difficultyHitObjects); + + return difficultyHitObjects; + } + } +} \ 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 d8f445f37c..e2147fdd85 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; 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.Difficulty.Preprocessing.Colour; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { @@ -15,16 +17,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills public class Colour : StrainDecaySkill { protected override double SkillMultiplier => 0.12; - protected override double StrainDecayBase => 0.8; - /// - /// Applies a speed bonus dependent on the time since the last hit. - /// - /// The interval between the current and previous note hit using the same key. - private static double speedBonus(double interval) - { - return Math.Pow(0.4, interval / 1000); - } + // This is set to decay slower than other skills, due to the fact that only the first note of each Mono/Colour/Coupled + // encoding having any difficulty values, and we want to allow colour difficulty to be able to build up even on + // slower maps. + protected override double StrainDecayBase => 0.8; public Colour(Mod[] mods) : base(mods) @@ -33,38 +30,37 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { - double difficulty = ColourEvaluator.EvaluateDifficultyOf(current); + double difficulty = ((TaikoDifficultyHitObject)current).Colour?.EvaluatedDifficulty ?? 0; return difficulty; } // TODO: Remove before pr - // public static String GetDebugHeaderLabels() - // { - // return "StartTime,Raw,Decayed,CoupledRunLength,RepetitionInterval,EncodingRunLength,Payload(MonoRunLength|MonoCount)"; - // } + public static String GetDebugHeaderLabels() + { + return "StartTime,Raw,Decayed,CoupledRunLength,RepetitionInterval,EncodingRunLength,Payload(MonoRunLength|MonoCount)"; + } - // // TODO: Remove before pr - // public string GetDebugString(DifficultyHitObject current) - // { - // double difficulty = ColourEvaluator.EvaluateDifficultyOf(current); - // difficulty *= speedBonus(current.DeltaTime); - // TaikoDifficultyHitObject? taikoCurrent = (TaikoDifficultyHitObject)current; - // TaikoDifficultyHitObjectColour? colour = taikoCurrent?.Colour; - // if (taikoCurrent != null && colour != null) - // { - // List payload = colour.Encoding.Payload; - // string payloadDisplay = ""; - // for (int i = 0; i < payload.Count; ++i) - // { - // payloadDisplay += $"({payload[i].Payload[0].RunLength}|{payload[i].Payload.Count})"; - // } + // TODO: Remove before pr + public string GetDebugString(DifficultyHitObject current) + { + double difficulty = ((TaikoDifficultyHitObject)current).Colour?.EvaluatedDifficulty ?? 0; + TaikoDifficultyHitObject? taikoCurrent = (TaikoDifficultyHitObject)current; + TaikoDifficultyHitObjectColour? colour = taikoCurrent?.Colour; + if (taikoCurrent != null && colour != null) + { + List payload = colour.Encoding.Payload; + string payloadDisplay = ""; + for (int i = 0; i < payload.Count; ++i) + { + payloadDisplay += $"({payload[i].Payload[0].RunLength}|{payload[i].Payload.Count})"; + } - // return $"{current.StartTime},{difficulty},{CurrentStrain},{colour.Encoding.Payload[0].Payload.Count},{colour.Encoding.RepetitionInterval},{colour.Encoding.Payload.Count},{payloadDisplay}"; - // } - // else - // { - // return $"{current.StartTime},{difficulty},{CurrentStrain},0,0,0,0,0"; - // } - // } + return $"{current.StartTime},{difficulty},{CurrentStrain},{colour.Encoding.Payload[0].Payload.Count},{colour.Encoding.RepetitionInterval},{colour.Encoding.Payload.Count},{payloadDisplay}"; + } + else + { + return $"{current.StartTime},{difficulty},{CurrentStrain},0,0,0,0,0"; + } + } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs index 09d9720a4f..18200fe1bb 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs @@ -26,8 +26,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills public double StaminaDifficultyValue => stamina.DifficultyValue() * stamina_skill_multiplier; // TODO: remove before pr - // private StreamWriter? colourDebugOutput; - // bool debugColour = false; + private StreamWriter? colourDebugOutput; + bool debugColour = false; + private StreamWriter? strainPeakDebugOutput; + bool debugStrain = false; public Peaks(Mod[] mods, IBeatmap beatmap) : base(mods) @@ -36,14 +38,20 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills colour = new Colour(mods); stamina = new Stamina(mods); - // if (debugColour) - // { - // String filename = $"{beatmap.BeatmapInfo.OnlineID} - {beatmap.BeatmapInfo.Metadata.Title}[{beatmap.BeatmapInfo.DifficultyName}].csv"; - // filename = filename.Replace('/', '_'); - // colourDebugOutput = new StreamWriter(File.OpenWrite($"/run/mount/secondary/workspace/osu/output/colour-debug/{filename}")); - // colourDebugOutput.WriteLine(Colour.GetDebugHeaderLabels()); - // } - + if (debugColour) + { + String filename = $"{beatmap.BeatmapInfo.OnlineID} - {beatmap.BeatmapInfo.Metadata.Title}[{beatmap.BeatmapInfo.DifficultyName}].csv"; + filename = filename.Replace('/', '_'); + colourDebugOutput = new StreamWriter(File.OpenWrite($"/run/mount/secondary/workspace/osu/output/colour-debug/{filename}")); + colourDebugOutput.WriteLine(Colour.GetDebugHeaderLabels()); + } + if (debugStrain) + { + String filename = $"{beatmap.BeatmapInfo.OnlineID} - {beatmap.BeatmapInfo.Metadata.Title}[{beatmap.BeatmapInfo.DifficultyName}].csv"; + filename = filename.Replace('/', '_'); + strainPeakDebugOutput = new StreamWriter(File.OpenWrite($"/run/mount/secondary/workspace/osu/output/strain-debug/{filename}")); + strainPeakDebugOutput.WriteLine("Colour,Stamina,Rhythm,Combined"); + } } /// @@ -90,6 +98,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills double peak = norm(1.5, colourPeak, staminaPeak); peak = norm(2, peak, rhythmPeak); + if (debugStrain && strainPeakDebugOutput != null) + { + strainPeakDebugOutput.WriteLine($"{colourPeak},{staminaPeak},{rhythmPeak},{peak}"); + } + // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). // These sections will not contribute to the difficulty. if (peak > 0) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 2982861e0b..195ec92835 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { - return TaikoDifficultyHitObject.Create(beatmap, clockRate); + return TaikoDifficultyPreprocessor.CreateDifficultyHitObjects(beatmap, clockRate); } protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) From 45c055bfa1f4f5f569fc4c1d24582ee322f0076a Mon Sep 17 00:00:00 2001 From: vun Date: Thu, 14 Jul 2022 17:25:21 +0800 Subject: [PATCH 29/60] Move rhythm preprocessing to its own folder --- .../{ => Rhythm}/TaikoDifficultyHitObjectRhythm.cs | 2 +- .../Difficulty/Preprocessing/TaikoDifficultyHitObject.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) rename osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/{ => Rhythm}/TaikoDifficultyHitObjectRhythm.cs (95%) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectRhythm.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Rhythm/TaikoDifficultyHitObjectRhythm.cs similarity index 95% rename from osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectRhythm.cs rename to osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Rhythm/TaikoDifficultyHitObjectRhythm.cs index 526d20e7d7..bf136b9fa6 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObjectRhythm.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Rhythm/TaikoDifficultyHitObjectRhythm.cs @@ -3,7 +3,7 @@ #nullable disable -namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing +namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm { /// /// Represents a rhythm change in a taiko map. diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index e490d310fd..1b5de64ed3 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -8,6 +8,7 @@ using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; +using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing { From 7e3f62a5a54000d74f0ad4b1ec5f69f74ac96cb5 Mon Sep 17 00:00:00 2001 From: Jay L Date: Fri, 15 Jul 2022 21:07:01 +1000 Subject: [PATCH 30/60] Codequality parse --- .../Difficulty/Evaluators/ColourEvaluator.cs | 7 +++- .../Difficulty/Evaluators/StaminaEvaluator.cs | 7 +--- .../Colour/{ => Data}/ColourEncoding.cs | 19 +++++---- .../{ => Data}/CoupledColourEncoding.cs | 21 ++++++---- .../Colour/{ => Data}/MonoEncoding.cs | 7 +++- .../TaikoColourDifficultyPreprocessor.cs | 30 +++++++++----- .../Colour/TaikoDifficultyHitObjectColour.cs | 9 +++- .../Rhythm/TaikoDifficultyHitObjectRhythm.cs | 2 - .../Preprocessing/TaikoDifficultyHitObject.cs | 20 ++++----- .../TaikoDifficultyPreprocessor.cs | 8 +++- .../Difficulty/Skills/Colour.cs | 34 +-------------- .../Difficulty/Skills/Peaks.cs | 41 +++---------------- .../Difficulty/TaikoDifficultyCalculator.cs | 2 +- 13 files changed, 89 insertions(+), 118 deletions(-) rename osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/{ => Data}/ColourEncoding.cs (70%) rename osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/{ => Data}/CoupledColourEncoding.cs (83%) rename osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/{ => Data}/MonoEncoding.cs (86%) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 2193184355..1d857a1dbb 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -1,12 +1,15 @@ +// 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.Taiko.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; +using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data; 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); @@ -54,11 +57,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators { double coupledEncodingDifficulty = 2 * EvaluateDifficultyOf(encoding); encoding.Payload[0].Payload[0].EncodedData[0].Colour!.EvaluatedDifficulty += coupledEncodingDifficulty; + for (int i = 0; i < encoding.Payload.Count; i++) { ColourEncoding colourEncoding = encoding.Payload[i]; double colourEncodingDifficulty = EvaluateDifficultyOf(colourEncoding, i) * coupledEncodingDifficulty; colourEncoding.Payload[0].EncodedData[0].Colour!.EvaluatedDifficulty += colourEncodingDifficulty; + for (int j = 0; j < colourEncoding.Payload.Count; j++) { MonoEncoding monoEncoding = colourEncoding.Payload[j]; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs index e47e131350..30918681f6 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -22,8 +22,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators // be removed when that is implemented. interval = Math.Max(interval, 100); - // return 15 / Math.Pow(interval, 0.6); - // return Math.Pow(0.2, interval / 1000); return 30 / interval; } @@ -33,14 +31,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// public static double EvaluateDifficultyOf(DifficultyHitObject current) { - if (!(current.BaseObject is Hit)) + if (current.BaseObject is not Hit) { return 0.0; } // 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); + TaikoDifficultyHitObject? keyPrevious = taikoCurrent.PreviousMono(1); if (keyPrevious == null) { @@ -49,7 +47,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators } double objectStrain = 0.5; // Add a base strain to all objects - // double objectStrain = 0; objectStrain += speedBonus(taikoCurrent.StartTime - keyPrevious.StartTime); return objectStrain; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs similarity index 70% rename from osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs rename to osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs index 052af7a2d1..cddf8816f5 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/ColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs @@ -1,7 +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.Collections.Generic; using osu.Game.Rulesets.Taiko.Objects; -namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour +namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data { /// /// Encodes a list of s. @@ -19,20 +22,20 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour /// is a strict comparison and is true if and only if the colour sequence is exactly the same. /// This does not require the s to have the same amount of s. /// - public bool isRepetitionOf(ColourEncoding other) + public bool IsRepetitionOf(ColourEncoding other) { - return hasIdenticalMonoLength(other) && - other.Payload.Count == Payload.Count && - (other.Payload[0].EncodedData[0].BaseObject as Hit)?.Type == - (Payload[0].EncodedData[0].BaseObject as Hit)?.Type; + return HasIdenticalMonoLength(other) && + other.Payload.Count == Payload.Count && + (other.Payload[0].EncodedData[0].BaseObject as Hit)?.Type == + (Payload[0].EncodedData[0].BaseObject as Hit)?.Type; } /// /// Determine if this has the same mono length of another . /// - public bool hasIdenticalMonoLength(ColourEncoding other) + public bool HasIdenticalMonoLength(ColourEncoding other) { return other.Payload[0].RunLength == Payload[0].RunLength; } } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs similarity index 83% rename from osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs rename to osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs index 85a5f14a5d..188d1b686b 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/CoupledColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs @@ -1,10 +1,13 @@ +// 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 System.Collections.Generic; -namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour +namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data { /// - /// Encodes a list of s, grouped together by back and forth repetition of the same + /// Encodes a list of s, grouped together by back and forth repetition of the same /// . Also stores the repetition interval between this and the previous . /// public class CoupledColourEncoding @@ -34,13 +37,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour /// Returns true if other is considered a repetition of this encoding. This is true if other's first two payload /// identical mono lengths. /// - public bool isRepetitionOf(CoupledColourEncoding other) + private bool isRepetitionOf(CoupledColourEncoding other) { - if (this.Payload.Count != other.Payload.Count) return false; + if (Payload.Count != other.Payload.Count) return false; - for (int i = 0; i < Math.Min(this.Payload.Count, 2); i++) + for (int i = 0; i < Math.Min(Payload.Count, 2); i++) { - if (!this.Payload[i].hasIdenticalMonoLength(other.Payload[i])) return false; + if (!Payload[i].HasIdenticalMonoLength(other.Payload[i])) return false; } return true; @@ -60,9 +63,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour CoupledColourEncoding? other = Previous.Previous; int interval = 2; + while (interval < max_repetition_interval) { - if (this.isRepetitionOf(other)) + if (isRepetitionOf(other)) { RepetitionInterval = Math.Min(interval, max_repetition_interval); return; @@ -70,10 +74,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour other = other.Previous; if (other == null) break; + ++interval; } RepetitionInterval = max_repetition_interval + 1; } } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/MonoEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs similarity index 86% rename from osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/MonoEncoding.cs rename to osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs index 98d66f0aa2..9e60946bd1 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/MonoEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs @@ -1,8 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Objects; using System.Collections.Generic; -namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour +namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data { /// /// Encode colour information for a sequence of s. Consecutive s @@ -19,4 +22,4 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour public int RunLength => EncodedData.Count; } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs index 17337281e2..a699422d2c 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -1,6 +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.Collections.Generic; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; +using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data; using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour @@ -50,16 +54,20 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour List encoded = new List(); MonoEncoding? lastEncoded = null; + for (int i = 0; i < data.Count; i++) { TaikoDifficultyHitObject taikoObject = (TaikoDifficultyHitObject)data[i]; // This ignores all non-note objects, which may or may not be the desired behaviour - TaikoDifficultyHitObject previousObject = taikoObject.PreviousNote(0); + TaikoDifficultyHitObject? previousObject = taikoObject.PreviousNote(0); // If the colour changed, or if this is the first object in the run, create a new mono encoding - if ( + if + ( previousObject == null || // First object in the list - (taikoObject.BaseObject as Hit)?.Type != (previousObject.BaseObject as Hit)?.Type) + (taikoObject.BaseObject as Hit)?.Type != (previousObject.BaseObject as Hit)?.Type + ) + { lastEncoded = new MonoEncoding(); lastEncoded.EncodedData.Add(taikoObject); @@ -82,6 +90,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { List encoded = new List(); ColourEncoding? lastEncoded = null; + for (int i = 0; i < data.Count; i++) { // Starts a new ColourEncoding if the previous MonoEncoding has a different mono length, or if this is @@ -121,17 +130,19 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { List encoded = new List(); CoupledColourEncoding? lastEncoded = null; + for (int i = 0; i < data.Count; i++) { // Starts a new CoupledColourEncoding. ColourEncodings that should be grouped together will be handled // later within this loop. - lastEncoded = new CoupledColourEncoding() + lastEncoded = new CoupledColourEncoding { Previous = lastEncoded }; // Determine if future ColourEncodings should be grouped. - bool isCoupled = i < data.Count - 2 && data[i].isRepetitionOf(data[i + 2]); + bool isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]); + if (!isCoupled) { // If not, add the current ColourEncoding to the encoded payload and continue. @@ -139,14 +150,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour } else { - // If so, add the current ColourEncoding to the encoded payload and start repeatedly checking if - // subsequent ColourEncodings should be grouped by increasing i and doing the appropriate isCoupled - // check; + // If so, add the current ColourEncoding to the encoded payload and start repeatedly checking if the + // subsequent ColourEncodings should be grouped by increasing i and doing the appropriate isCoupled check. while (isCoupled) { lastEncoded.Payload.Add(data[i]); i++; - isCoupled = i < data.Count - 2 && data[i].isRepetitionOf(data[i + 2]); + isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]); } // Skip over peeked data and add the rest to the payload @@ -167,4 +177,4 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour return encoded; } } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs index 9c2e0cc206..9fc7a1dacb 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs @@ -1,3 +1,8 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data; + namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { /// @@ -6,7 +11,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour /// public class TaikoDifficultyHitObjectColour { - public CoupledColourEncoding Encoding { get; private set; } + public CoupledColourEncoding Encoding { get; } public double EvaluatedDifficulty = 0; @@ -15,4 +20,4 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour Encoding = encoding; } } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Rhythm/TaikoDifficultyHitObjectRhythm.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Rhythm/TaikoDifficultyHitObjectRhythm.cs index bf136b9fa6..a273d7e2ea 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Rhythm/TaikoDifficultyHitObjectRhythm.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Rhythm/TaikoDifficultyHitObjectRhythm.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm { /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index 1b5de64ed3..a0d2fc7797 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -47,10 +47,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// The list of s that is a hit (i.e. not a slider or spinner) in the current beatmap. /// The position of this in the list. public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate, - List objects, - List centreHitObjects, - List rimHitObjects, - List noteObjects, int index) + List objects, + List centreHitObjects, + List rimHitObjects, + List noteObjects, int index) : base(hitObject, lastObject, clockRate, objects, index) { var currentHit = hitObject as Hit; @@ -59,13 +59,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing Rhythm = getClosestRhythm(lastObject, lastLastObject, clockRate); HitType? hitType = currentHit?.Type; - if (hitType == Objects.HitType.Centre) + if (hitType == HitType.Centre) { MonoIndex = centreHitObjects.Count; centreHitObjects.Add(this); monoDifficultyHitObjects = centreHitObjects; } - else if (hitType == Objects.HitType.Rim) + else if (hitType == HitType.Rim) { MonoIndex = rimHitObjects.Count; rimHitObjects.Add(this); @@ -116,12 +116,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing return common_rhythms.OrderBy(x => Math.Abs(x.Ratio - ratio)).First(); } - public TaikoDifficultyHitObject PreviousMono(int backwardsIndex) => monoDifficultyHitObjects.ElementAtOrDefault(MonoIndex - (backwardsIndex + 1)); + public TaikoDifficultyHitObject? PreviousMono(int backwardsIndex) => monoDifficultyHitObjects?.ElementAtOrDefault(MonoIndex - (backwardsIndex + 1)); - public TaikoDifficultyHitObject NextMono(int forwardsIndex) => monoDifficultyHitObjects.ElementAtOrDefault(MonoIndex + (forwardsIndex + 1)); + public TaikoDifficultyHitObject? NextMono(int forwardsIndex) => monoDifficultyHitObjects?.ElementAtOrDefault(MonoIndex + (forwardsIndex + 1)); - public TaikoDifficultyHitObject PreviousNote(int backwardsIndex) => noteObjects.ElementAtOrDefault(NoteIndex - (backwardsIndex + 1)); + public TaikoDifficultyHitObject? PreviousNote(int backwardsIndex) => noteObjects.ElementAtOrDefault(NoteIndex - (backwardsIndex + 1)); - public TaikoDifficultyHitObject NextNote(int forwardsIndex) => noteObjects.ElementAtOrDefault(NoteIndex + (forwardsIndex + 1)); + public TaikoDifficultyHitObject? NextNote(int forwardsIndex) => noteObjects.ElementAtOrDefault(NoteIndex + (forwardsIndex + 1)); } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs index e37690d7e8..fa8e6a8a26 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System.Collections.Generic; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Beatmaps; @@ -30,9 +33,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing centreObjects, rimObjects, noteObjects, difficultyHitObjects.Count) ); } - var encoded = TaikoColourDifficultyPreprocessor.ProcessAndAssign(difficultyHitObjects); + + TaikoColourDifficultyPreprocessor.ProcessAndAssign(difficultyHitObjects); return difficultyHitObjects; } } -} \ 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 e2147fdd85..8f8f62d214 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -1,13 +1,12 @@ // 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 System.Collections.Generic; +#nullable disable + using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; -using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { @@ -33,34 +32,5 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills double difficulty = ((TaikoDifficultyHitObject)current).Colour?.EvaluatedDifficulty ?? 0; return difficulty; } - - // TODO: Remove before pr - public static String GetDebugHeaderLabels() - { - return "StartTime,Raw,Decayed,CoupledRunLength,RepetitionInterval,EncodingRunLength,Payload(MonoRunLength|MonoCount)"; - } - - // TODO: Remove before pr - public string GetDebugString(DifficultyHitObject current) - { - double difficulty = ((TaikoDifficultyHitObject)current).Colour?.EvaluatedDifficulty ?? 0; - TaikoDifficultyHitObject? taikoCurrent = (TaikoDifficultyHitObject)current; - TaikoDifficultyHitObjectColour? colour = taikoCurrent?.Colour; - if (taikoCurrent != null && colour != null) - { - List payload = colour.Encoding.Payload; - string payloadDisplay = ""; - for (int i = 0; i < payload.Count; ++i) - { - payloadDisplay += $"({payload[i].Payload[0].RunLength}|{payload[i].Payload.Count})"; - } - - return $"{current.StartTime},{difficulty},{CurrentStrain},{colour.Encoding.Payload[0].Payload.Count},{colour.Encoding.RepetitionInterval},{colour.Encoding.Payload.Count},{payloadDisplay}"; - } - else - { - return $"{current.StartTime},{difficulty},{CurrentStrain},0,0,0,0,0"; - } - } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs index 18200fe1bb..3e3bc543e1 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs @@ -1,11 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable disable + using System; -using System.IO; using System.Collections.Generic; using System.Linq; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; -using osu.Game.Beatmaps; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { @@ -25,33 +28,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills public double RhythmDifficultyValue => rhythm.DifficultyValue() * rhythm_skill_multiplier; public double StaminaDifficultyValue => stamina.DifficultyValue() * stamina_skill_multiplier; - // TODO: remove before pr - private StreamWriter? colourDebugOutput; - bool debugColour = false; - private StreamWriter? strainPeakDebugOutput; - bool debugStrain = false; - - public Peaks(Mod[] mods, IBeatmap beatmap) + public Peaks(Mod[] mods) : base(mods) { rhythm = new Rhythm(mods); colour = new Colour(mods); stamina = new Stamina(mods); - - if (debugColour) - { - String filename = $"{beatmap.BeatmapInfo.OnlineID} - {beatmap.BeatmapInfo.Metadata.Title}[{beatmap.BeatmapInfo.DifficultyName}].csv"; - filename = filename.Replace('/', '_'); - colourDebugOutput = new StreamWriter(File.OpenWrite($"/run/mount/secondary/workspace/osu/output/colour-debug/{filename}")); - colourDebugOutput.WriteLine(Colour.GetDebugHeaderLabels()); - } - if (debugStrain) - { - String filename = $"{beatmap.BeatmapInfo.OnlineID} - {beatmap.BeatmapInfo.Metadata.Title}[{beatmap.BeatmapInfo.DifficultyName}].csv"; - filename = filename.Replace('/', '_'); - strainPeakDebugOutput = new StreamWriter(File.OpenWrite($"/run/mount/secondary/workspace/osu/output/strain-debug/{filename}")); - strainPeakDebugOutput.WriteLine("Colour,Stamina,Rhythm,Combined"); - } } /// @@ -66,12 +48,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills rhythm.Process(current); colour.Process(current); stamina.Process(current); - - // if (debugColour && colourDebugOutput != null) - // { - // colourDebugOutput.WriteLine(colour.GetDebugString(current)); - // colourDebugOutput.Flush(); - // } } /// @@ -98,11 +74,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills double peak = norm(1.5, colourPeak, staminaPeak); peak = norm(2, peak, rhythmPeak); - if (debugStrain && strainPeakDebugOutput != null) - { - strainPeakDebugOutput.WriteLine($"{colourPeak},{staminaPeak},{rhythmPeak},{peak}"); - } - // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). // These sections will not contribute to the difficulty. if (peak > 0) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 195ec92835..22976d6a0b 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { return new Skill[] { - new Peaks(mods, beatmap) + new Peaks(mods) }; } From c8b7902a630f5a2b532ca411d6146f3a37ae640c Mon Sep 17 00:00:00 2001 From: Jay L Date: Fri, 15 Jul 2022 22:10:20 +1000 Subject: [PATCH 31/60] Reintroduce Convert Nerf, Rescale Multiplier --- .../Difficulty/TaikoDifficultyCalculator.cs | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 22976d6a0b..a70d3d9a38 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { public class TaikoDifficultyCalculator : DifficultyCalculator { - private const double difficulty_multiplier = 1.9; + private const double difficulty_multiplier = 1.35; public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) @@ -57,12 +57,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty var combined = (Peaks)skills[0]; - 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 colourRating = combined.ColourDifficultyValue * difficulty_multiplier; + double rhythmRating = combined.RhythmDifficultyValue * difficulty_multiplier; + double staminaRating = combined.StaminaDifficultyValue * difficulty_multiplier; - double combinedRating = combined.DifficultyValue(); - double starRating = rescale(combinedRating * difficulty_multiplier); + double combinedRating = combined.DifficultyValue() * difficulty_multiplier; + double starRating = rescale(combinedRating * 1.4); + + // TODO: This is temporary measure as we don't detect abuse-type playstyles of converts within the current system. + if (beatmap.BeatmapInfo.Ruleset.OnlineID == 0) + { + starRating *= 0.80; + } HitWindows hitWindows = new TaikoHitWindows(); hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); @@ -81,7 +87,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty } /// - /// Applies a final re-scaling of the star rating to bring maps with recorded full combos below 9.5 stars. + /// Applies a final re-scaling of the star rating. /// /// The raw star rating value before re-scaling. private double rescale(double sr) From 8a17b509d91d6924ec79181c4b4c99f264d0694c Mon Sep 17 00:00:00 2001 From: Jay L Date: Sat, 16 Jul 2022 21:20:25 +1000 Subject: [PATCH 32/60] Increase SpeedBonus Cap to 600BPM --- .../Difficulty/Evaluators/StaminaEvaluator.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs index 30918681f6..5906f39c33 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -16,13 +16,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// The interval between the current and previous note hit using the same key. private static double speedBonus(double interval) { - // Cap to 300bpm 1/4, 50ms note interval, 100ms key interval - // This is a temporary measure to prevent absurdly high speed mono convert maps being rated too high - // There is a plan to replace this with detecting mono that can be hit by special techniques, and this will - // be removed when that is implemented. + // Cap to 600bpm 1/4, 25ms note interval, 50ms key interval + // This is a measure to prevent absurdly high speed maps giving infinity/negative values. interval = Math.Max(interval, 100); - return 30 / interval; + return 60 / interval; } /// From 8beb5568b82108664ff3f97b9bee0808477f5bd8 Mon Sep 17 00:00:00 2001 From: vun Date: Sat, 16 Jul 2022 19:45:35 +0800 Subject: [PATCH 33/60] Fix speed bonus --- .../Difficulty/Evaluators/StaminaEvaluator.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs index 5906f39c33..35f1d386a4 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -17,10 +17,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators private static double speedBonus(double interval) { // Cap to 600bpm 1/4, 25ms note interval, 50ms key interval - // This is a measure to prevent absurdly high speed maps giving infinity/negative values. - interval = Math.Max(interval, 100); + // This is temporary measure to prevent mono abuses. Once that is properly addressed, interval will be + // capped at a very small value to avoid infinite/negative speed bonuses. + interval = Math.Max(interval, 50); - return 60 / interval; + return 30 / interval; } /// From a66fd87274ee4576cb3744f9040980d85a3600ba Mon Sep 17 00:00:00 2001 From: vun Date: Sat, 16 Jul 2022 19:48:29 +0800 Subject: [PATCH 34/60] Fix speed bonus comment --- .../Difficulty/Evaluators/StaminaEvaluator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs index 35f1d386a4..f9f27de9c4 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators private static double speedBonus(double interval) { // Cap to 600bpm 1/4, 25ms note interval, 50ms key interval - // This is temporary measure to prevent mono abuses. Once that is properly addressed, interval will be + // This a is temporary measure to prevent mono abuses. Once that is properly addressed, interval will be // capped at a very small value to avoid infinite/negative speed bonuses. interval = Math.Max(interval, 50); From e82e11ead59a298e6a7e507d271b8eec8b9c0535 Mon Sep 17 00:00:00 2001 From: Jay L Date: Sun, 17 Jul 2022 14:56:07 +1000 Subject: [PATCH 35/60] Fix SpeedBonus xml --- .idea/.idea.osu/.idea/discord.xml | 7 +++++++ .../Difficulty/Evaluators/StaminaEvaluator.cs | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 .idea/.idea.osu/.idea/discord.xml diff --git a/.idea/.idea.osu/.idea/discord.xml b/.idea/.idea.osu/.idea/discord.xml new file mode 100644 index 0000000000..30bab2abb1 --- /dev/null +++ b/.idea/.idea.osu/.idea/discord.xml @@ -0,0 +1,7 @@ + + + + + \ 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 f9f27de9c4..954c1661dd 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -17,8 +17,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators private static double speedBonus(double interval) { // Cap to 600bpm 1/4, 25ms note interval, 50ms key interval - // This a is temporary measure to prevent mono abuses. Once that is properly addressed, interval will be - // capped at a very small value to avoid infinite/negative speed bonuses. + // Interval will be capped at a very small value to avoid infinite/negative speed bonuses. + // TODO - This is a temporary measure as we need to implement methods of detecting playstyle-abuse of SpeedBonus. interval = Math.Max(interval, 50); return 30 / interval; From 77fa5674532cdd9c21e2e7697e86eeca8f14356a Mon Sep 17 00:00:00 2001 From: Jay L Date: Sun, 17 Jul 2022 14:56:25 +1000 Subject: [PATCH 36/60] Adjust tests --- .../TaikoDifficultyCalculatorTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs index 579b461624..425f72cadc 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -16,13 +16,13 @@ namespace osu.Game.Rulesets.Taiko.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; - [TestCase(1.9971301024093662d, 200, "diffcalc-test")] - [TestCase(1.9971301024093662d, 200, "diffcalc-test-strong")] + [TestCase(3.1098944660126882d, 200, "diffcalc-test")] + [TestCase(3.1098944660126882d, 200, "diffcalc-test-strong")] public void Test(double expectedStarRating, int expectedMaxCombo, string name) => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(3.1645810961313674d, 200, "diffcalc-test")] - [TestCase(3.1645810961313674d, 200, "diffcalc-test-strong")] + [TestCase(4.0974106752474251d, 200, "diffcalc-test")] + [TestCase(4.0974106752474251d, 200, "diffcalc-test-strong")] public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new TaikoModDoubleTime()); From 9e299bb88b3d1938fb25ef18ec29aa49396d45bd Mon Sep 17 00:00:00 2001 From: Jay L Date: Sun, 17 Jul 2022 18:54:26 +1000 Subject: [PATCH 37/60] Delete Idea Project Notation --- .idea/.idea.osu/.idea/discord.xml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 .idea/.idea.osu/.idea/discord.xml diff --git a/.idea/.idea.osu/.idea/discord.xml b/.idea/.idea.osu/.idea/discord.xml deleted file mode 100644 index 30bab2abb1..0000000000 --- a/.idea/.idea.osu/.idea/discord.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file From cb63ec282e135d19bb7330a518c186f24a8f4e5f Mon Sep 17 00:00:00 2001 From: Jay L Date: Wed, 20 Jul 2022 23:33:38 +1000 Subject: [PATCH 38/60] Partial Review changes --- .../Difficulty/Evaluators/ColourEvaluator.cs | 24 +++++++++++-------- .../Difficulty/Evaluators/StaminaEvaluator.cs | 2 +- .../Colour/Data/CoupledColourEncoding.cs | 4 ++-- .../Preprocessing/Colour/Data/MonoEncoding.cs | 1 - .../Preprocessing/TaikoDifficultyHitObject.cs | 8 +++---- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 1d857a1dbb..bda161bf63 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -10,14 +10,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators { public class ColourEvaluator { - private static double sigmoid(double val, double center, double width) + /// + /// A sigmoid function. It gives a value between (middle - height/2) and (middle + height/2). + /// + /// The input value. + /// The center of the sigmoid, where the largest gradient occurs and value is equal to middle. + /// The radius of the sigmoid, outside of which values are near the minimum/maximum. + /// The middle of the sigmoid output. + /// The height of the sigmoid output. This will be equal to max value - min value. + public static double Sigmoid(double val, double center, double width, double middle, double height) { - return Math.Tanh(Math.E * -(val - center) / width); - } - - private static double sigmoid(double val, double center, double width, double middle, double height) - { - return sigmoid(val, center, width) * (height / 2) + middle; + double sigmoid = Math.Tanh(Math.E * -(val - center) / width); + return sigmoid * (height / 2) + middle; } /// @@ -27,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// public static double EvaluateDifficultyOf(MonoEncoding encoding, int i) { - return sigmoid(i, 2, 2, 0.5, 1); + return Sigmoid(i, 2, 2, 0.5, 1); } /// @@ -37,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// The index of the colour encoding within it's parent . public static double EvaluateDifficultyOf(ColourEncoding encoding, int i) { - return sigmoid(i, 2, 2, 0.5, 1); + return Sigmoid(i, 2, 2, 0.5, 1); } /// @@ -45,7 +49,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// public static double EvaluateDifficultyOf(CoupledColourEncoding encoding) { - return 1 - sigmoid(encoding.RepetitionInterval, 2, 2, 0.5, 1); + return 1 - Sigmoid(encoding.RepetitionInterval, 2, 2, 0.5, 1); } /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs index 954c1661dd..49b3ae2e19 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// /// Evaluates the minimum mechanical stamina required to play the current object. This is calculated using the - /// maximum possible interval between two hits using the same key, by alternating 2 keys for each colour. + /// maximum possible interval between two hits using the same key, by alternating 2 keys for each colour. /// public static double EvaluateDifficultyOf(DifficultyHitObject current) { diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs index 188d1b686b..9d204225fc 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs @@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data public int RepetitionInterval { get; private set; } = max_repetition_interval + 1; /// - /// Returns true if other is considered a repetition of this encoding. This is true if other's first two payload - /// identical mono lengths. + /// Returns true if other is considered a repetition of this encoding. This is true if other's first two payloads + /// have identical mono lengths. /// private bool isRepetitionOf(CoupledColourEncoding other) { diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs index 9e60946bd1..f42f968657 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs @@ -16,7 +16,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// /// List of s that are encoded within this . /// This is not declared as to avoid circular dependencies. - /// TODO: Review this, are circular dependencies within data-only classes are acceptable? /// public List EncodedData { get; private set; } = new List(); diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index a0d2fc7797..6619a54a7a 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing { private readonly IReadOnlyList? monoDifficultyHitObjects; public readonly int MonoIndex; - private readonly IReadOnlyList noteObjects; + private readonly IReadOnlyList noteDifficultyHitObjects; public readonly int NoteIndex; /// @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing : base(hitObject, lastObject, clockRate, objects, index) { var currentHit = hitObject as Hit; - this.noteObjects = noteObjects; + noteDifficultyHitObjects = noteObjects; Rhythm = getClosestRhythm(lastObject, lastLastObject, clockRate); HitType? hitType = currentHit?.Type; @@ -120,8 +120,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing public TaikoDifficultyHitObject? NextMono(int forwardsIndex) => monoDifficultyHitObjects?.ElementAtOrDefault(MonoIndex + (forwardsIndex + 1)); - public TaikoDifficultyHitObject? PreviousNote(int backwardsIndex) => noteObjects.ElementAtOrDefault(NoteIndex - (backwardsIndex + 1)); + public TaikoDifficultyHitObject? PreviousNote(int backwardsIndex) => noteDifficultyHitObjects.ElementAtOrDefault(NoteIndex - (backwardsIndex + 1)); - public TaikoDifficultyHitObject? NextNote(int forwardsIndex) => noteObjects.ElementAtOrDefault(NoteIndex + (forwardsIndex + 1)); + public TaikoDifficultyHitObject? NextNote(int forwardsIndex) => noteDifficultyHitObjects.ElementAtOrDefault(NoteIndex + (forwardsIndex + 1)); } } From 08dd9c79db55bcee648c2ae891c5969a63315ae5 Mon Sep 17 00:00:00 2001 From: Jay L Date: Thu, 21 Jul 2022 09:55:19 +1000 Subject: [PATCH 39/60] Fix Convert-related nerf This addresses recent player unsatisfaction with converts being underweighted. --- .../Difficulty/TaikoDifficultyCalculator.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index a70d3d9a38..3a230f7b91 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -64,10 +64,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty double combinedRating = combined.DifficultyValue() * difficulty_multiplier; double starRating = rescale(combinedRating * 1.4); - // TODO: This is temporary measure as we don't detect abuse-type playstyles of converts within the current system. + // TODO: This is temporary measure as we don't detect abuse of multiple-input playstyles of converts within the current system. if (beatmap.BeatmapInfo.Ruleset.OnlineID == 0) { - starRating *= 0.80; + starRating *= 0.925; + // For maps with low colour variance and high stamina requirement, multiple inputs are more likely to be abused. + if (colourRating < 2 && staminaRating > 8) + starRating *= 0.80; } HitWindows hitWindows = new TaikoHitWindows(); From b7567f7db2f3714693de8244d53f5192a69e19ab Mon Sep 17 00:00:00 2001 From: Jay L Date: Thu, 21 Jul 2022 10:52:41 +1000 Subject: [PATCH 40/60] Share sigmoid, Fix Preprocessor XML --- .../Difficulty/Evaluators/ColourEvaluator.cs | 21 +++++-------------- .../TaikoColourDifficultyPreprocessor.cs | 16 +++++++------- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index bda161bf63..f677fe8b25 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -25,21 +25,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators } /// - /// Evaluate the difficulty of the first note of a . - /// The encoding to evaluate. - /// The index of the mono encoding within it's parent . + /// Evaluate the difficulty of the first note of a or a . + /// The index of either encoding within it's respective parent. /// - public static double EvaluateDifficultyOf(MonoEncoding encoding, int i) - { - return Sigmoid(i, 2, 2, 0.5, 1); - } - - /// - /// Evaluate the difficulty of the first note of a . - /// - /// The encoding to evaluate. - /// The index of the colour encoding within it's parent . - public static double EvaluateDifficultyOf(ColourEncoding encoding, int i) + public static double EvaluateDifficultyOf(int i) { return Sigmoid(i, 2, 2, 0.5, 1); } @@ -65,13 +54,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators for (int i = 0; i < encoding.Payload.Count; i++) { ColourEncoding colourEncoding = encoding.Payload[i]; - double colourEncodingDifficulty = EvaluateDifficultyOf(colourEncoding, i) * coupledEncodingDifficulty; + double colourEncodingDifficulty = EvaluateDifficultyOf(i) * coupledEncodingDifficulty; colourEncoding.Payload[0].EncodedData[0].Colour!.EvaluatedDifficulty += colourEncodingDifficulty; for (int j = 0; j < colourEncoding.Payload.Count; j++) { MonoEncoding monoEncoding = colourEncoding.Payload[j]; - monoEncoding.EncodedData[0].Colour!.EvaluatedDifficulty += EvaluateDifficultyOf(monoEncoding, j) * colourEncodingDifficulty * 0.5; + monoEncoding.EncodedData[0].Colour!.EvaluatedDifficulty += EvaluateDifficultyOf(j) * colourEncodingDifficulty * 0.5; } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs index a699422d2c..a3238efc65 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -16,9 +16,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour public class TaikoColourDifficultyPreprocessor { /// - /// Process and encode a list of s into a list of s, - /// assign the appropriate s to each , - /// and preevaluate colour difficulty of each . + /// Processes and encodes a list of s into a list of s, + /// assigning the appropriate s to each , + /// and pre-evaluating colour difficulty of each . /// public static List ProcessAndAssign(List hitObjects) { @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour }); }); - // Preevaluate and assign difficulty values + // Pre-evaluate and assign difficulty values ColourEvaluator.PreEvaluateDifficulties(coupledEncoding); }); @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour // This ignores all non-note objects, which may or may not be the desired behaviour TaikoDifficultyHitObject? previousObject = taikoObject.PreviousNote(0); - // If the colour changed, or if this is the first object in the run, create a new mono encoding + // If the colour changed or if this is the first object in the run, create a new mono encoding if ( previousObject == null || // First object in the list @@ -75,8 +75,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour continue; } - // If we're here, we're in the same encoding as the previous object, thus lastEncoded is not null. Add - // the current object to the encoded payload. + // If we're here, we're in the same encoding as the previous object, thus lastEncoded is not null. + // Add the current object to the encoded payload. lastEncoded!.EncodedData.Add(taikoObject); } @@ -159,7 +159,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]); } - // Skip over peeked data and add the rest to the payload + // Skip over viewed data and add the rest to the payload lastEncoded.Payload.Add(data[i]); lastEncoded.Payload.Add(data[i + 1]); i++; From 7917a60e3c18c1f9f9d9c54fc7f1ae811b6cc470 Mon Sep 17 00:00:00 2001 From: vun Date: Thu, 21 Jul 2022 15:45:03 +0800 Subject: [PATCH 41/60] Move TaikoDifficultyHitObject creation back to TaikoDifficultyCalculator --- .../TaikoDifficultyPreprocessor.cs | 26 +++---------------- .../Difficulty/TaikoDifficultyCalculator.cs | 16 +++++++++++- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs index fa8e6a8a26..bd703b7263 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Game.Rulesets.Difficulty.Preprocessing; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing @@ -11,31 +10,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing public class TaikoDifficultyPreprocessor { /// - /// Creates a list of s from a s. - /// This is placed here in a separate class to avoid having to know - /// too much implementation details of the preprocessing, and avoid - /// having circular dependencies with various preprocessing and evaluator classes. + /// Does preprocessing on a list of s. + /// TODO: Review this - this is currently only a one-step process, but will potentially be expanded in the future. /// - /// The beatmap from which the list of is created. - /// The rate at which the gameplay clock is run at. - public static List CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) + public static List Process(List difficultyHitObjects) { - List difficultyHitObjects = new List(); - List centreObjects = new List(); - List rimObjects = new List(); - List noteObjects = new List(); - - for (int i = 2; i < beatmap.HitObjects.Count; i++) - { - difficultyHitObjects.Add( - new TaikoDifficultyHitObject( - beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, difficultyHitObjects, - centreObjects, rimObjects, noteObjects, difficultyHitObjects.Count) - ); - } - TaikoColourDifficultyPreprocessor.ProcessAndAssign(difficultyHitObjects); - return difficultyHitObjects; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 3a230f7b91..716a016b12 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -47,7 +47,21 @@ namespace osu.Game.Rulesets.Taiko.Difficulty protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { - return TaikoDifficultyPreprocessor.CreateDifficultyHitObjects(beatmap, clockRate); + List difficultyHitObjects = new List(); + List centreObjects = new List(); + List rimObjects = new List(); + List noteObjects = new List(); + + for (int i = 2; i < beatmap.HitObjects.Count; i++) + { + difficultyHitObjects.Add( + new TaikoDifficultyHitObject( + beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, difficultyHitObjects, + centreObjects, rimObjects, noteObjects, difficultyHitObjects.Count) + ); + } + + return TaikoDifficultyPreprocessor.Process(difficultyHitObjects); } protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) From e4086b058bb3052a89c41c709bd6483b3fe15809 Mon Sep 17 00:00:00 2001 From: vun Date: Thu, 21 Jul 2022 19:15:22 +0800 Subject: [PATCH 42/60] Implement stateless colour evaluator and required encoding changes --- .../Difficulty/Evaluators/ColourEvaluator.cs | 49 +++++++------- .../Colour/Data/ColourEncoding.cs | 7 ++ .../Preprocessing/Colour/Data/MonoEncoding.cs | 7 ++ .../TaikoColourDifficultyPreprocessor.cs | 66 +++++++++++-------- .../Colour/TaikoDifficultyHitObjectColour.cs | 24 ++++--- .../Preprocessing/TaikoDifficultyHitObject.cs | 5 +- .../Difficulty/Skills/Colour.cs | 5 +- 7 files changed, 99 insertions(+), 64 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index f677fe8b25..30094dc869 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data; @@ -18,19 +19,26 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// The radius of the sigmoid, outside of which values are near the minimum/maximum. /// The middle of the sigmoid output. /// The height of the sigmoid output. This will be equal to max value - min value. - public static double Sigmoid(double val, double center, double width, double middle, double height) + private static double sigmoid(double val, double center, double width, double middle, double height) { double sigmoid = Math.Tanh(Math.E * -(val - center) / width); return sigmoid * (height / 2) + middle; } /// - /// Evaluate the difficulty of the first note of a or a . - /// The index of either encoding within it's respective parent. + /// Evaluate the difficulty of the first note of a . /// - public static double EvaluateDifficultyOf(int i) + public static double EvaluateDifficultyOf(MonoEncoding encoding) { - return Sigmoid(i, 2, 2, 0.5, 1); + return sigmoid(encoding.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(encoding.Parent!) * 0.5; + } + + /// + /// Evaluate the difficulty of the first note of a . + /// + public static double EvaluateDifficultyOf(ColourEncoding encoding) + { + return sigmoid(encoding.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(encoding.Parent!); } /// @@ -38,31 +46,22 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// public static double EvaluateDifficultyOf(CoupledColourEncoding encoding) { - return 1 - Sigmoid(encoding.RepetitionInterval, 2, 2, 0.5, 1); + return 2 * (1 - sigmoid(encoding.RepetitionInterval, 2, 2, 0.5, 1)); } - /// - /// Pre-evaluate and *assign* difficulty values of all hit objects encoded in a . - /// Difficulty values are assigned to of each - /// encoded within. - /// - public static void PreEvaluateDifficulties(CoupledColourEncoding encoding) + public static double EvaluateDifficultyOf(DifficultyHitObject hitObject) { - double coupledEncodingDifficulty = 2 * EvaluateDifficultyOf(encoding); - encoding.Payload[0].Payload[0].EncodedData[0].Colour!.EvaluatedDifficulty += coupledEncodingDifficulty; + TaikoDifficultyHitObjectColour colour = ((TaikoDifficultyHitObject)hitObject).Colour; + double difficulty = 0.0d; - for (int i = 0; i < encoding.Payload.Count; i++) - { - ColourEncoding colourEncoding = encoding.Payload[i]; - double colourEncodingDifficulty = EvaluateDifficultyOf(i) * coupledEncodingDifficulty; - colourEncoding.Payload[0].EncodedData[0].Colour!.EvaluatedDifficulty += colourEncodingDifficulty; + if (colour.MonoEncoding != null) // Difficulty for MonoEncoding + difficulty += EvaluateDifficultyOf(colour.MonoEncoding); + if (colour.ColourEncoding != null) // Difficulty for ColourEncoding + difficulty += EvaluateDifficultyOf(colour.ColourEncoding); + if (colour.CoupledColourEncoding != null) // Difficulty for CoupledColourEncoding + difficulty += EvaluateDifficultyOf(colour.CoupledColourEncoding); - for (int j = 0; j < colourEncoding.Payload.Count; j++) - { - MonoEncoding monoEncoding = colourEncoding.Payload[j]; - monoEncoding.EncodedData[0].Colour!.EvaluatedDifficulty += EvaluateDifficultyOf(j) * colourEncodingDifficulty * 0.5; - } - } + return difficulty; } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs index cddf8816f5..23c5fa8b4a 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs @@ -17,6 +17,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// public List Payload { get; private set; } = new List(); + public CoupledColourEncoding? Parent; + + /// + /// Index of this encoding within it's parent encoding + /// + public int Index; + /// /// Determine if this is a repetition of another . This /// is a strict comparison and is true if and only if the colour sequence is exactly the same. diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs index f42f968657..abeba53e9a 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs @@ -19,6 +19,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// public List EncodedData { get; private set; } = new List(); + public ColourEncoding? Parent; + + /// + /// Index of this encoding within it's parent encoding + /// + public int Index; + public int RunLength => EncodedData.Count; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs index a3238efc65..d775246a2e 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Game.Rulesets.Difficulty.Preprocessing; -using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data; using osu.Game.Rulesets.Taiko.Objects; @@ -25,22 +24,36 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour List colours = new List(); List encodings = Encode(hitObjects); - // Assign colour to objects + // Assign indexing and encoding data to all relevant objects. Only the first note of each encoding type is + // assigned with the relevant encodings. encodings.ForEach(coupledEncoding => { - coupledEncoding.Payload.ForEach(encoding => - { - encoding.Payload.ForEach(mono => - { - mono.EncodedData.ForEach(hitObject => - { - hitObject.Colour = new TaikoDifficultyHitObjectColour(coupledEncoding); - }); - }); - }); + coupledEncoding.Payload[0].Payload[0].EncodedData[0].Colour.CoupledColourEncoding = coupledEncoding; - // Pre-evaluate and assign difficulty values - ColourEvaluator.PreEvaluateDifficulties(coupledEncoding); + // TODO: Review this - + // The outermost loop is kept a ForEach loop since it doesn't need index information, and we want to + // keep i and j for ColourEncoding's and MonoEncoding's index respectively, to keep it in line with + // documentation. + // If we want uniformity for the outermost loop, it can be switched to a for loop with h or something + // else as an index + // + // While parent and index should be part of the encoding process, they are assigned here instead due to + // this being a simple one location to assign them. + for (int i = 0; i < coupledEncoding.Payload.Count; ++i) + { + ColourEncoding colourEncoding = coupledEncoding.Payload[i]; + colourEncoding.Parent = coupledEncoding; + colourEncoding.Index = i; + colourEncoding.Payload[0].EncodedData[0].Colour.ColourEncoding = colourEncoding; + + for (int j = 0; j < colourEncoding.Payload.Count; ++j) + { + MonoEncoding monoEncoding = colourEncoding.Payload[j]; + monoEncoding.Parent = colourEncoding; + monoEncoding.Index = j; + monoEncoding.EncodedData[0].Colour.MonoEncoding = monoEncoding; + } + } }); return colours; @@ -67,7 +80,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour previousObject == null || // First object in the list (taikoObject.BaseObject as Hit)?.Type != (previousObject.BaseObject as Hit)?.Type ) - { lastEncoded = new MonoEncoding(); lastEncoded.EncodedData.Add(taikoObject); @@ -111,18 +123,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour return encoded; } - /// - /// Encodes a list of s into a list of s. - /// - public static List Encode(List data) - { - List firstPass = EncodeMono(data); - List secondPass = EncodeColour(firstPass); - List thirdPass = EncodeCoupledColour(secondPass); - - return thirdPass; - } - /// /// Encodes a list of s into a list of s. /// @@ -176,5 +176,17 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour return encoded; } + + /// + /// Encodes a list of s into a list of s. + /// + public static List Encode(List data) + { + List firstPass = EncodeMono(data); + List secondPass = EncodeColour(firstPass); + List thirdPass = EncodeCoupledColour(secondPass); + + return thirdPass; + } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs index 9fc7a1dacb..6a6b427393 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs @@ -6,18 +6,26 @@ using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { /// - /// Stores colour compression information for a . This is only present for the - /// first in a chunk. + /// Stores colour compression information for a . /// public class TaikoDifficultyHitObjectColour { - public CoupledColourEncoding Encoding { get; } + /// + /// encoding that encodes this note, only present if this is the first note within a + /// + /// + public MonoEncoding? MonoEncoding; - public double EvaluatedDifficulty = 0; + /// + /// encoding that encodes this note, only present if this is the first note within + /// a + /// + public ColourEncoding? ColourEncoding; - public TaikoDifficultyHitObjectColour(CoupledColourEncoding encoding) - { - Encoding = encoding; - } + /// + /// encoding that encodes this note, only present if this is the first note + /// within a + /// + public CoupledColourEncoding? CoupledColourEncoding; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index 6619a54a7a..14fd67be33 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// by other skills in the future. /// This need to be writeable by TaikoDifficultyHitObjectColour so that it can assign potentially reused instances /// - public TaikoDifficultyHitObjectColour? Colour; + public TaikoDifficultyHitObjectColour Colour; /// /// Creates a new difficulty hit object. @@ -53,6 +53,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing List noteObjects, int index) : base(hitObject, lastObject, clockRate, objects, index) { + // Create the Colour object, its properties should be filled in by TaikoDifficultyPreprocessor + Colour = new TaikoDifficultyHitObjectColour(); + var currentHit = hitObject as Hit; noteDifficultyHitObjects = noteObjects; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs index 8f8f62d214..386135ea4d 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -6,7 +6,7 @@ using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; +using osu.Game.Rulesets.Taiko.Difficulty.Evaluators; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { @@ -29,8 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { - double difficulty = ((TaikoDifficultyHitObject)current).Colour?.EvaluatedDifficulty ?? 0; - return difficulty; + return ColourEvaluator.EvaluateDifficultyOf(current); } } } From 4433f902ea4a20a6ec7bbcf70ee03ce1c26134a3 Mon Sep 17 00:00:00 2001 From: vun Date: Fri, 22 Jul 2022 10:49:53 +0800 Subject: [PATCH 43/60] Fix and add comments --- .../Preprocessing/Colour/Data/MonoEncoding.cs | 3 +++ .../Colour/TaikoColourDifficultyPreprocessor.cs | 3 --- .../Preprocessing/TaikoDifficultyHitObject.cs | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs index abeba53e9a..6f25eea51e 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs @@ -26,6 +26,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// public int Index; + /// + /// How long the mono pattern encoded within is + /// public int RunLength => EncodedData.Count; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs index d775246a2e..3772013e7a 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -30,12 +30,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { coupledEncoding.Payload[0].Payload[0].EncodedData[0].Colour.CoupledColourEncoding = coupledEncoding; - // TODO: Review this - // The outermost loop is kept a ForEach loop since it doesn't need index information, and we want to // keep i and j for ColourEncoding's and MonoEncoding's index respectively, to keep it in line with // documentation. - // If we want uniformity for the outermost loop, it can be switched to a for loop with h or something - // else as an index // // While parent and index should be part of the encoding process, they are assigned here instead due to // this being a simple one location to assign them. diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index 14fd67be33..fd9a225f6a 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -17,9 +17,24 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// public class TaikoDifficultyHitObject : DifficultyHitObject { + /// + /// The list of all of the same colour as this in the beatmap. + /// private readonly IReadOnlyList? monoDifficultyHitObjects; + + /// + /// The index of this in . + /// public readonly int MonoIndex; + + /// + /// The list of all that is either a regular note or finisher in the beatmap + /// private readonly IReadOnlyList noteDifficultyHitObjects; + + /// + /// The index of this in . + /// public readonly int NoteIndex; /// From 6359c1a4fea5ec9790cbf276804a4f9a2233228d Mon Sep 17 00:00:00 2001 From: vun Date: Fri, 22 Jul 2022 16:31:19 +0800 Subject: [PATCH 44/60] Fix outdated comment --- .../Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs index 23c5fa8b4a..9415769dab 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs @@ -27,7 +27,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// /// Determine if this is a repetition of another . This /// is a strict comparison and is true if and only if the colour sequence is exactly the same. - /// This does not require the s to have the same amount of s. /// public bool IsRepetitionOf(ColourEncoding other) { From 7d4593eb6ddbbc1d4a257d2bf8a3622f39a79ce0 Mon Sep 17 00:00:00 2001 From: vun Date: Fri, 22 Jul 2022 18:20:35 +0800 Subject: [PATCH 45/60] Fix comments --- .../Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs | 3 +++ .../Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs | 3 +++ .../Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs | 1 - 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs index 9415769dab..04066e7539 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs @@ -17,6 +17,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// public List Payload { get; private set; } = new List(); + /// + /// The parent that contains this + /// public CoupledColourEncoding? Parent; /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs index 6f25eea51e..7eee8896ac 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs @@ -19,6 +19,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// public List EncodedData { get; private set; } = new List(); + /// + /// The parent that contains this + /// public ColourEncoding? Parent; /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs index bd703b7263..c5ee8de809 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs @@ -11,7 +11,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing { /// /// Does preprocessing on a list of s. - /// TODO: Review this - this is currently only a one-step process, but will potentially be expanded in the future. /// public static List Process(List difficultyHitObjects) { From fc08d77090b6cdd0de9dca93dfe2c3bfb2b85647 Mon Sep 17 00:00:00 2001 From: vun Date: Fri, 22 Jul 2022 18:31:59 +0800 Subject: [PATCH 46/60] Remove review-specific comment --- .../Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs index 3772013e7a..517e240682 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -33,9 +33,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour // The outermost loop is kept a ForEach loop since it doesn't need index information, and we want to // keep i and j for ColourEncoding's and MonoEncoding's index respectively, to keep it in line with // documentation. - // - // While parent and index should be part of the encoding process, they are assigned here instead due to - // this being a simple one location to assign them. for (int i = 0; i < coupledEncoding.Payload.Count; ++i) { ColourEncoding colourEncoding = coupledEncoding.Payload[i]; From 502e31dd37b3b8be70aeb55b0efde4e51e15244a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Aug 2022 21:26:54 +0900 Subject: [PATCH 47/60] General refactoring --- .../Preprocessing/Colour/Data/MonoEncoding.cs | 1 - .../TaikoColourDifficultyPreprocessor.cs | 79 ++++++++----------- .../Colour/TaikoDifficultyHitObjectColour.cs | 9 +-- .../Preprocessing/TaikoDifficultyHitObject.cs | 43 +++++----- .../Difficulty/Skills/Colour.cs | 2 - .../Difficulty/Skills/Peaks.cs | 2 - 6 files changed, 57 insertions(+), 79 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs index 7eee8896ac..0e998696f9 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs @@ -15,7 +15,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data { /// /// List of s that are encoded within this . - /// This is not declared as to avoid circular dependencies. /// public List EncodedData { get; private set; } = new List(); diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs index 517e240682..7b7fab26b1 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -9,8 +9,7 @@ using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { /// - /// Utility class to perform various encodings. This is separated out from the encoding classes to prevent circular - /// dependencies. + /// Utility class to perform various encodings. /// public class TaikoColourDifficultyPreprocessor { @@ -26,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour // Assign indexing and encoding data to all relevant objects. Only the first note of each encoding type is // assigned with the relevant encodings. - encodings.ForEach(coupledEncoding => + foreach (var coupledEncoding in encodings) { coupledEncoding.Payload[0].Payload[0].EncodedData[0].Colour.CoupledColourEncoding = coupledEncoding; @@ -48,7 +47,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour monoEncoding.EncodedData[0].Colour.MonoEncoding = monoEncoding; } } - }); + } return colours; } @@ -58,35 +57,29 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour /// public static List EncodeMono(List data) { - List encoded = new List(); - - MonoEncoding? lastEncoded = null; + List encodings = new List(); + MonoEncoding? currentEncoding = null; for (int i = 0; i < data.Count; i++) { TaikoDifficultyHitObject taikoObject = (TaikoDifficultyHitObject)data[i]; + // This ignores all non-note objects, which may or may not be the desired behaviour TaikoDifficultyHitObject? previousObject = taikoObject.PreviousNote(0); - // If the colour changed or if this is the first object in the run, create a new mono encoding - if - ( - previousObject == null || // First object in the list - (taikoObject.BaseObject as Hit)?.Type != (previousObject.BaseObject as Hit)?.Type - ) + // If this is the first object in the list or the colour changed, create a new mono encoding + if (currentEncoding == null || (taikoObject.BaseObject as Hit)?.Type != (previousObject?.BaseObject as Hit)?.Type) { - lastEncoded = new MonoEncoding(); - lastEncoded.EncodedData.Add(taikoObject); - encoded.Add(lastEncoded); + currentEncoding = new MonoEncoding(); + encodings.Add(currentEncoding); continue; } - // If we're here, we're in the same encoding as the previous object, thus lastEncoded is not null. // Add the current object to the encoded payload. - lastEncoded!.EncodedData.Add(taikoObject); + currentEncoding.EncodedData.Add(taikoObject); } - return encoded; + return encodings; } /// @@ -94,27 +87,24 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour /// public static List EncodeColour(List data) { - List encoded = new List(); - ColourEncoding? lastEncoded = null; + List encodings = new List(); + ColourEncoding? currentEncoding = null; for (int i = 0; i < data.Count; i++) { - // Starts a new ColourEncoding if the previous MonoEncoding has a different mono length, or if this is - // the first MonoEncoding in the list. - if (lastEncoded == null || data[i].RunLength != data[i - 1].RunLength) + // Start a new ColourEncoding if the previous MonoEncoding has a different mono length, or if this is the first MonoEncoding in the list. + if (currentEncoding == null || data[i].RunLength != data[i - 1].RunLength) { - lastEncoded = new ColourEncoding(); - lastEncoded.Payload.Add(data[i]); - encoded.Add(lastEncoded); + currentEncoding = new ColourEncoding(); + encodings.Add(currentEncoding); continue; } - // If we're here, we're in the same encoding as the previous object. Add the current MonoEncoding to the - // encoded payload. - lastEncoded.Payload.Add(data[i]); + // Add the current MonoEncoding to the encoded payload. + currentEncoding.Payload.Add(data[i]); } - return encoded; + return encodings; } /// @@ -122,16 +112,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour /// public static List EncodeCoupledColour(List data) { - List encoded = new List(); - CoupledColourEncoding? lastEncoded = null; + List encodings = new List(); + CoupledColourEncoding? currentEncoding = null; for (int i = 0; i < data.Count; i++) { - // Starts a new CoupledColourEncoding. ColourEncodings that should be grouped together will be handled - // later within this loop. - lastEncoded = new CoupledColourEncoding + // Start a new CoupledColourEncoding. ColourEncodings that should be grouped together will be handled later within this loop. + currentEncoding = new CoupledColourEncoding { - Previous = lastEncoded + Previous = currentEncoding }; // Determine if future ColourEncodings should be grouped. @@ -140,7 +129,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour if (!isCoupled) { // If not, add the current ColourEncoding to the encoded payload and continue. - lastEncoded.Payload.Add(data[i]); + currentEncoding.Payload.Add(data[i]); } else { @@ -148,27 +137,27 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour // subsequent ColourEncodings should be grouped by increasing i and doing the appropriate isCoupled check. while (isCoupled) { - lastEncoded.Payload.Add(data[i]); + currentEncoding.Payload.Add(data[i]); i++; isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]); } // Skip over viewed data and add the rest to the payload - lastEncoded.Payload.Add(data[i]); - lastEncoded.Payload.Add(data[i + 1]); + currentEncoding.Payload.Add(data[i]); + currentEncoding.Payload.Add(data[i + 1]); i++; } - encoded.Add(lastEncoded); + encodings.Add(currentEncoding); } // Final pass to find repetition intervals - for (int i = 0; i < encoded.Count; i++) + for (int i = 0; i < encodings.Count; i++) { - encoded[i].FindRepetitionInterval(); + encodings[i].FindRepetitionInterval(); } - return encoded; + return encodings; } /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs index 6a6b427393..41080eeb33 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs @@ -11,20 +11,17 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour public class TaikoDifficultyHitObjectColour { /// - /// encoding that encodes this note, only present if this is the first note within a - /// + /// The that encodes this note, only present if this is the first note within a /// public MonoEncoding? MonoEncoding; /// - /// encoding that encodes this note, only present if this is the first note within - /// a + /// The that encodes this note, only present if this is the first note within a /// public ColourEncoding? ColourEncoding; /// - /// encoding that encodes this note, only present if this is the first note - /// within a + /// The that encodes this note, only present if this is the first note within a /// public CoupledColourEncoding? CoupledColourEncoding; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index fd9a225f6a..e7a8abfd38 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -45,9 +45,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// /// Colour data for this hit object. This is used by colour evaluator to calculate colour difficulty, but can be used /// by other skills in the future. - /// This need to be writeable by TaikoDifficultyHitObjectColour so that it can assign potentially reused instances /// - public TaikoDifficultyHitObjectColour Colour; + public readonly TaikoDifficultyHitObjectColour Colour; /// /// Creates a new difficulty hit object. @@ -59,7 +58,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing /// The list of all s in the current beatmap. /// The list of centre (don) s in the current beatmap. /// The list of rim (kat) s in the current beatmap. - /// The list of s that is a hit (i.e. not a slider or spinner) in the current beatmap. + /// The list of s that is a hit (i.e. not a drumroll or swell) in the current beatmap. /// The position of this in the list. public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate, List objects, @@ -68,33 +67,31 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing List noteObjects, int index) : base(hitObject, lastObject, clockRate, objects, index) { - // Create the Colour object, its properties should be filled in by TaikoDifficultyPreprocessor - Colour = new TaikoDifficultyHitObjectColour(); - - var currentHit = hitObject as Hit; noteDifficultyHitObjects = noteObjects; + // Create the Colour object, its properties should be filled in by TaikoDifficultyPreprocessor + Colour = new TaikoDifficultyHitObjectColour(); Rhythm = getClosestRhythm(lastObject, lastLastObject, clockRate); - HitType? hitType = currentHit?.Type; - if (hitType == HitType.Centre) + switch ((hitObject as Hit)?.Type) { - MonoIndex = centreHitObjects.Count; - centreHitObjects.Add(this); - monoDifficultyHitObjects = centreHitObjects; - } - else if (hitType == HitType.Rim) - { - MonoIndex = rimHitObjects.Count; - rimHitObjects.Add(this); - monoDifficultyHitObjects = rimHitObjects; - } + case HitType.Centre: + MonoIndex = centreHitObjects.Count; + centreHitObjects.Add(this); + monoDifficultyHitObjects = centreHitObjects; + break; - // Need to be done after HitType is set. - if (hitType == null) return; + case HitType.Rim: + MonoIndex = rimHitObjects.Count; + rimHitObjects.Add(this); + monoDifficultyHitObjects = rimHitObjects; + break; - NoteIndex = noteObjects.Count; - noteObjects.Add(this); + default: + NoteIndex = noteObjects.Count; + noteObjects.Add(this); + break; + } } /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs index 386135ea4d..dac0beadda 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs index 3e3bc543e1..ec8e754c5c 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; From 94c6beeaf7c465ff22b229863388701f94b71314 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Aug 2022 21:30:40 +0900 Subject: [PATCH 48/60] Use ctor in a place that looks visually weird I read through this thinking "why doesn't Previous get assigned to currentEncoding here? But it's because the initializer runs right after the ctor and before the "method" returns. So really there's 3 operations running on one line here - ctor, init, and assignment. --- .../Preprocessing/Colour/Data/ColourEncoding.cs | 2 +- .../Preprocessing/Colour/Data/CoupledColourEncoding.cs | 9 +++++++-- .../Colour/TaikoColourDifficultyPreprocessor.cs | 5 +---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs index 04066e7539..cd39a3d232 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// /// s that are grouped together within this . /// - public List Payload { get; private set; } = new List(); + public readonly List Payload = new List(); /// /// The parent that contains this diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs index 9d204225fc..1b831eedd8 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs @@ -20,12 +20,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// /// The s that are grouped together within this . /// - public List Payload = new List(); + public readonly List Payload = new List(); /// /// The previous . This is used to determine the repetition interval. /// - public CoupledColourEncoding? Previous = null; + public readonly CoupledColourEncoding? Previous; /// /// How many between the current and previous identical . @@ -33,6 +33,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// public int RepetitionInterval { get; private set; } = max_repetition_interval + 1; + public CoupledColourEncoding(CoupledColourEncoding? previous) + { + Previous = previous; + } + /// /// Returns true if other is considered a repetition of this encoding. This is true if other's first two payloads /// have identical mono lengths. diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs index 7b7fab26b1..2d69f5fe35 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -118,10 +118,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour for (int i = 0; i < data.Count; i++) { // Start a new CoupledColourEncoding. ColourEncodings that should be grouped together will be handled later within this loop. - currentEncoding = new CoupledColourEncoding - { - Previous = currentEncoding - }; + currentEncoding = new CoupledColourEncoding(currentEncoding); // Determine if future ColourEncodings should be grouped. bool isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]); From 21d29980329a4ff4b114abb3cf4430ee3e7eff8d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Aug 2022 21:35:34 +0900 Subject: [PATCH 49/60] Privatise internals of TaikoColourDifficultyPreprocessor --- .../TaikoColourDifficultyPreprocessor.cs | 34 +++++++++---------- .../TaikoDifficultyPreprocessor.cs | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs index 2d69f5fe35..2b047a4336 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour /// /// Utility class to perform various encodings. /// - public class TaikoColourDifficultyPreprocessor + public static class TaikoColourDifficultyPreprocessor { /// /// Processes and encodes a list of s into a list of s, @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour public static List ProcessAndAssign(List hitObjects) { List colours = new List(); - List encodings = Encode(hitObjects); + List encodings = encode(hitObjects); // Assign indexing and encoding data to all relevant objects. Only the first note of each encoding type is // assigned with the relevant encodings. @@ -52,10 +52,22 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour return colours; } + /// + /// Encodes a list of s into a list of s. + /// + private static List encode(List data) + { + List firstPass = encodeMono(data); + List secondPass = encodeColour(firstPass); + List thirdPass = encodeCoupledColour(secondPass); + + return thirdPass; + } + /// /// Encodes a list of s into a list of s. /// - public static List EncodeMono(List data) + private static List encodeMono(List data) { List encodings = new List(); MonoEncoding? currentEncoding = null; @@ -85,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour /// /// Encodes a list of s into a list of s. /// - public static List EncodeColour(List data) + private static List encodeColour(List data) { List encodings = new List(); ColourEncoding? currentEncoding = null; @@ -110,7 +122,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour /// /// Encodes a list of s into a list of s. /// - public static List EncodeCoupledColour(List data) + private static List encodeCoupledColour(List data) { List encodings = new List(); CoupledColourEncoding? currentEncoding = null; @@ -156,17 +168,5 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour return encodings; } - - /// - /// Encodes a list of s into a list of s. - /// - public static List Encode(List data) - { - List firstPass = EncodeMono(data); - List secondPass = EncodeColour(firstPass); - List thirdPass = EncodeCoupledColour(secondPass); - - return thirdPass; - } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs index c5ee8de809..2223c8e2d1 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing { - public class TaikoDifficultyPreprocessor + public static class TaikoDifficultyPreprocessor { /// /// Does preprocessing on a list of s. From 78283ce3c5993133c227c1113e8891e0b03fb4ab Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Aug 2022 21:38:40 +0900 Subject: [PATCH 50/60] Remove TaikoDifficultyPreprocessor --- .../TaikoColourDifficultyPreprocessor.cs | 5 +---- .../TaikoDifficultyPreprocessor.cs | 21 ------------------- .../Difficulty/TaikoDifficultyCalculator.cs | 5 ++++- 3 files changed, 5 insertions(+), 26 deletions(-) delete mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs index 2b047a4336..38d51aef91 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -18,9 +18,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour /// assigning the appropriate s to each , /// and pre-evaluating colour difficulty of each . /// - public static List ProcessAndAssign(List hitObjects) + public static void ProcessAndAssign(List hitObjects) { - List colours = new List(); List encodings = encode(hitObjects); // Assign indexing and encoding data to all relevant objects. Only the first note of each encoding type is @@ -48,8 +47,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour } } } - - return colours; } /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs deleted file mode 100644 index 2223c8e2d1..0000000000 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyPreprocessor.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using osu.Game.Rulesets.Difficulty.Preprocessing; -using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; - -namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing -{ - public static class TaikoDifficultyPreprocessor - { - /// - /// Does preprocessing on a list of s. - /// - public static List Process(List difficultyHitObjects) - { - TaikoColourDifficultyPreprocessor.ProcessAndAssign(difficultyHitObjects); - return difficultyHitObjects; - } - } -} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index ceaa3c56b5..ea2f04a3d9 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; +using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour; using osu.Game.Rulesets.Taiko.Difficulty.Skills; using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Rulesets.Taiko.Objects; @@ -63,7 +64,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty ); } - return TaikoDifficultyPreprocessor.Process(difficultyHitObjects); + TaikoColourDifficultyPreprocessor.ProcessAndAssign(difficultyHitObjects); + + return difficultyHitObjects; } protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) From 4d4ee05981ca32f09d6a4c1996b8a765705821f5 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Aug 2022 21:48:04 +0900 Subject: [PATCH 51/60] Whoops I meant to remove these --- .../Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs index 38d51aef91..0d1d3cd7a9 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -81,7 +81,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { currentEncoding = new MonoEncoding(); encodings.Add(currentEncoding); - continue; } // Add the current object to the encoded payload. @@ -106,7 +105,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour { currentEncoding = new ColourEncoding(); encodings.Add(currentEncoding); - continue; } // Add the current MonoEncoding to the encoded payload. From c03e47317a30cd9ac914f481cbb71677586eac72 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Aug 2022 21:54:23 +0900 Subject: [PATCH 52/60] Fix notes not being added to list --- .../Difficulty/Preprocessing/TaikoDifficultyHitObject.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index e7a8abfd38..4aaee50c18 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -86,11 +86,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing rimHitObjects.Add(this); monoDifficultyHitObjects = rimHitObjects; break; + } - default: - NoteIndex = noteObjects.Count; - noteObjects.Add(this); - break; + if (hitObject is Hit) + { + NoteIndex = noteObjects.Count; + noteObjects.Add(this); } } From 8e0049c00548ef64c7faee7ed480370c274efe95 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Aug 2022 21:57:35 +0900 Subject: [PATCH 53/60] Add back null check --- .../Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs index 0d1d3cd7a9..81ba219bc0 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour TaikoDifficultyHitObject? previousObject = taikoObject.PreviousNote(0); // If this is the first object in the list or the colour changed, create a new mono encoding - if (currentEncoding == null || (taikoObject.BaseObject as Hit)?.Type != (previousObject?.BaseObject as Hit)?.Type) + if (currentEncoding == null || previousObject == null || (taikoObject.BaseObject as Hit)?.Type != (previousObject.BaseObject as Hit)?.Type) { currentEncoding = new MonoEncoding(); encodings.Add(currentEncoding); From 40b1554feab6e5d161c0f1f5315c1048a515a880 Mon Sep 17 00:00:00 2001 From: vun Date: Thu, 18 Aug 2022 14:12:03 +0800 Subject: [PATCH 54/60] Change FindRepetitionInterval to start with one previous encoding --- .../Preprocessing/Colour/Data/CoupledColourEncoding.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs index 1b831eedd8..3f692e9d3d 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs @@ -60,14 +60,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// public void FindRepetitionInterval() { - if (Previous?.Previous == null) + if (Previous == null) { RepetitionInterval = max_repetition_interval + 1; return; } - CoupledColourEncoding? other = Previous.Previous; - int interval = 2; + CoupledColourEncoding? other = Previous; + int interval = 1; while (interval < max_repetition_interval) { From 5dcd4ce7c52de316d3b2f8665ffccfc86c3b9ada Mon Sep 17 00:00:00 2001 From: vun Date: Fri, 19 Aug 2022 15:31:03 +0800 Subject: [PATCH 55/60] Naming changes --- .../Difficulty/Evaluators/ColourEvaluator.cs | 12 ++--- ...rEncoding.cs => AlternatingMonoPattern.cs} | 22 ++++----- .../Data/{MonoEncoding.cs => MonoStreak.cs} | 10 ++-- ...lourEncoding.cs => RepeatingHitPattern.cs} | 28 +++++------ .../TaikoColourDifficultyPreprocessor.cs | 46 +++++++++---------- .../Colour/TaikoDifficultyHitObjectColour.cs | 6 +-- 6 files changed, 62 insertions(+), 62 deletions(-) rename osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/{ColourEncoding.cs => AlternatingMonoPattern.cs} (54%) rename osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/{MonoEncoding.cs => MonoStreak.cs} (81%) rename osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/{CoupledColourEncoding.cs => RepeatingHitPattern.cs} (59%) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 30094dc869..912a02f30e 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -26,25 +26,25 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators } /// - /// Evaluate the difficulty of the first note of a . + /// Evaluate the difficulty of the first note of a . /// - public static double EvaluateDifficultyOf(MonoEncoding encoding) + public static double EvaluateDifficultyOf(MonoStreak encoding) { return sigmoid(encoding.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(encoding.Parent!) * 0.5; } /// - /// Evaluate the difficulty of the first note of a . + /// Evaluate the difficulty of the first note of a . /// - public static double EvaluateDifficultyOf(ColourEncoding encoding) + public static double EvaluateDifficultyOf(AlternatingMonoPattern encoding) { return sigmoid(encoding.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(encoding.Parent!); } /// - /// Evaluate the difficulty of the first note of a . + /// Evaluate the difficulty of the first note of a . /// - public static double EvaluateDifficultyOf(CoupledColourEncoding encoding) + public static double EvaluateDifficultyOf(RepeatingHitPatterns encoding) { return 2 * (1 - sigmoid(encoding.RepetitionInterval, 2, 2, 0.5, 1)); } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs similarity index 54% rename from osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs rename to osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs index cd39a3d232..bb4ddc73d0 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/ColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs @@ -7,20 +7,20 @@ using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data { /// - /// Encodes a list of s. - /// s with the same are grouped together. + /// Encodes a list of s. + /// s with the same are grouped together. /// - public class ColourEncoding + public class AlternatingMonoPattern { /// - /// s that are grouped together within this . + /// s that are grouped together within this . /// - public readonly List Payload = new List(); + public readonly List Payload = new List(); /// - /// The parent that contains this + /// The parent that contains this /// - public CoupledColourEncoding? Parent; + public RepeatingHitPatterns? Parent; /// /// Index of this encoding within it's parent encoding @@ -28,10 +28,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data public int Index; /// - /// Determine if this is a repetition of another . This + /// Determine if this is a repetition of another . This /// is a strict comparison and is true if and only if the colour sequence is exactly the same. /// - public bool IsRepetitionOf(ColourEncoding other) + public bool IsRepetitionOf(AlternatingMonoPattern other) { return HasIdenticalMonoLength(other) && other.Payload.Count == Payload.Count && @@ -40,9 +40,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data } /// - /// Determine if this has the same mono length of another . + /// Determine if this has the same mono length of another . /// - public bool HasIdenticalMonoLength(ColourEncoding other) + public bool HasIdenticalMonoLength(AlternatingMonoPattern other) { return other.Payload[0].RunLength == Payload[0].RunLength; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs similarity index 81% rename from osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs rename to osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs index 0e998696f9..26175d9559 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs @@ -9,19 +9,19 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data { /// /// Encode colour information for a sequence of s. Consecutive s - /// of the same are encoded within the same . + /// of the same are encoded within the same . /// - public class MonoEncoding + public class MonoStreak { /// - /// List of s that are encoded within this . + /// List of s that are encoded within this . /// public List EncodedData { get; private set; } = new List(); /// - /// The parent that contains this + /// The parent that contains this /// - public ColourEncoding? Parent; + public AlternatingMonoPattern? Parent; /// /// Index of this encoding within it's parent encoding diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/RepeatingHitPattern.cs similarity index 59% rename from osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs rename to osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/RepeatingHitPattern.cs index 3f692e9d3d..91b41b80e7 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/CoupledColourEncoding.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/RepeatingHitPattern.cs @@ -7,33 +7,33 @@ using System.Collections.Generic; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data { /// - /// Encodes a list of s, grouped together by back and forth repetition of the same - /// . Also stores the repetition interval between this and the previous . + /// Encodes a list of s, grouped together by back and forth repetition of the same + /// . Also stores the repetition interval between this and the previous . /// - public class CoupledColourEncoding + public class RepeatingHitPatterns { /// - /// Maximum amount of s to look back to find a repetition. + /// Maximum amount of s to look back to find a repetition. /// private const int max_repetition_interval = 16; /// - /// The s that are grouped together within this . + /// The s that are grouped together within this . /// - public readonly List Payload = new List(); + public readonly List Payload = new List(); /// - /// The previous . This is used to determine the repetition interval. + /// The previous . This is used to determine the repetition interval. /// - public readonly CoupledColourEncoding? Previous; + public readonly RepeatingHitPatterns? Previous; /// - /// How many between the current and previous identical . + /// How many between the current and previous identical . /// If no repetition is found this will have a value of + 1. /// public int RepetitionInterval { get; private set; } = max_repetition_interval + 1; - public CoupledColourEncoding(CoupledColourEncoding? previous) + public RepeatingHitPatterns(RepeatingHitPatterns? previous) { Previous = previous; } @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// Returns true if other is considered a repetition of this encoding. This is true if other's first two payloads /// have identical mono lengths. /// - private bool isRepetitionOf(CoupledColourEncoding other) + private bool isRepetitionOf(RepeatingHitPatterns other) { if (Payload.Count != other.Payload.Count) return false; @@ -55,8 +55,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data } /// - /// Finds the closest previous that has the identical . - /// Interval is defined as the amount of chunks between the current and repeated encoding. + /// Finds the closest previous that has the identical . + /// Interval is defined as the amount of chunks between the current and repeated encoding. /// public void FindRepetitionInterval() { @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data return; } - CoupledColourEncoding? other = Previous; + RepeatingHitPatterns? other = Previous; int interval = 1; while (interval < max_repetition_interval) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs index 81ba219bc0..5ae659574d 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour /// public static void ProcessAndAssign(List hitObjects) { - List encodings = encode(hitObjects); + List encodings = encode(hitObjects); // Assign indexing and encoding data to all relevant objects. Only the first note of each encoding type is // assigned with the relevant encodings. @@ -33,14 +33,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour // documentation. for (int i = 0; i < coupledEncoding.Payload.Count; ++i) { - ColourEncoding colourEncoding = coupledEncoding.Payload[i]; + AlternatingMonoPattern colourEncoding = coupledEncoding.Payload[i]; colourEncoding.Parent = coupledEncoding; colourEncoding.Index = i; colourEncoding.Payload[0].EncodedData[0].Colour.ColourEncoding = colourEncoding; for (int j = 0; j < colourEncoding.Payload.Count; ++j) { - MonoEncoding monoEncoding = colourEncoding.Payload[j]; + MonoStreak monoEncoding = colourEncoding.Payload[j]; monoEncoding.Parent = colourEncoding; monoEncoding.Index = j; monoEncoding.EncodedData[0].Colour.MonoEncoding = monoEncoding; @@ -50,24 +50,24 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour } /// - /// Encodes a list of s into a list of s. + /// Encodes a list of s into a list of s. /// - private static List encode(List data) + private static List encode(List data) { - List firstPass = encodeMono(data); - List secondPass = encodeColour(firstPass); - List thirdPass = encodeCoupledColour(secondPass); + List firstPass = encodeMono(data); + List secondPass = encodeColour(firstPass); + List thirdPass = encodeCoupledColour(secondPass); return thirdPass; } /// - /// Encodes a list of s into a list of s. + /// Encodes a list of s into a list of s. /// - private static List encodeMono(List data) + private static List encodeMono(List data) { - List encodings = new List(); - MonoEncoding? currentEncoding = null; + List encodings = new List(); + MonoStreak? currentEncoding = null; for (int i = 0; i < data.Count; i++) { @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour // If this is the first object in the list or the colour changed, create a new mono encoding if (currentEncoding == null || previousObject == null || (taikoObject.BaseObject as Hit)?.Type != (previousObject.BaseObject as Hit)?.Type) { - currentEncoding = new MonoEncoding(); + currentEncoding = new MonoStreak(); encodings.Add(currentEncoding); } @@ -91,19 +91,19 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour } /// - /// Encodes a list of s into a list of s. + /// Encodes a list of s into a list of s. /// - private static List encodeColour(List data) + private static List encodeColour(List data) { - List encodings = new List(); - ColourEncoding? currentEncoding = null; + List encodings = new List(); + AlternatingMonoPattern? currentEncoding = null; for (int i = 0; i < data.Count; i++) { // Start a new ColourEncoding if the previous MonoEncoding has a different mono length, or if this is the first MonoEncoding in the list. if (currentEncoding == null || data[i].RunLength != data[i - 1].RunLength) { - currentEncoding = new ColourEncoding(); + currentEncoding = new AlternatingMonoPattern(); encodings.Add(currentEncoding); } @@ -115,17 +115,17 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour } /// - /// Encodes a list of s into a list of s. + /// Encodes a list of s into a list of s. /// - private static List encodeCoupledColour(List data) + private static List encodeCoupledColour(List data) { - List encodings = new List(); - CoupledColourEncoding? currentEncoding = null; + List encodings = new List(); + RepeatingHitPatterns? currentEncoding = null; for (int i = 0; i < data.Count; i++) { // Start a new CoupledColourEncoding. ColourEncodings that should be grouped together will be handled later within this loop. - currentEncoding = new CoupledColourEncoding(currentEncoding); + currentEncoding = new RepeatingHitPatterns(currentEncoding); // Determine if future ColourEncodings should be grouped. bool isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]); diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs index 41080eeb33..708ce8ecd0 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs @@ -13,16 +13,16 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour /// /// The that encodes this note, only present if this is the first note within a /// - public MonoEncoding? MonoEncoding; + public MonoStreak? MonoEncoding; /// /// The that encodes this note, only present if this is the first note within a /// - public ColourEncoding? ColourEncoding; + public AlternatingMonoPattern? ColourEncoding; /// /// The that encodes this note, only present if this is the first note within a /// - public CoupledColourEncoding? CoupledColourEncoding; + public RepeatingHitPatterns? CoupledColourEncoding; } } From 51176e95772736807b05d0158f8a79d14018dada Mon Sep 17 00:00:00 2001 From: vun Date: Fri, 19 Aug 2022 15:45:43 +0800 Subject: [PATCH 56/60] Naming changes --- .../Difficulty/Evaluators/ColourEvaluator.cs | 24 ++-- .../Colour/Data/AlternatingMonoPattern.cs | 12 +- .../Preprocessing/Colour/Data/MonoStreak.cs | 6 +- .../Colour/Data/RepeatingHitPattern.cs | 14 +-- .../TaikoColourDifficultyPreprocessor.cs | 108 +++++++++--------- .../Colour/TaikoDifficultyHitObjectColour.cs | 12 +- .../Difficulty/Skills/Colour.cs | 4 +- 7 files changed, 90 insertions(+), 90 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 912a02f30e..afddedf962 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -28,25 +28,25 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// /// Evaluate the difficulty of the first note of a . /// - public static double EvaluateDifficultyOf(MonoStreak encoding) + public static double EvaluateDifficultyOf(MonoStreak monoStreak) { - return sigmoid(encoding.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(encoding.Parent!) * 0.5; + return sigmoid(monoStreak.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(monoStreak.Parent!) * 0.5; } /// /// Evaluate the difficulty of the first note of a . /// - public static double EvaluateDifficultyOf(AlternatingMonoPattern encoding) + public static double EvaluateDifficultyOf(AlternatingMonoPattern alternatingMonoPattern) { - return sigmoid(encoding.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(encoding.Parent!); + return sigmoid(alternatingMonoPattern.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(alternatingMonoPattern.Parent!); } /// /// Evaluate the difficulty of the first note of a . /// - public static double EvaluateDifficultyOf(RepeatingHitPatterns encoding) + public static double EvaluateDifficultyOf(RepeatingHitPatterns repeatingHitPattern) { - return 2 * (1 - sigmoid(encoding.RepetitionInterval, 2, 2, 0.5, 1)); + return 2 * (1 - sigmoid(repeatingHitPattern.RepetitionInterval, 2, 2, 0.5, 1)); } public static double EvaluateDifficultyOf(DifficultyHitObject hitObject) @@ -54,12 +54,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators TaikoDifficultyHitObjectColour colour = ((TaikoDifficultyHitObject)hitObject).Colour; double difficulty = 0.0d; - if (colour.MonoEncoding != null) // Difficulty for MonoEncoding - difficulty += EvaluateDifficultyOf(colour.MonoEncoding); - if (colour.ColourEncoding != null) // Difficulty for ColourEncoding - difficulty += EvaluateDifficultyOf(colour.ColourEncoding); - if (colour.CoupledColourEncoding != null) // Difficulty for CoupledColourEncoding - difficulty += EvaluateDifficultyOf(colour.CoupledColourEncoding); + if (colour.MonoStreak != null) // Difficulty for MonoStreak + difficulty += EvaluateDifficultyOf(colour.MonoStreak); + if (colour.AlternatingMonoPattern != null) // Difficulty for AlternatingMonoPattern + difficulty += EvaluateDifficultyOf(colour.AlternatingMonoPattern); + if (colour.RepeatingHitPatterns != null) // Difficulty for RepeatingHitPattern + difficulty += EvaluateDifficultyOf(colour.RepeatingHitPatterns); return difficulty; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs index bb4ddc73d0..9d2df877d3 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// /// s that are grouped together within this . /// - public readonly List Payload = new List(); + public readonly List MonoStreaks = new List(); /// /// The parent that contains this @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data public RepeatingHitPatterns? Parent; /// - /// Index of this encoding within it's parent encoding + /// Index of this within it's parent /// public int Index; @@ -34,9 +34,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data public bool IsRepetitionOf(AlternatingMonoPattern other) { return HasIdenticalMonoLength(other) && - other.Payload.Count == Payload.Count && - (other.Payload[0].EncodedData[0].BaseObject as Hit)?.Type == - (Payload[0].EncodedData[0].BaseObject as Hit)?.Type; + other.MonoStreaks.Count == MonoStreaks.Count && + (other.MonoStreaks[0].HitObjects[0].BaseObject as Hit)?.Type == + (MonoStreaks[0].HitObjects[0].BaseObject as Hit)?.Type; } /// @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// public bool HasIdenticalMonoLength(AlternatingMonoPattern other) { - return other.Payload[0].RunLength == Payload[0].RunLength; + return other.MonoStreaks[0].RunLength == MonoStreaks[0].RunLength; } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs index 26175d9559..82a09d61fe 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// /// List of s that are encoded within this . /// - public List EncodedData { get; private set; } = new List(); + public List HitObjects { get; private set; } = new List(); /// /// The parent that contains this @@ -24,13 +24,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data public AlternatingMonoPattern? Parent; /// - /// Index of this encoding within it's parent encoding + /// Index of this within it's parent /// public int Index; /// /// How long the mono pattern encoded within is /// - public int RunLength => EncodedData.Count; + public int RunLength => HitObjects.Count; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/RepeatingHitPattern.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/RepeatingHitPattern.cs index 91b41b80e7..d5ce2d0a55 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/RepeatingHitPattern.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/RepeatingHitPattern.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// /// The s that are grouped together within this . /// - public readonly List Payload = new List(); + public readonly List AlternatingMonoPatterns = new List(); /// /// The previous . This is used to determine the repetition interval. @@ -39,24 +39,24 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data } /// - /// Returns true if other is considered a repetition of this encoding. This is true if other's first two payloads + /// Returns true if other is considered a repetition of this pattern. This is true if other's first two payloads /// have identical mono lengths. /// private bool isRepetitionOf(RepeatingHitPatterns other) { - if (Payload.Count != other.Payload.Count) return false; + if (AlternatingMonoPatterns.Count != other.AlternatingMonoPatterns.Count) return false; - for (int i = 0; i < Math.Min(Payload.Count, 2); i++) + for (int i = 0; i < Math.Min(AlternatingMonoPatterns.Count, 2); i++) { - if (!Payload[i].HasIdenticalMonoLength(other.Payload[i])) return false; + if (!AlternatingMonoPatterns[i].HasIdenticalMonoLength(other.AlternatingMonoPatterns[i])) return false; } return true; } /// - /// Finds the closest previous that has the identical . - /// Interval is defined as the amount of chunks between the current and repeated encoding. + /// Finds the closest previous that has the identical . + /// Interval is defined as the amount of chunks between the current and repeated patterns. /// public void FindRepetitionInterval() { diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs index 5ae659574d..bd46957fc0 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -20,30 +20,30 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour /// public static void ProcessAndAssign(List hitObjects) { - List encodings = encode(hitObjects); + List hitPatterns = encode(hitObjects); // Assign indexing and encoding data to all relevant objects. Only the first note of each encoding type is // assigned with the relevant encodings. - foreach (var coupledEncoding in encodings) + foreach (var repeatingHitPattern in hitPatterns) { - coupledEncoding.Payload[0].Payload[0].EncodedData[0].Colour.CoupledColourEncoding = coupledEncoding; + repeatingHitPattern.AlternatingMonoPatterns[0].MonoStreaks[0].HitObjects[0].Colour.RepeatingHitPatterns = repeatingHitPattern; // The outermost loop is kept a ForEach loop since it doesn't need index information, and we want to - // keep i and j for ColourEncoding's and MonoEncoding's index respectively, to keep it in line with + // keep i and j for AlternatingMonoPattern's and MonoStreak's index respectively, to keep it in line with // documentation. - for (int i = 0; i < coupledEncoding.Payload.Count; ++i) + for (int i = 0; i < repeatingHitPattern.AlternatingMonoPatterns.Count; ++i) { - AlternatingMonoPattern colourEncoding = coupledEncoding.Payload[i]; - colourEncoding.Parent = coupledEncoding; - colourEncoding.Index = i; - colourEncoding.Payload[0].EncodedData[0].Colour.ColourEncoding = colourEncoding; + AlternatingMonoPattern monoPattern = repeatingHitPattern.AlternatingMonoPatterns[i]; + monoPattern.Parent = repeatingHitPattern; + monoPattern.Index = i; + monoPattern.MonoStreaks[0].HitObjects[0].Colour.AlternatingMonoPattern = monoPattern; - for (int j = 0; j < colourEncoding.Payload.Count; ++j) + for (int j = 0; j < monoPattern.MonoStreaks.Count; ++j) { - MonoStreak monoEncoding = colourEncoding.Payload[j]; - monoEncoding.Parent = colourEncoding; - monoEncoding.Index = j; - monoEncoding.EncodedData[0].Colour.MonoEncoding = monoEncoding; + MonoStreak monoStreak = monoPattern.MonoStreaks[j]; + monoStreak.Parent = monoPattern; + monoStreak.Index = j; + monoStreak.HitObjects[0].Colour.MonoStreak = monoStreak; } } } @@ -54,20 +54,20 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour /// private static List encode(List data) { - List firstPass = encodeMono(data); - List secondPass = encodeColour(firstPass); - List thirdPass = encodeCoupledColour(secondPass); + List monoStreaks = encodeMonoStreak(data); + List alternatingMonoPatterns = encodeAlternatingMonoPattern(monoStreaks); + List repeatingHitPatterns = encodeRepeatingHitPattern(alternatingMonoPatterns); - return thirdPass; + return repeatingHitPatterns; } /// /// Encodes a list of s into a list of s. /// - private static List encodeMono(List data) + private static List encodeMonoStreak(List data) { - List encodings = new List(); - MonoStreak? currentEncoding = null; + List monoStreaks = new List(); + MonoStreak? currentMonoStreak = null; for (int i = 0; i < data.Count; i++) { @@ -76,92 +76,92 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour // This ignores all non-note objects, which may or may not be the desired behaviour TaikoDifficultyHitObject? previousObject = taikoObject.PreviousNote(0); - // If this is the first object in the list or the colour changed, create a new mono encoding - if (currentEncoding == null || previousObject == null || (taikoObject.BaseObject as Hit)?.Type != (previousObject.BaseObject as Hit)?.Type) + // If this is the first object in the list or the colour changed, create a new mono streak + if (currentMonoStreak == null || previousObject == null || (taikoObject.BaseObject as Hit)?.Type != (previousObject.BaseObject as Hit)?.Type) { - currentEncoding = new MonoStreak(); - encodings.Add(currentEncoding); + currentMonoStreak = new MonoStreak(); + monoStreaks.Add(currentMonoStreak); } // Add the current object to the encoded payload. - currentEncoding.EncodedData.Add(taikoObject); + currentMonoStreak.HitObjects.Add(taikoObject); } - return encodings; + return monoStreaks; } /// /// Encodes a list of s into a list of s. /// - private static List encodeColour(List data) + private static List encodeAlternatingMonoPattern(List data) { - List encodings = new List(); - AlternatingMonoPattern? currentEncoding = null; + List monoPatterns = new List(); + AlternatingMonoPattern? currentMonoPattern = null; for (int i = 0; i < data.Count; i++) { - // Start a new ColourEncoding if the previous MonoEncoding has a different mono length, or if this is the first MonoEncoding in the list. - if (currentEncoding == null || data[i].RunLength != data[i - 1].RunLength) + // Start a new AlternatingMonoPattern if the previous MonoStreak has a different mono length, or if this is the first MonoStreak in the list. + if (currentMonoPattern == null || data[i].RunLength != data[i - 1].RunLength) { - currentEncoding = new AlternatingMonoPattern(); - encodings.Add(currentEncoding); + currentMonoPattern = new AlternatingMonoPattern(); + monoPatterns.Add(currentMonoPattern); } - // Add the current MonoEncoding to the encoded payload. - currentEncoding.Payload.Add(data[i]); + // Add the current MonoStreak to the encoded payload. + currentMonoPattern.MonoStreaks.Add(data[i]); } - return encodings; + return monoPatterns; } /// /// Encodes a list of s into a list of s. /// - private static List encodeCoupledColour(List data) + private static List encodeRepeatingHitPattern(List data) { - List encodings = new List(); - RepeatingHitPatterns? currentEncoding = null; + List hitPatterns = new List(); + RepeatingHitPatterns? currentHitPattern = null; for (int i = 0; i < data.Count; i++) { - // Start a new CoupledColourEncoding. ColourEncodings that should be grouped together will be handled later within this loop. - currentEncoding = new RepeatingHitPatterns(currentEncoding); + // Start a new RepeatingHitPattern. AlternatingMonoPatterns that should be grouped together will be handled later within this loop. + currentHitPattern = new RepeatingHitPatterns(currentHitPattern); - // Determine if future ColourEncodings should be grouped. + // Determine if future AlternatingMonoPatterns should be grouped. bool isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]); if (!isCoupled) { - // If not, add the current ColourEncoding to the encoded payload and continue. - currentEncoding.Payload.Add(data[i]); + // If not, add the current AlternatingMonoPattern to the encoded payload and continue. + currentHitPattern.AlternatingMonoPatterns.Add(data[i]); } else { - // If so, add the current ColourEncoding to the encoded payload and start repeatedly checking if the - // subsequent ColourEncodings should be grouped by increasing i and doing the appropriate isCoupled check. + // If so, add the current AlternatingMonoPattern to the encoded payload and start repeatedly checking if the + // subsequent AlternatingMonoPatterns should be grouped by increasing i and doing the appropriate isCoupled check. while (isCoupled) { - currentEncoding.Payload.Add(data[i]); + currentHitPattern.AlternatingMonoPatterns.Add(data[i]); i++; isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]); } // Skip over viewed data and add the rest to the payload - currentEncoding.Payload.Add(data[i]); - currentEncoding.Payload.Add(data[i + 1]); + currentHitPattern.AlternatingMonoPatterns.Add(data[i]); + currentHitPattern.AlternatingMonoPatterns.Add(data[i + 1]); i++; } - encodings.Add(currentEncoding); + hitPatterns.Add(currentHitPattern); } // Final pass to find repetition intervals - for (int i = 0; i < encodings.Count; i++) + for (int i = 0; i < hitPatterns.Count; i++) { - encodings[i].FindRepetitionInterval(); + hitPatterns[i].FindRepetitionInterval(); } - return encodings; + return hitPatterns; } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs index 708ce8ecd0..c631b8d4a8 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs @@ -11,18 +11,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour public class TaikoDifficultyHitObjectColour { /// - /// The that encodes this note, only present if this is the first note within a + /// The that encodes this note, only present if this is the first note within a /// - public MonoStreak? MonoEncoding; + public MonoStreak? MonoStreak; /// - /// The that encodes this note, only present if this is the first note within a + /// The that encodes this note, only present if this is the first note within a /// - public AlternatingMonoPattern? ColourEncoding; + public AlternatingMonoPattern? AlternatingMonoPattern; /// - /// The that encodes this note, only present if this is the first note within a + /// The that encodes this note, only present if this is the first note within a /// - public RepeatingHitPatterns? CoupledColourEncoding; + public RepeatingHitPatterns? RepeatingHitPatterns; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs index dac0beadda..2d45b5eed0 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { protected override double SkillMultiplier => 0.12; - // This is set to decay slower than other skills, due to the fact that only the first note of each Mono/Colour/Coupled - // encoding having any difficulty values, and we want to allow colour difficulty to be able to build up even on + // This is set to decay slower than other skills, due to the fact that only the first note of each encoding class + // having any difficulty values, and we want to allow colour difficulty to be able to build up even on // slower maps. protected override double StrainDecayBase => 0.8; From a26de0a10f4f32131b658df3ffdb2c47026f027d Mon Sep 17 00:00:00 2001 From: vun Date: Fri, 19 Aug 2022 16:05:34 +0800 Subject: [PATCH 57/60] Add HitType property to MonoStreak --- .../Preprocessing/Colour/Data/AlternatingMonoPattern.cs | 3 +-- .../Difficulty/Preprocessing/Colour/Data/MonoStreak.cs | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs index 9d2df877d3..5e6f32cce2 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs @@ -35,8 +35,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data { return HasIdenticalMonoLength(other) && other.MonoStreaks.Count == MonoStreaks.Count && - (other.MonoStreaks[0].HitObjects[0].BaseObject as Hit)?.Type == - (MonoStreaks[0].HitObjects[0].BaseObject as Hit)?.Type; + other.MonoStreaks[0].HitType == MonoStreaks[0].HitType; } /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs index 82a09d61fe..9ebea156a5 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data { /// /// Encode colour information for a sequence of s. Consecutive s - /// of the same are encoded within the same . + /// of the same are encoded within the same . /// public class MonoStreak { @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// public int Index; + public HitType? HitType => (HitObjects[0].BaseObject as Hit)?.Type; + /// /// How long the mono pattern encoded within is /// From 684efefb50c16657af99b8da4e0298155b0fd314 Mon Sep 17 00:00:00 2001 From: vun Date: Fri, 19 Aug 2022 16:13:36 +0800 Subject: [PATCH 58/60] Add FirstHitObject as a property of encoding classes --- .../Difficulty/Evaluators/ColourEvaluator.cs | 4 ++-- .../Preprocessing/Colour/Data/AlternatingMonoPattern.cs | 5 +++++ .../Difficulty/Preprocessing/Colour/Data/MonoStreak.cs | 8 ++++++++ .../{RepeatingHitPattern.cs => RepeatingHitPatterns.cs} | 5 +++++ .../Colour/TaikoColourDifficultyPreprocessor.cs | 6 +++--- .../Colour/TaikoDifficultyHitObjectColour.cs | 4 ++-- 6 files changed, 25 insertions(+), 7 deletions(-) rename osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/{RepeatingHitPattern.cs => RepeatingHitPatterns.cs} (93%) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index afddedf962..6c685e854e 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -58,8 +58,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators difficulty += EvaluateDifficultyOf(colour.MonoStreak); if (colour.AlternatingMonoPattern != null) // Difficulty for AlternatingMonoPattern difficulty += EvaluateDifficultyOf(colour.AlternatingMonoPattern); - if (colour.RepeatingHitPatterns != null) // Difficulty for RepeatingHitPattern - difficulty += EvaluateDifficultyOf(colour.RepeatingHitPatterns); + if (colour.RepeatingHitPattern != null) // Difficulty for RepeatingHitPattern + difficulty += EvaluateDifficultyOf(colour.RepeatingHitPattern); return difficulty; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs index 5e6f32cce2..450eefe75c 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs @@ -27,6 +27,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// public int Index; + /// + /// The first in this . + /// + public TaikoDifficultyHitObject FirstHitObject => MonoStreaks[0].FirstHitObject; + /// /// Determine if this is a repetition of another . This /// is a strict comparison and is true if and only if the colour sequence is exactly the same. diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs index 9ebea156a5..4e15043acf 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs @@ -28,6 +28,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// public int Index; + /// + /// The first in this . + /// + public TaikoDifficultyHitObject FirstHitObject => HitObjects[0]; + + /// + /// The hit type of all objects encoded within this + /// public HitType? HitType => (HitObjects[0].BaseObject as Hit)?.Type; /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/RepeatingHitPattern.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/RepeatingHitPatterns.cs similarity index 93% rename from osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/RepeatingHitPattern.cs rename to osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/RepeatingHitPatterns.cs index d5ce2d0a55..fe0dc6dd9a 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/RepeatingHitPattern.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/RepeatingHitPatterns.cs @@ -22,6 +22,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// public readonly List AlternatingMonoPatterns = new List(); + /// + /// The parent in this + /// + public TaikoDifficultyHitObject FirstHitObject => AlternatingMonoPatterns[0].FirstHitObject; + /// /// The previous . This is used to determine the repetition interval. /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs index bd46957fc0..d19e05f4e0 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoColourDifficultyPreprocessor.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour // assigned with the relevant encodings. foreach (var repeatingHitPattern in hitPatterns) { - repeatingHitPattern.AlternatingMonoPatterns[0].MonoStreaks[0].HitObjects[0].Colour.RepeatingHitPatterns = repeatingHitPattern; + repeatingHitPattern.FirstHitObject.Colour.RepeatingHitPattern = repeatingHitPattern; // The outermost loop is kept a ForEach loop since it doesn't need index information, and we want to // keep i and j for AlternatingMonoPattern's and MonoStreak's index respectively, to keep it in line with @@ -36,14 +36,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour AlternatingMonoPattern monoPattern = repeatingHitPattern.AlternatingMonoPatterns[i]; monoPattern.Parent = repeatingHitPattern; monoPattern.Index = i; - monoPattern.MonoStreaks[0].HitObjects[0].Colour.AlternatingMonoPattern = monoPattern; + monoPattern.FirstHitObject.Colour.AlternatingMonoPattern = monoPattern; for (int j = 0; j < monoPattern.MonoStreaks.Count; ++j) { MonoStreak monoStreak = monoPattern.MonoStreaks[j]; monoStreak.Parent = monoPattern; monoStreak.Index = j; - monoStreak.HitObjects[0].Colour.MonoStreak = monoStreak; + monoStreak.FirstHitObject.Colour.MonoStreak = monoStreak; } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs index c631b8d4a8..9c147eee9c 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/TaikoDifficultyHitObjectColour.cs @@ -21,8 +21,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour public AlternatingMonoPattern? AlternatingMonoPattern; /// - /// The that encodes this note, only present if this is the first note within a + /// The that encodes this note, only present if this is the first note within a /// - public RepeatingHitPatterns? RepeatingHitPatterns; + public RepeatingHitPatterns? RepeatingHitPattern; } } From f3e1287f04addc40f7e7ef98fe6a89d5681e5df3 Mon Sep 17 00:00:00 2001 From: vun Date: Fri, 19 Aug 2022 16:19:45 +0800 Subject: [PATCH 59/60] Remove redundant using statement --- .../Preprocessing/Colour/Data/AlternatingMonoPattern.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs index 450eefe75c..60d4e55a64 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data { From fb9bb2d42dd8884cfd4d93e15024d791a69e2eda Mon Sep 17 00:00:00 2001 From: vun Date: Wed, 24 Aug 2022 08:57:13 +0800 Subject: [PATCH 60/60] Declare Parent as non-nullable --- .../Difficulty/Evaluators/ColourEvaluator.cs | 4 ++-- .../Preprocessing/Colour/Data/AlternatingMonoPattern.cs | 2 +- .../Difficulty/Preprocessing/Colour/Data/MonoStreak.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs index 6c685e854e..7d88be2f70 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// public static double EvaluateDifficultyOf(MonoStreak monoStreak) { - return sigmoid(monoStreak.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(monoStreak.Parent!) * 0.5; + return sigmoid(monoStreak.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(monoStreak.Parent) * 0.5; } /// @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators /// public static double EvaluateDifficultyOf(AlternatingMonoPattern alternatingMonoPattern) { - return sigmoid(alternatingMonoPattern.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(alternatingMonoPattern.Parent!); + return sigmoid(alternatingMonoPattern.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(alternatingMonoPattern.Parent); } /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs index 60d4e55a64..7910a8262b 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/AlternatingMonoPattern.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// /// The parent that contains this /// - public RepeatingHitPatterns? Parent; + public RepeatingHitPatterns Parent = null!; /// /// Index of this within it's parent diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs index 4e15043acf..174988bed7 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/Colour/Data/MonoStreak.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data /// /// The parent that contains this /// - public AlternatingMonoPattern? Parent; + public AlternatingMonoPattern Parent = null!; /// /// Index of this within it's parent