1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-13 05:53:10 +08:00

[WIP] Colour evaluator for new colour encoding

This commit is contained in:
vun 2022-06-28 10:38:58 +08:00
parent 55e5b41c03
commit cba47f8202
7 changed files with 61 additions and 43 deletions

View File

@ -12,9 +12,19 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
return Math.Tanh(Math.E * -(val - center) / width); return Math.Tanh(Math.E * -(val - center) / width);
} }
public static double EvaluateDifficultyOf(TaikoDifficultyHitObjectColour colour) public static double EvaluateDifficultyOf(TaikoDifficultyHitObjectColour? colour)
{ {
return 1; if (colour == null) return 0;
double difficulty = 7.5 * Math.Log(colour.Encoding.Payload.Length + 1, 10);
// foreach (ColourEncoding encoding in colour.Encoding.Payload)
// {
// difficulty += sigmoid(encoding.MonoRunLength, 1, 1) * 0.4 + 0.6;
// }
difficulty *= -sigmoid(colour.RepetitionInterval, 1, 7);
// difficulty *= -sigmoid(colour.RepetitionInterval, 2, 2) * 0.5 + 0.5;
return difficulty;
} }
public static double EvaluateDifficultyOf(DifficultyHitObject current) public static double EvaluateDifficultyOf(DifficultyHitObject current)

View File

@ -16,7 +16,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
/// <param name="interval">The interval between the current and previous note hit using the same key.</param> /// <param name="interval">The interval between the current and previous note hit using the same key.</param>
private static double speedBonus(double interval) private static double speedBonus(double interval)
{ {
return Math.Pow(0.8, interval / 1000); // return 10 / Math.Pow(interval, 0.6);
return Math.Pow(0.1, interval / 1000);
} }
/// <summary> /// <summary>
@ -40,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
return 0.0; return 0.0;
} }
double objectStrain = 0.85; double objectStrain = 1;
objectStrain *= speedBonus(taikoCurrent.StartTime - keyPrevious.StartTime); objectStrain *= speedBonus(taikoCurrent.StartTime - keyPrevious.StartTime);
return objectStrain; return objectStrain;
} }

View File

