Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite of the colour skill & refactoring of difficulty calculation within osu!taiko #19571

Merged
merged 75 commits into from
Aug 25, 2022
Merged
Show file tree
Hide file tree
Changes from 68 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
f01deae
Colour compression preprocessing implementation
vunyunt May 24, 2022
1972bdd
Working colour encoding
vunyunt May 26, 2022
2b6a330
Merge branch 'ppy:master' into colour-rework
vunyunt May 26, 2022
86ffa81
Implement stamina evaluator (untested yet)
vunyunt May 31, 2022
8bbe70b
Fix NullPointerReference
vunyunt May 31, 2022
0a21f7c
Implement mono history in TaikoDifficultyHitObject
vunyunt May 31, 2022
c987708
Merge remote-tracking branch 'apollo/dho' into colour-rework
vunyunt Jun 2, 2022
54dc22b
Merge branch 'ppy:master' into colour-rework
vunyunt Jun 2, 2022
56a4034
Change RepetitionInterval to have max_repetition_interval + 1 when no…
vunyunt Jun 2, 2022
3dd0c4a
[WIP] Colour rework
vunyunt Jun 6, 2022
bfada36
Merge branch 'taiko-evaluators' into colour-rework
vunyunt Jun 6, 2022
07d3a7b
Fix logic error, minor stamina changes
vunyunt Jun 6, 2022
29b259c
Merge branch 'ppy:master' into colour-rework
vunyunt Jun 6, 2022
fd49a27
Fix encoding repetition, parameter adjustments
vunyunt Jun 7, 2022
d8d4ac4
Refactor LocallyCombinedDifficulty to an external skill
vunyunt Jun 8, 2022
5793ca5
Parameter tweaks
vunyunt Jun 9, 2022
2e004bf
Merge branch 'ppy:master' into colour-rework
vunyunt Jun 9, 2022
6dbaf0a
Refactor
Lawtrohux Jun 9, 2022
4c574eb
Rescale multipliers (values unaffected)
vunyunt Jun 9, 2022
54f41ab
Merge branch 'colour-rework-huismet' of https://github.com/Lawtrohux/…
vunyunt Jun 9, 2022
2881406
Nerf alternating pattern slightly, value rescale
vunyunt Jun 9, 2022
2b2150a
Refactor TaikoDifficultyHitObject creation into the class as a static…
vunyunt Jun 10, 2022
d7a5144
Merge branch 'ppy:master' into colour-rework
vunyunt Jun 10, 2022
319d0aa
Merge remote-tracking branch 'ppy/master' into colour-rework
vunyunt Jun 13, 2022
da1d99d
Parameter tweaks, change repetition interval definition
vunyunt Jun 19, 2022
95c43d9
Merge remote-tracking branch 'ppy/master' into colour-rework
vunyunt Jun 19, 2022
5796431
Revert performance calculator to upstream
vunyunt Jun 19, 2022
3529514
Disablle nullable in TaikoDifficultyHitObjectColour
vunyunt Jun 19, 2022
c5fd483
Flatten speed bonus for stamina
vunyunt Jun 22, 2022
f42aac9
TAIKO-6 Pre-evaluate colour to avoid per-note evaluation
vunyunt Jun 23, 2022
1537226
Implement new colour encoding
vunyunt Jun 25, 2022
8c16258
Comment out logging for debugging purposes
vunyunt Jun 25, 2022
55e5b41
Merge branch 'ppy:master' into colour-encoding-2
vunyunt Jun 25, 2022
cba47f8
[WIP] Colour evaluator for new colour encoding
vunyunt Jun 28, 2022
5f8d21f
Per encoding evaluation
vunyunt Jul 1, 2022
505a24a
Implement new colour encoding and evaluator
vunyunt Jul 5, 2022
f6dedc7
Fixed encoding logic, parameter adjustments
vunyunt Jul 5, 2022
6660379
TAIKO-6 Tweak encoding and parameters, reduce rhythm weight
vunyunt Jul 7, 2022
1cb18f8
Refactor colour encoding to avoid circular dependencies
vunyunt Jul 14, 2022
45c055b
Move rhythm preprocessing to its own folder
vunyunt Jul 14, 2022
7e3f62a
Codequality parse
Lawtrohux Jul 15, 2022
c8b7902
Reintroduce Convert Nerf, Rescale Multiplier
Lawtrohux Jul 15, 2022
9994f13
Merge branch 'ppy:master' into colour-encoding-2
vunyunt Jul 15, 2022
8a17b50
Increase SpeedBonus Cap to 600BPM
Lawtrohux Jul 16, 2022
8beb556
Fix speed bonus
vunyunt Jul 16, 2022
a66fd87
Fix speed bonus comment
vunyunt Jul 16, 2022
e82e11e
Fix SpeedBonus xml
Lawtrohux Jul 17, 2022
77fa567
Adjust tests
Lawtrohux Jul 17, 2022
9e299bb
Delete Idea Project Notation
Lawtrohux Jul 17, 2022
cb63ec2
Partial Review changes
Lawtrohux Jul 20, 2022
08dd9c7
Fix Convert-related nerf
Lawtrohux Jul 20, 2022
b7567f7
Share sigmoid, Fix Preprocessor XML
Lawtrohux Jul 21, 2022
7917a60
Move TaikoDifficultyHitObject creation back to TaikoDifficultyCalculator
vunyunt Jul 21, 2022
e4086b0
Implement stateless colour evaluator and required encoding changes
vunyunt Jul 21, 2022
4433f90
Fix and add comments
vunyunt Jul 22, 2022
6359c1a
Fix outdated comment
vunyunt Jul 22, 2022
17d418d
Merge remote-tracking branch 'ppy/master' into colour-encoding-2
vunyunt Jul 22, 2022
7d4593e
Fix comments
vunyunt Jul 22, 2022
fc08d77
Remove review-specific comment
vunyunt Jul 22, 2022
49063ab
Merge remote-tracking branch 'ppy/master' into colour-encoding-2
vunyunt Jul 22, 2022
d686e84
Merge branch 'master' into colour-encoding-2
smoogipoo Aug 15, 2022
502e31d
General refactoring
smoogipoo Aug 15, 2022
94c6bee
Use ctor in a place that looks visually weird
smoogipoo Aug 15, 2022
21d2998
Privatise internals of TaikoColourDifficultyPreprocessor
smoogipoo Aug 15, 2022
78283ce
Remove TaikoDifficultyPreprocessor
smoogipoo Aug 15, 2022
4d4ee05
Whoops I meant to remove these
smoogipoo Aug 15, 2022
c03e473
Fix notes not being added to list
smoogipoo Aug 15, 2022
8e0049c
Add back null check
smoogipoo Aug 15, 2022
40b1554
Change FindRepetitionInterval to start with one previous encoding
vunyunt Aug 18, 2022
5dcd4ce
Naming changes
vunyunt Aug 19, 2022
51176e9
Naming changes
vunyunt Aug 19, 2022
a26de0a
Add HitType property to MonoStreak
vunyunt Aug 19, 2022
684efef
Add FirstHitObject as a property of encoding classes
vunyunt Aug 19, 2022
f3e1287
Remove redundant using statement
vunyunt Aug 19, 2022
fb9bb2d
Declare Parent as non-nullable
vunyunt Aug 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ public class TaikoDifficultyCalculatorTest : DifficultyCalculatorTest
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";

