1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-17 22:17:25 +08:00

Implement new colour encoding

This commit is contained in:
vun 2022-06-25 22:42:56 +08:00
parent f42aac9954
commit 15372267e1
6 changed files with 188 additions and 77 deletions

View File

@ -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)

View File

@ -0,0 +1,68 @@
using System.Collections.Generic;
using osu.Game.Rulesets.Difficulty.Preprocessing;
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
{
public class ColourEncoding
{
/// <summary>
/// Amount consecutive notes of the same colour
/// </summary>
public int MonoRunLength = 1;
/// <summary>
/// Amount of consecutive encoding with the same <see cref="MonoRunLength" />
/// </summary>
public int EncodingRunLength = 1;
/// <summary>
/// Beginning index in the data that this encodes
/// </summary>
public int StartIndex = 0;
public bool isIdenticalTo(ColourEncoding other)
{
return other.MonoRunLength == MonoRunLength && other.EncodingRunLength == EncodingRunLength;
}
public static List<ColourEncoding> Encode(List<DifficultyHitObject> data)
{
// Encoding mono lengths
List<ColourEncoding> firstPass = new List<ColourEncoding>();
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<ColourEncoding> secondPass = new List<ColourEncoding>();
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;
}
}
}

View File

@ -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;
/// <summary>
/// Beginning index in the data that this encodes
/// </summary>
public int StartIndex { get; private set; } = 0;
public int EndIndex { get; private set; } = 0;
private CoupledColourEncoding(ColourEncoding[] payload)
{
Payload = payload;
}
public static List<CoupledColourEncoding> Encode(List<ColourEncoding> data)
{
List<CoupledColourEncoding> encoded = new List<CoupledColourEncoding>();
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;
}
}
}

View File

@ -52,21 +52,21 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
/// <param name="clockRate">The rate at which the gameplay clock is run at.</param>
public static List<DifficultyHitObject> Create(IBeatmap beatmap, double clockRate)
{
List<DifficultyHitObject> difficultyHitObject = new List<DifficultyHitObject>();
List<DifficultyHitObject> difficultyHitObjects = new List<DifficultyHitObject>();
List<TaikoDifficultyHitObject> centreObjects = new List<TaikoDifficultyHitObject>();
List<TaikoDifficultyHitObject> rimObjects = new List<TaikoDifficultyHitObject>();
List<TaikoDifficultyHitObject> noteObjects = new List<TaikoDifficultyHitObject>();
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<TaikoDifficultyHitObjectColour> colours = TaikoDifficultyHitObjectColour.CreateColoursFor(difficultyHitObject);
List<TaikoDifficultyHitObjectColour> 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;
}
/// <summary>

View File

@ -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
/// </summary>
public class TaikoDifficultyHitObjectColour
{
public CoupledColourEncoding Encoding { get; private set; }
private const int max_repetition_interval = 16;
public TaikoDifficultyHitObjectColour Previous { get; private set; }
/// <summary>
/// True if the current colour is different from the previous colour.
/// </summary>
public bool Delta { get; private set; }
/// <summary>
/// How many notes are Delta repeated
/// </summary>
public int DeltaRunLength { get; private set; }
/// <summary>
/// How many notes between the current and previous identical <see cref="TaikoDifficultyHitObjectColour"/>.
/// Negative number means that there is no repetition in range.
/// If no repetition is found this will have a value of <see cref="max_repetition_interval"/> + 1.
/// </summary>
public int RepetitionInterval { get; private set; }
public int RepetitionInterval { get; private set; } = max_repetition_interval + 1;
/// <summary>
/// Evaluated colour difficulty is cached here, as difficulty don't need to be calculated per-note.
/// </summary>
/// 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;
/// <summary>
/// Get the <see cref="TaikoDifficultyHitObjectColour"/> instance for the given hitObject. This is implemented
/// as a static function instead of constructor to allow for reusing existing instances.
/// </summary>
public static List<TaikoDifficultyHitObjectColour> CreateColoursFor(List<DifficultyHitObject> hitObjects)
public TaikoDifficultyHitObjectColour? repeatedColour { get; private set; } = null;
public TaikoDifficultyHitObjectColour(CoupledColourEncoding encoding)
{
Encoding = encoding;
}
public static List<TaikoDifficultyHitObjectColour> EncodeAndAssign(List<DifficultyHitObject> hitObjects)
{
List<TaikoDifficultyHitObjectColour> colours = new List<TaikoDifficultyHitObjectColour>();
for (int i = 0; i < hitObjects.Count; i++)
List<CoupledColourEncoding> 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;
}
}
}
}

View File

@ -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;
}