@ -14,7 +14,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
/// Amount of consecutive encoding with the same <see cref="MonoRunLength" /> /// Amount of consecutive encoding with the same <see cref="MonoRunLength" />
/// </summary> /// </summary>
public int EncodingRunLength = 1; public int EncodingRunLength = 1;
/// <summary>
/// How many notes are encoded with this encoding
/// </summary>
public int NoteLength => MonoRunLength + EncodingRunLength;
/// <summary> /// <summary>
/// Beginning index in the data that this encodes /// Beginning index in the data that this encodes
/// </summary> /// </summary>
@ -27,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
public static List<ColourEncoding> Encode(List<DifficultyHitObject> data) public static List<ColourEncoding> Encode(List<DifficultyHitObject> data)
{ {
// Encoding mono lengths // Compute mono lengths
List<ColourEncoding> firstPass = new List<ColourEncoding>(); List<ColourEncoding> firstPass = new List<ColourEncoding>();
ColourEncoding? lastEncoded = null; ColourEncoding? lastEncoded = null;
for (int i = 0; i < data.Count; i++) for (int i = 0; i < data.Count; i++)
@ -36,7 +41,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
// This ignores all non-note objects, which may or may not be the desired behaviour // This ignores all non-note objects, which may or may not be the desired behaviour
TaikoDifficultyHitObject previousObject = (TaikoDifficultyHitObject)taikoObject.PreviousNote(0); TaikoDifficultyHitObject previousObject = (TaikoDifficultyHitObject)taikoObject.PreviousNote(0);
if (previousObject == null || lastEncoded == null || taikoObject.HitType != previousObject.HitType) if (
previousObject == null ||
lastEncoded == null ||
taikoObject.HitType != previousObject.HitType ||
taikoObject.Rhythm.Ratio > 1.9) // Reset colour after a slow down of 2x (set as 1.9x for margin of error)
{ {
lastEncoded = new ColourEncoding(); lastEncoded = new ColourEncoding();
lastEncoded.StartIndex = i; lastEncoded.StartIndex = i;
@ -47,7 +56,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
lastEncoded.MonoRunLength += 1; lastEncoded.MonoRunLength += 1;
} }
// Encode encoding lengths // Compute encoding lengths
List<ColourEncoding> secondPass = new List<ColourEncoding>(); List<ColourEncoding> secondPass = new List<ColourEncoding>();
lastEncoded = null; lastEncoded = null;
for (int i = 0; i < firstPass.Count; i++) for (int i = 0; i < firstPass.Count; i++)

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -19,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
/// </summary> /// </summary>
public class TaikoDifficultyHitObject : DifficultyHitObject public class TaikoDifficultyHitObject : DifficultyHitObject
{ {
private readonly IReadOnlyList<TaikoDifficultyHitObject> monoDifficultyHitObjects; private readonly IReadOnlyList<TaikoDifficultyHitObject>? monoDifficultyHitObjects;
public readonly int MonoIndex; public readonly int MonoIndex;
private readonly IReadOnlyList<TaikoDifficultyHitObject> noteObjects; private readonly IReadOnlyList<TaikoDifficultyHitObject> noteObjects;
public readonly int NoteIndex; public readonly int NoteIndex;
@ -34,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
/// by other skills in the future. /// by other skills in the future.
/// This need to be writeable by TaikoDifficultyHitObjectColour so that it can assign potentially reused instances /// This need to be writeable by TaikoDifficultyHitObjectColour so that it can assign potentially reused instances
/// </summary> /// </summary>
public TaikoDifficultyHitObjectColour Colour; public TaikoDifficultyHitObjectColour? Colour;
/// <summary> /// <summary>
/// The hit type of this hit object. /// The hit type of this hit object.
@ -65,14 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
centreObjects, rimObjects, noteObjects, difficultyHitObjects.Count) centreObjects, rimObjects, noteObjects, difficultyHitObjects.Count)
); );
} }
TaikoDifficultyHitObjectColour.EncodeAndAssign(difficultyHitObjects);
List<TaikoDifficultyHitObjectColour> colours = TaikoDifficultyHitObjectColour.EncodeAndAssign(difficultyHitObjects);
// Pre-evaluate colours
for (int i = 0; i < colours.Count; i++)
{
colours[i].EvaluatedDifficulty = ColourEvaluator.EvaluateDifficultyOf(colours[i]);
}
return difficultyHitObjects; return difficultyHitObjects;
} }

View File

