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:
parent
f42aac9954
commit
15372267e1
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user