// 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); int repetitionStart = 0; for (int i = 0; i < hitObjects.Count; i++) { history.Enqueue(hitObjects[i]); if (!history.Full) continue; if (!containsPatternRepeat(history, patternLength)) { repetitionStart = i - 2 * patternLength; continue; } int repeatedLength = i - repetitionStart; if (repeatedLength < roll_min_repetitions) continue; markObjectsAsCheese(repetitionStart, 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; 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(0, i - tlLength), i); } } /// /// Marks all objects from index up until (exclusive) as . /// private void markObjectsAsCheese(int start, int end) { for (int i = start; i < end; ++i) hitObjects[i].StaminaCheese = true; } } }