From 48b7e0f0900d52a23bb9fa1a753983c373ca6ec8 Mon Sep 17 00:00:00 2001 From: Jay L Date: Tue, 10 May 2022 18:17:40 +1000 Subject: [PATCH 01/11] Stamina Skill Rewrite and Refactor --- .../Difficulty/Skills/Stamina.cs | 139 +++++++++--------- .../Difficulty/TaikoDifficultyCalculator.cs | 20 +-- 2 files changed, 75 insertions(+), 84 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 54cf233d69..b10da95444 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -1,16 +1,50 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; +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.Preprocessing; using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { + class SingleKeyStamina + { + private double previousHitTime = -1; + + private double strainValueOf(DifficultyHitObject current) + { + if (previousHitTime == -1) + { + previousHitTime = current.StartTime; + return 0; + } + else + { + double objectStrain = 0.5; + objectStrain += speedBonus(current.StartTime - previousHitTime); + previousHitTime = current.StartTime; + return objectStrain; + } + } + + public double StrainValueAt(DifficultyHitObject current) + { + return strainValueOf(current); + } + + /// + /// 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 / Math.Pow(notePairDuration + 100, 1); + } + } + /// /// Calculates the stamina coefficient of taiko difficulty. /// @@ -22,39 +56,43 @@ 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 max_history_length = 2; + private SingleKeyStamina[] keyStamina = new SingleKeyStamina[4] + { + new SingleKeyStamina(), + new SingleKeyStamina(), + new SingleKeyStamina(), + new SingleKeyStamina() + }; - /// - /// The index of the hand this instance is associated with. - /// - /// - /// The value of 0 indicates the left hand (full alternating gameplay starting with left hand is assumed). - /// This naturally translates onto index offsets of the objects in the map. - /// - private readonly int hand; - - /// - /// Stores the last durations between notes hit with the hand indicated by . - /// - private readonly LimitedCapacityQueue notePairDurationHistory = new LimitedCapacityQueue(max_history_length); - - /// - /// Stores the of the last object that was hit by the other hand. - /// - private double offhandObjectDuration = double.MaxValue; + private int donIndex = 1; + private int katIndex = 3; /// /// Creates a skill. /// /// Mods for use in skill calculations. - /// Whether this instance is performing calculations for the right hand. - public Stamina(Mod[] mods, bool rightHand) + public Stamina(Mod[] mods) : base(mods) { - hand = rightHand ? 1 : 0; + } + + private SingleKeyStamina getNextSingleKeyStamina(TaikoDifficultyHitObject current) + { + if (current.HitType == HitType.Centre) + { + donIndex = donIndex == 0 ? 1 : 0; + return keyStamina[donIndex]; + } + else + { + katIndex = katIndex == 2 ? 3 : 2; + return keyStamina[katIndex]; + } + } + + private double sigmoid(double val, double center, double width) + { + return Math.Tanh(Math.E * -(val - center) / width); } protected override double StrainValueOf(DifficultyHitObject current) @@ -65,52 +103,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills } TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)current; + double objectStrain = getNextSingleKeyStamina(hitObject).StrainValueAt(hitObject); - if (hitObject.ObjectIndex % 2 == hand) - { - double objectStrain = 1; - - if (hitObject.ObjectIndex == 1) - return 1; - - notePairDurationHistory.Enqueue(hitObject.DeltaTime + offhandObjectDuration); - - double shortestRecentNote = notePairDurationHistory.Min(); - objectStrain += speedBonus(shortestRecentNote); - - if (hitObject.StaminaCheese) - objectStrain *= cheesePenalty(hitObject.DeltaTime + offhandObjectDuration); - - return objectStrain; - } - - offhandObjectDuration = hitObject.DeltaTime; - return 0; - } - - /// - /// Applies a penalty for hit objects marked with . - /// - /// The duration between the current and previous note hit using the hand indicated by . - private double cheesePenalty(double notePairDuration) - { - if (notePairDuration > 125) return 1; - if (notePairDuration < 100) return 0.6; - - return 0.6 + (notePairDuration - 100) * 0.016; - } - - /// - /// Applies a speed bonus dependent on the time since the last hit performed using this hand. - /// - /// The duration between the current and previous note hit using the hand indicated by . - private double speedBonus(double notePairDuration) - { - if (notePairDuration >= 200) return 0; - - double bonus = 200 - notePairDuration; - bonus *= bonus; - return bonus / 100000; + return objectStrain; } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index a9d512f076..de69f63bf4 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 { private const double rhythm_skill_multiplier = 0.014; private const double colour_skill_multiplier = 0.01; - private const double stamina_skill_multiplier = 0.02; + private const double stamina_skill_multiplier = 0.021; public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) @@ -33,8 +33,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { new Colour(mods), new Rhythm(mods), - new Stamina(mods, true), - new Stamina(mods, false), + new Stamina(mods) }; protected override Mod[] DifficultyAdjustmentMods => new Mod[] @@ -58,7 +57,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty ); } - new StaminaCheeseDetector(taikoDifficultyHitObjects).FindCheese(); return taikoDifficultyHitObjects; } @@ -69,17 +67,16 @@ namespace osu.Game.Rulesets.Taiko.Difficulty var colour = (Colour)skills[0]; var rhythm = (Rhythm)skills[1]; - var staminaRight = (Stamina)skills[2]; - var staminaLeft = (Stamina)skills[3]; + var stamina = (Stamina)skills[2]; double colourRating = colour.DifficultyValue() * colour_skill_multiplier; double rhythmRating = rhythm.DifficultyValue() * rhythm_skill_multiplier; - double staminaRating = (staminaRight.DifficultyValue() + staminaLeft.DifficultyValue()) * stamina_skill_multiplier; + double staminaRating = stamina.DifficultyValue() * stamina_skill_multiplier; double staminaPenalty = simpleColourPenalty(staminaRating, colourRating); staminaRating *= staminaPenalty; - double combinedRating = locallyCombinedDifficulty(colour, rhythm, staminaRight, staminaLeft, staminaPenalty); + double combinedRating = locallyCombinedDifficulty(colour, rhythm, stamina, staminaPenalty); double separatedRating = norm(1.5, colourRating, rhythmRating, staminaRating); double starRating = 1.4 * separatedRating + 0.5 * combinedRating; starRating = rescale(starRating); @@ -127,20 +124,19 @@ 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 staminaRight, Stamina staminaLeft, double staminaPenalty) + private double locallyCombinedDifficulty(Colour colour, Rhythm rhythm, Stamina stamina, double staminaPenalty) { List peaks = new List(); var colourPeaks = colour.GetCurrentStrainPeaks().ToList(); var rhythmPeaks = rhythm.GetCurrentStrainPeaks().ToList(); - var staminaRightPeaks = staminaRight.GetCurrentStrainPeaks().ToList(); - var staminaLeftPeaks = staminaLeft.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 = (staminaRightPeaks[i] + staminaLeftPeaks[i]) * stamina_skill_multiplier * staminaPenalty; + double staminaPeak = staminaPeaks[i] * stamina_skill_multiplier * staminaPenalty; double peak = norm(2, colourPeak, rhythmPeak, staminaPeak); From f15738fded36c06d022014871effae3c3c380709 Mon Sep 17 00:00:00 2001 From: Jay L Date: Sun, 15 May 2022 22:09:24 +1000 Subject: [PATCH 02/11] Remove Redundant Detection Not needed, as nerf is not required anymore and has negative effect. --- .../Preprocessing/StaminaCheeseDetector.cs | 145 ------------------ 1 file changed, 145 deletions(-) delete mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/StaminaCheeseDetector.cs diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/StaminaCheeseDetector.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/StaminaCheeseDetector.cs deleted file mode 100644 index 3b1a9ad777..0000000000 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/StaminaCheeseDetector.cs +++ /dev/null @@ -1,145 +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; -using System.Collections.Generic; -using osu.Game.Rulesets.Difficulty.Utils; -using osu.Game.Rulesets.Taiko.Objects; - -namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing -{ - /// - /// Detects special hit object patterns which are easier to hit using special techniques - /// than normally assumed in the fully-alternating play style. - /// - /// - /// This component detects two basic types of patterns, leveraged by the following techniques: - /// - /// Rolling allows hitting patterns with quickly and regularly alternating notes with a single hand. - /// TL tapping makes hitting longer sequences of consecutive same-colour notes with little to no colour changes in-between. - /// - /// - public class StaminaCheeseDetector - { - /// - /// The minimum number of consecutive objects with repeating patterns that can be classified as hittable using a roll. - /// - private const int roll_min_repetitions = 12; - - /// - /// The minimum number of consecutive objects with repeating patterns that can be classified as hittable using a TL tap. - /// - private const int tl_min_repetitions = 16; - - /// - /// The list of all s in the map. - /// - private readonly List hitObjects; - - public StaminaCheeseDetector(List hitObjects) - { - this.hitObjects = hitObjects; - } - - /// - /// Finds and marks all objects in that special difficulty-reducing techiques apply to - /// with the flag. - /// - public void FindCheese() - { - findRolls(3); - findRolls(4); - - findTlTap(0, HitType.Rim); - findTlTap(1, HitType.Rim); - findTlTap(0, HitType.Centre); - findTlTap(1, HitType.Centre); - } - - /// - /// Finds and marks all sequences hittable using a roll. - /// - /// The length of a single repeating pattern to consider (triplets/quadruplets). - private void findRolls(int patternLength) - { - var history = new LimitedCapacityQueue(2 * patternLength); - - // for convenience, we're tracking the index of the item *before* our suspected repeat's start, - // as that index can be simply subtracted from the current index to get the number of elements in between - // without off-by-one errors - int indexBeforeLastRepeat = -1; - int lastMarkEnd = 0; - - for (int i = 0; i < hitObjects.Count; i++) - { - history.Enqueue(hitObjects[i]); - if (!history.Full) - continue; - - if (!containsPatternRepeat(history, patternLength)) - { - // we're setting this up for the next iteration, hence the +1. - // right here this index will point at the queue's front (oldest item), - // but that item is about to be popped next loop with an enqueue. - indexBeforeLastRepeat = i - history.Count + 1; - continue; - } - - int repeatedLength = i - indexBeforeLastRepeat; - if (repeatedLength < roll_min_repetitions) - continue; - - markObjectsAsCheese(Math.Max(lastMarkEnd, i - repeatedLength + 1), i); - lastMarkEnd = i; - } - } - - /// - /// Determines whether the objects stored in contain a repetition of a pattern of length . - /// - private static bool containsPatternRepeat(LimitedCapacityQueue history, int patternLength) - { - for (int j = 0; j < patternLength; j++) - { - if (history[j].HitType != history[j + patternLength].HitType) - return false; - } - - return true; - } - - /// - /// Finds and marks all sequences hittable using a TL tap. - /// - /// Whether sequences starting with an odd- (1) or even-indexed (0) hit object should be checked. - /// The type of hit to check for TL taps. - private void findTlTap(int parity, HitType type) - { - int tlLength = -2; - int lastMarkEnd = 0; - - for (int i = parity; i < hitObjects.Count; i += 2) - { - if (hitObjects[i].HitType == type) - tlLength += 2; - else - tlLength = -2; - - if (tlLength < tl_min_repetitions) - continue; - - markObjectsAsCheese(Math.Max(lastMarkEnd, i - tlLength + 1), i); - lastMarkEnd = i; - } - } - - /// - /// Marks all objects from to (inclusive) as . - /// - private void markObjectsAsCheese(int start, int end) - { - for (int i = start; i <= end; i++) - hitObjects[i].StaminaCheese = true; - } - } -} From d174099016363de0cd77f62b5eb0c183d2df0b47 Mon Sep 17 00:00:00 2001 From: Jay L Date: Sun, 15 May 2022 22:09:54 +1000 Subject: [PATCH 03/11] Refactor and Cleanup of Stamina.cs --- .../Difficulty/Skills/Stamina.cs | 60 ++++++++++--------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index b10da95444..6d4a1fd948 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -1,7 +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; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; @@ -10,29 +9,28 @@ using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { - class SingleKeyStamina + /// + /// Stamina of a single key, calculated based on repetition speed. + /// + public class SingleKeyStamina { private double previousHitTime = -1; - private double strainValueOf(DifficultyHitObject current) + /// + /// Similar to + /// + public double StrainValueOf(DifficultyHitObject current) { if (previousHitTime == -1) { previousHitTime = current.StartTime; return 0; } - else - { - double objectStrain = 0.5; - objectStrain += speedBonus(current.StartTime - previousHitTime); - previousHitTime = current.StartTime; - return objectStrain; - } - } - public double StrainValueAt(DifficultyHitObject current) - { - return strainValueOf(current); + double objectStrain = 0.5; + objectStrain += speedBonus(current.StartTime - previousHitTime); + previousHitTime = current.StartTime; + return objectStrain; } /// @@ -41,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// The duration between the current and previous note hit using the same key. private double speedBonus(double notePairDuration) { - return 175 / Math.Pow(notePairDuration + 100, 1); + return 175 / (notePairDuration + 100); } } @@ -56,7 +54,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills protected override double SkillMultiplier => 1; protected override double StrainDecayBase => 0.4; - private SingleKeyStamina[] keyStamina = new SingleKeyStamina[4] + /// + /// Stamina of each individual keys, calculated based on repetition speed. + /// + private readonly SingleKeyStamina[] keyStamina = { new SingleKeyStamina(), new SingleKeyStamina(), @@ -64,7 +65,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills new SingleKeyStamina() }; + /// + /// Current index to for a don hit. + /// private int donIndex = 1; + + /// + /// Current index to for a kat hit. + /// private int katIndex = 3; /// @@ -76,23 +84,21 @@ 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) { donIndex = donIndex == 0 ? 1 : 0; return keyStamina[donIndex]; } - else - { - katIndex = katIndex == 2 ? 3 : 2; - return keyStamina[katIndex]; - } - } - private double sigmoid(double val, double center, double width) - { - return Math.Tanh(Math.E * -(val - center) / width); + katIndex = katIndex == 2 ? 3 : 2; + return keyStamina[katIndex]; } protected override double StrainValueOf(DifficultyHitObject current) @@ -103,9 +109,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills } TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)current; - double objectStrain = getNextSingleKeyStamina(hitObject).StrainValueAt(hitObject); - - return objectStrain; + return getNextSingleKeyStamina(hitObject).StrainValueOf(hitObject); } } } From b44afb19b16a19e7464c9f1281307f4c396e1fc7 Mon Sep 17 00:00:00 2001 From: Jay L Date: Sun, 15 May 2022 22:10:18 +1000 Subject: [PATCH 04/11] Include Convert Penalty For low colour variance --- .../Difficulty/TaikoDifficultyCalculator.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index de69f63bf4..5816b9957c 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -76,6 +76,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty double staminaPenalty = simpleColourPenalty(staminaRating, colourRating); staminaRating *= staminaPenalty; + if (beatmap.BeatmapInfo.Ruleset.OnlineID == 0 && colourRating < 0.05) + { + staminaPenalty *= 0.25; + } + double combinedRating = locallyCombinedDifficulty(colour, rhythm, stamina, staminaPenalty); double separatedRating = norm(1.5, colourRating, rhythmRating, staminaRating); double starRating = 1.4 * separatedRating + 0.5 * combinedRating; From d94e30bed3985f3301ea8d769ad5060affc94755 Mon Sep 17 00:00:00 2001 From: Jay L Date: Sun, 15 May 2022 22:11:01 +1000 Subject: [PATCH 05/11] Rewrite Performance difficultyValue Calculation To change values in line with the community survey, pp has been rescaled immensely, with changes on both the lower and upper end. --- .../Difficulty/TaikoPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index a8122551ff..8d99fd3b87 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty private double computeDifficultyValue(ScoreInfo score, TaikoDifficultyAttributes attributes) { - double difficultyValue = Math.Pow(5.0 * Math.Max(1.0, attributes.StarRating / 0.0075) - 4.0, 2.0) / 100000.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; From 94294ba351c2c4609328da2ada403a37b89c55b7 Mon Sep 17 00:00:00 2001 From: Jay L Date: Mon, 16 May 2022 10:26:47 +1000 Subject: [PATCH 06/11] Apply Review Changes --- osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs | 2 +- osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 6d4a1fd948..7846e97a65 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// public class SingleKeyStamina { - private double previousHitTime = -1; + private double previousHitTime; /// /// Similar to diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 5816b9957c..1aa31c6fe4 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -76,6 +76,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty 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; From c7ec95c0d0d4416561ef622e134b1aef81ea285e Mon Sep 17 00:00:00 2001 From: Jay L Date: Mon, 16 May 2022 14:25:07 +1000 Subject: [PATCH 07/11] Rectify Review Changes --- osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 7846e97a65..d54da8b869 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -14,14 +14,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// public class SingleKeyStamina { - private double previousHitTime; + private double? previousHitTime; /// /// Similar to /// public double StrainValueOf(DifficultyHitObject current) { - if (previousHitTime == -1) + if (previousHitTime == null) { previousHitTime = current.StartTime; return 0; From db6abd86f03bc538fa2c3ffda98120b883f37d5c Mon Sep 17 00:00:00 2001 From: Jay L Date: Mon, 16 May 2022 14:48:39 +1000 Subject: [PATCH 08/11] Rectify null value call tired programming is never a good idea --- osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index d54da8b869..744dafd57e 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills } double objectStrain = 0.5; - objectStrain += speedBonus(current.StartTime - previousHitTime); + objectStrain += speedBonus(current.StartTime - previousHitTime.Value); previousHitTime = current.StartTime; return objectStrain; } From 934bcaf82e8851415a44656151f30a1c9eb5ed7f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 16 May 2022 14:22:30 +0900 Subject: [PATCH 09/11] 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 226da7df09..51332a1ece 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Taiko.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; - [TestCase(2.2420075288523802d, 200, "diffcalc-test")] - [TestCase(2.2420075288523802d, 200, "diffcalc-test-strong")] + [TestCase(1.9971301024093662d, 200, "diffcalc-test")] + [TestCase(1.9971301024093662d, 200, "diffcalc-test-strong")] public void Test(double expectedStarRating, int expectedMaxCombo, string name) => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(3.134084469440479d, 200, "diffcalc-test")] - [TestCase(3.134084469440479d, 200, "diffcalc-test-strong")] + [TestCase(3.1645810961313674d, 200, "diffcalc-test")] + [TestCase(3.1645810961313674d, 200, "diffcalc-test-strong")] public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new TaikoModDoubleTime()); From 63a3829768be608c125076767538910104b4fe89 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 23 May 2022 14:19:25 +0900 Subject: [PATCH 10/11] Split out SingleKeyStamina class --- .../Difficulty/Skills/SingleKeyStamina.cs | 42 +++++++++++++++++++ .../Difficulty/Skills/Stamina.cs | 34 --------------- 2 files changed, 42 insertions(+), 34 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs new file mode 100644 index 0000000000..cabfd231d8 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs @@ -0,0 +1,42 @@ +// 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 744dafd57e..32d8b70485 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -9,40 +9,6 @@ using osu.Game.Rulesets.Taiko.Objects; 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); - } - } - /// /// Calculates the stamina coefficient of taiko difficulty. /// From 6cfe35360a6ed2e666047dce18a45570b098041a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 23 May 2022 14:36:06 +0900 Subject: [PATCH 11/11] Refactor key indexing --- .../Difficulty/Skills/Stamina.cs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 32d8b70485..61bcbfa59d 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -20,26 +20,27 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills protected override double SkillMultiplier => 1; protected override double StrainDecayBase => 0.4; - /// - /// Stamina of each individual keys, calculated based on repetition speed. - /// - private readonly SingleKeyStamina[] keyStamina = + private readonly SingleKeyStamina[] centreKeyStamina = { new SingleKeyStamina(), - new SingleKeyStamina(), + new SingleKeyStamina() + }; + + private readonly SingleKeyStamina[] rimKeyStamina = + { new SingleKeyStamina(), new SingleKeyStamina() }; /// - /// Current index to for a don hit. + /// Current index into for a centre hit. /// - private int donIndex = 1; + private int centreKeyIndex; /// - /// Current index to for a kat hit. + /// Current index into for a rim hit. /// - private int katIndex = 3; + private int rimKeyIndex; /// /// Creates a skill. @@ -59,12 +60,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills // Alternate key for the same color. if (current.HitType == HitType.Centre) { - donIndex = donIndex == 0 ? 1 : 0; - return keyStamina[donIndex]; + centreKeyIndex = (centreKeyIndex + 1) % 2; + return centreKeyStamina[centreKeyIndex]; } - katIndex = katIndex == 2 ? 3 : 2; - return keyStamina[katIndex]; + rimKeyIndex = (rimKeyIndex + 1) % 2; + return rimKeyStamina[rimKeyIndex]; } protected override double StrainValueOf(DifficultyHitObject current)