From 15372267e17c78c919c764362d0d32c5c98d99cd Mon Sep 17 00:00:00 2001 From: vun Date: Sat, 25 Jun 2022 22:42:56 +0800 Subject: [PATCH] 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; }