[TestCase(1.9971301024093662d, 200, "diffcalc-test")]
[TestCase(1.9971301024093662d, 200, "diffcalc-test-strong")]
[TestCase(3.1098944660126882d, 200, "diffcalc-test")]
[TestCase(3.1098944660126882d, 200, "diffcalc-test-strong")]
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
=> base.Test(expectedStarRating, expectedMaxCombo, name);

[TestCase(3.1645810961313674d, 200, "diffcalc-test")]
[TestCase(3.1645810961313674d, 200, "diffcalc-test-strong")]
[TestCase(4.0974106752474251d, 200, "diffcalc-test")]
[TestCase(4.0974106752474251d, 200, "diffcalc-test-strong")]
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
=> Test(expectedStarRating, expectedMaxCombo, name, new TaikoModDoubleTime());

Expand Down
67 changes: 67 additions & 0 deletions osu.Game.Rulesets.Taiko/Difficulty/Evaluators/ColourEvaluator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// 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.

using System;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour;
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data;

namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
{
public class ColourEvaluator
{
/// <summary>
/// A sigmoid function. It gives a value between (middle - height/2) and (middle + height/2).
/// </summary>
/// <param name="val">The input value.</param>
/// <param name="center">The center of the sigmoid, where the largest gradient occurs and value is equal to middle.</param>
/// <param name="width">The radius of the sigmoid, outside of which values are near the minimum/maximum.</param>
/// <param name="middle">The middle of the sigmoid output.</param>
/// <param name="height">The height of the sigmoid output. This will be equal to max value - min value.</param>
private static double sigmoid(double val, double center, double width, double middle, double height)
{
double sigmoid = Math.Tanh(Math.E * -(val - center) / width);
return sigmoid * (height / 2) + middle;
}

/// <summary>
/// Evaluate the difficulty of the first note of a <see cref="MonoEncoding"/>.
/// </summary>
public static double EvaluateDifficultyOf(MonoEncoding encoding)
{
return sigmoid(encoding.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(encoding.Parent!) * 0.5;
}

/// <summary>
/// Evaluate the difficulty of the first note of a <see cref="ColourEncoding"/>.
/// </summary>
public static double EvaluateDifficultyOf(ColourEncoding encoding)
{
return sigmoid(encoding.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(encoding.Parent!);
}

/// <summary>
/// Evaluate the difficulty of the first note of a <see cref="CoupledColourEncoding"/>.
/// </summary>
public static double EvaluateDifficultyOf(CoupledColourEncoding encoding)
{
return 2 * (1 - sigmoid(encoding.RepetitionInterval, 2, 2, 0.5, 1));
}

public static double EvaluateDifficultyOf(DifficultyHitObject hitObject)
{
TaikoDifficultyHitObjectColour colour = ((TaikoDifficultyHitObject)hitObject).Colour;
double difficulty = 0.0d;

if (colour.MonoEncoding != null) // Difficulty for MonoEncoding
difficulty += EvaluateDifficultyOf(colour.MonoEncoding);
if (colour.ColourEncoding != null) // Difficulty for ColourEncoding
difficulty += EvaluateDifficultyOf(colour.ColourEncoding);
if (colour.CoupledColourEncoding != null) // Difficulty for CoupledColourEncoding
difficulty += EvaluateDifficultyOf(colour.CoupledColourEncoding);

return difficulty;
}
}
}
53 changes: 53 additions & 0 deletions osu.Game.Rulesets.Taiko/Difficulty/Evaluators/StaminaEvaluator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 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.

using System;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
using osu.Game.Rulesets.Taiko.Objects;

namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
{
public class StaminaEvaluator
{
/// <summary>
/// Applies a speed bonus dependent on the time since the last hit performed using this key.
/// </summary>
/// <param name="interval">The interval between the current and previous note hit using the same key.</param>
private static double speedBonus(double interval)
{
// Cap to 600bpm 1/4, 25ms note interval, 50ms key interval
// Interval will be capped at a very small value to avoid infinite/negative speed bonuses.
// TODO - This is a temporary measure as we need to implement methods of detecting playstyle-abuse of SpeedBonus.
interval = Math.Max(interval, 50);

return 30 / interval;
}

/// <summary>
/// Evaluates the minimum mechanical stamina required to play the current object. This is calculated using the
/// maximum possible interval between two hits using the same key, by alternating 2 keys for each colour.
/// </summary>
public static double EvaluateDifficultyOf(DifficultyHitObject current)
{
if (current.BaseObject is not Hit)
{
return 0.0;
}

// Find the previous hit object hit by the current key, which is two notes of the same colour prior.
TaikoDifficultyHitObject taikoCurrent = (TaikoDifficultyHitObject)current;
TaikoDifficultyHitObject? keyPrevious = taikoCurrent.PreviousMono(1);

if (keyPrevious == null)
{
// There is no previous hit object hit by the current key
return 0.0;
}

double objectStrain = 0.5; // Add a base strain to all objects
objectStrain += speedBonus(taikoCurrent.StartTime - keyPrevious.StartTime);
return objectStrain;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// 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.

using System.Collections.Generic;
using osu.Game.Rulesets.Taiko.Objects;

namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
{
/// <summary>
/// Encodes a list of <see cref="MonoEncoding"/>s.
/// <see cref="MonoEncoding"/>s with the same <see cref="MonoEncoding.RunLength"/> are grouped together.
/// </summary>
public class ColourEncoding
{
/// <summary>
/// <see cref="MonoEncoding"/>s that are grouped together within this <see cref="ColourEncoding"/>.
/// </summary>
public readonly List<MonoEncoding> Payload = new List<MonoEncoding>();

/// <summary>
/// The parent <see cref="CoupledColourEncoding"/> that contains this <see cref="ColourEncoding"/>
/// </summary>
public CoupledColourEncoding? Parent;

/// <summary>
/// Index of this encoding within it's parent encoding
/// </summary>
public int Index;

/// <summary>
/// Determine if this <see cref="ColourEncoding"/> is a repetition of another <see cref="ColourEncoding"/>. This
/// is a strict comparison and is true if and only if the colour sequence is exactly the same.
/// </summary>
public bool IsRepetitionOf(ColourEncoding other)
{
return HasIdenticalMonoLength(other) &&
other.Payload.Count == Payload.Count &&
(other.Payload[0].EncodedData[0].BaseObject as Hit)?.Type ==
(Payload[0].EncodedData[0].BaseObject as Hit)?.Type;
}

/// <summary>
/// Determine if this <see cref="ColourEncoding"/> has the same mono length of another <see cref="ColourEncoding"/>.
/// </summary>
public bool HasIdenticalMonoLength(ColourEncoding other)
{
return other.Payload[0].RunLength == Payload[0].RunLength;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// 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.

using System;
using System.Collections.Generic;

namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
{
/// <summary>
/// Encodes a list of <see cref="ColourEncoding"/>s, grouped together by back and forth repetition of the same
/// <see cref="ColourEncoding"/>. Also stores the repetition interval between this and the previous <see cref="CoupledColourEncoding"/>.
/// </summary>
public class CoupledColourEncoding
{
/// <summary>
/// Maximum amount of <see cref="CoupledColourEncoding"/>s to look back to find a repetition.
/// </summary>
private const int max_repetition_interval = 16;

/// <summary>
/// The <see cref="ColourEncoding"/>s that are grouped together within this <see cref="CoupledColourEncoding"/>.
/// </summary>
public readonly List<ColourEncoding> Payload = new List<ColourEncoding>();

/// <summary>
/// The previous <see cref="CoupledColourEncoding"/>. This is used to determine the repetition interval.
/// </summary>
public readonly CoupledColourEncoding? Previous;

/// <summary>
/// How many <see cref="CoupledColourEncoding"/> between the current and previous identical <see cref="CoupledColourEncoding"/>.
/// If no repetition is found this will have a value of <see cref="max_repetition_interval"/> + 1.
/// </summary>
public int RepetitionInterval { get; private set; } = max_repetition_interval + 1;

public CoupledColourEncoding(CoupledColourEncoding? previous)
{
Previous = previous;
}

/// <summary>
/// Returns true if other is considered a repetition of this encoding. This is true if other's first two payloads
/// have identical mono lengths.
/// </summary>
private bool isRepetitionOf(CoupledColourEncoding other)
{
if (Payload.Count != other.Payload.Count) return false;

for (int i = 0; i < Math.Min(Payload.Count, 2); i++)
{
if (!Payload[i].HasIdenticalMonoLength(other.Payload[i])) return false;
}

return true;
}

/// <summary>
/// Finds the closest previous <see cref="CoupledColourEncoding"/> that has the identical <see cref="Payload"/>.
/// Interval is defined as the amount of <see cref="CoupledColourEncoding"/> chunks between the current and repeated encoding.
/// </summary>
public void FindRepetitionInterval()
{
if (Previous?.Previous == null)
{
RepetitionInterval = max_repetition_interval + 1;
return;
}

CoupledColourEncoding? other = Previous.Previous;
int interval = 2;

while (interval < max_repetition_interval)
{
if (isRepetitionOf(other))
{
RepetitionInterval = Math.Min(interval, max_repetition_interval);
return;
}

other = other.Previous;
if (other == null) break;

++interval;
}

RepetitionInterval = max_repetition_interval + 1;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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.

using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Taiko.Objects;
using System.Collections.Generic;

namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
{
/// <summary>
/// Encode colour information for a sequence of <see cref="TaikoDifficultyHitObject"/>s. Consecutive <see cref="TaikoDifficultyHitObject"/>s
/// of the same <see cref="HitType"/> are encoded within the same <see cref="MonoEncoding"/>.
/// </summary>
public class MonoEncoding
{
/// <summary>
/// List of <see cref="DifficultyHitObject"/>s that are encoded within this <see cref="MonoEncoding"/>.
/// </summary>
public List<TaikoDifficultyHitObject> EncodedData { get; private set; } = new List<TaikoDifficultyHitObject>();

/// <summary>
/// The parent <see cref="ColourEncoding"/> that contains this <see cref="MonoEncoding"/>
/// </summary>
public ColourEncoding? Parent;

/// <summary>
/// Index of this encoding within it's parent encoding
/// </summary>
public int Index;

/// <summary>
/// How long the mono pattern encoded within is
/// </summary>
public int RunLength => EncodedData.Count;
}
}
Loading