@ -5,12 +5,11 @@ using osu.Game.Rulesets.Difficulty.Preprocessing;
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
{ {
/// <summary> /// <summary>
/// Stores colour compression information for a <see cref="TaikoDifficultyHitObject"/>. /// Stores colour compression information for a <see cref="TaikoDifficultyHitObject"/>. This is only present for the
/// first <see cref="TaikoDifficultyHitObject"/> in a <see cref="CoupledColourEncoding"/> chunk.
/// </summary> /// </summary>
public class TaikoDifficultyHitObjectColour public class TaikoDifficultyHitObjectColour
{ {
public CoupledColourEncoding Encoding { get; private set; }
private const int max_repetition_interval = 16; private const int max_repetition_interval = 16;
/// <summary> /// <summary>
@ -21,11 +20,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
public int RepetitionInterval { get; private set; } = max_repetition_interval + 1; public int RepetitionInterval { get; private set; } = max_repetition_interval + 1;
/// <summary> /// <summary>
/// Evaluated colour difficulty is cached here, as difficulty don't need to be calculated per-note. /// Encoding information of <see cref="TaikoDifficultyHitObjectColour"/>.
/// </summary> /// </summary>
/// TODO: Consider having all evaluated difficulty cached in TaikoDifficultyHitObject instead, since we may be public CoupledColourEncoding Encoding { get; private set; }
/// reusing evaluator results in the future.
public double EvaluatedDifficulty = 0;
public TaikoDifficultyHitObjectColour? Previous { get; private set; } = null; public TaikoDifficultyHitObjectColour? Previous { get; private set; } = null;
@ -60,8 +57,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
} }
/// <summary> /// <summary>
/// Finds the closest previous <see cref="TaikoDifficultyHitObjectColour"/> that has the identical delta value /// Finds the closest previous <see cref="TaikoDifficultyHitObjectColour"/> that has the identical <see cref="CoupledColourEncoding.Payload"/>.
/// and run length with the current instance, and returns the amount of notes between them. /// Interval is defined as the amount of <see cref="CoupledColourEncoding"/> chunks between the current and repeated encoding.
/// </summary> /// </summary>
public void FindRepetitionInterval() public void FindRepetitionInterval()
{ {
@ -72,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
} }
TaikoDifficultyHitObjectColour? other = Previous.Previous; TaikoDifficultyHitObjectColour? other = Previous.Previous;
int interval = this.Encoding.StartIndex - other.Encoding.EndIndex; int interval = 2;
while (interval < max_repetition_interval) while (interval < max_repetition_interval)
{ {
if (Encoding.hasIdenticalPayload(other.Encoding)) if (Encoding.hasIdenticalPayload(other.Encoding))
@ -84,7 +81,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
other = other.Previous; other = other.Previous;
if (other == null) break; if (other == null) break;
interval = this.Encoding.StartIndex - other.Encoding.EndIndex; ++interval;
} }
RepetitionInterval = max_repetition_interval + 1; RepetitionInterval = max_repetition_interval + 1;

View File

@ -1,12 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable using System;
using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
using osu.Game.Rulesets.Taiko.Difficulty.Evaluators;
namespace osu.Game.Rulesets.Taiko.Difficulty.Skills namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
{ {
@ -18,6 +18,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
protected override double SkillMultiplier => 1; protected override double SkillMultiplier => 1;
protected override double StrainDecayBase => 0.4; protected override double StrainDecayBase => 0.4;
/// <summary>
/// Applies a speed bonus dependent on the time since the last hit.
/// </summary>
/// <param name="interval">The interval between the current and previous note hit using the same key.</param>
private static double speedBonus(double interval)
{
return Math.Pow(0.4, interval / 1000);
}
public Colour(Mod[] mods) public Colour(Mod[] mods)
: base(mods) : base(mods)
{ {
@ -25,18 +34,20 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
protected override double StrainValueOf(DifficultyHitObject current) protected override double StrainValueOf(DifficultyHitObject current)
{ {
TaikoDifficultyHitObjectColour colour = ((TaikoDifficultyHitObject)current).Colour; double difficulty = ColourEvaluator.EvaluateDifficultyOf(current);
double difficulty = colour == null ? 0 : colour.EvaluatedDifficulty; // difficulty *= speedBonus(current.DeltaTime);
// if (current != null && colour != null) // TaikoDifficultyHitObject? taikoCurrent = (TaikoDifficultyHitObject)current;
// TaikoDifficultyHitObjectColour? colour = taikoCurrent?.Colour;
// if (taikoCurrent != null && colour != null)
// { // {
// ColourEncoding[] payload = colour.Encoding.Payload; // ColourEncoding[] payload = colour.Encoding.Payload;
// string payloadDisplay = ""; // string payloadDisplay = "";
// for (int i = 0; i < payload.Length; ++i) // for (int i = 0; i < payload.Length; ++i)
// { // {
// payloadDisplay += $",({payload[i].MonoRunLength},{payload[i].EncodingRunLength})"; // payloadDisplay += $",({payload[i].MonoRunLength}|{payload[i].EncodingRunLength})";
// } // }
// System.Console.WriteLine($"{current.StartTime},{colour.RepetitionInterval},{colour.Encoding.RunLength}{payloadDisplay}"); // System.Console.WriteLine($"{current.StartTime},{difficulty},{colour.RepetitionInterval},{colour.Encoding.RunLength}{payloadDisplay}");
// } // }
return difficulty; return difficulty;

View File

@ -4,17 +4,16 @@ using System.Linq;
using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Difficulty.Evaluators;
namespace osu.Game.Rulesets.Taiko.Difficulty.Skills namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
{ {
public class Peaks : Skill public class Peaks : Skill
{ {
private const double rhythm_skill_multiplier = 0.32 * final_multiplier; private const double rhythm_skill_multiplier = 0.3 * final_multiplier;
private const double colour_skill_multiplier = 0.33 * final_multiplier; private const double colour_skill_multiplier = 0.39 * final_multiplier;
private const double stamina_skill_multiplier = 0.4 * final_multiplier; private const double stamina_skill_multiplier = 0.33 * final_multiplier;
private const double final_multiplier = 0.047; private const double final_multiplier = 0.06;
private readonly Rhythm rhythm; private readonly Rhythm rhythm;
private readonly Colour colour; private readonly Colour colour;