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

Refactor skills - move logic out of inheritance hierarchy #15010

Closed
wants to merge 10 commits into from
8 changes: 2 additions & 6 deletions osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ public class Movement : StrainDecaySkill
private const double direction_change_bonus = 21.0;

protected override double SkillMultiplier => 900;
protected override double StrainDecayBase => 0.2;

protected override double DecayWeight => 0.94;

protected override int SectionLength => 750;
protected override double DifficultySumWeight => 0.94;

protected readonly float HalfCatcherWidth;

Expand All @@ -34,7 +30,7 @@ public class Movement : StrainDecaySkill
private readonly double catcherSpeedMultiplier;

public Movement(Mod[] mods, float halfCatcherWidth, double clockRate)
: base(mods)
: base(mods, strainDecayBase: 0.2, sectionLength: 750)
{
HalfCatcherWidth = halfCatcherWidth;

Expand Down
15 changes: 6 additions & 9 deletions osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@

namespace osu.Game.Rulesets.Mania.Difficulty.Skills
{
public class Strain : StrainDecaySkill
public class Strain : StrainSkill
{
private const double individual_decay_base = 0.125;
private const double overall_decay_base = 0.30;

protected override double SkillMultiplier => 1;
protected override double StrainDecayBase => 1;

private readonly double[] holdEndTimes;
private readonly double[] individualStrains;

Expand All @@ -32,7 +29,7 @@ public Strain(Mod[] mods, int totalColumns)
overallStrain = 1;
}

protected override double StrainValueOf(DifficultyHitObject current)
protected override double StrainValueAt(DifficultyHitObject current)
{
var maniaCurrent = (ManiaDifficultyHitObject)current;
var endTime = maniaCurrent.EndTime;
Expand Down Expand Up @@ -68,12 +65,12 @@ protected override double StrainValueOf(DifficultyHitObject current)

overallStrain = applyDecay(overallStrain, current.DeltaTime, overall_decay_base) + (1 + holdAddition) * holdFactor;

return individualStrain + overallStrain - CurrentStrain;
return individualStrain + overallStrain;
joseph-ireland marked this conversation as resolved.
Show resolved Hide resolved
}

protected override double CalculateInitialStrain(double offset)
=> applyDecay(individualStrain, offset - Previous[0].StartTime, individual_decay_base)
+ applyDecay(overallStrain, offset - Previous[0].StartTime, overall_decay_base);
protected override double StrainAtTime(double time)
=> applyDecay(individualStrain, time - Previous[0].StartTime, individual_decay_base)
+ applyDecay(overallStrain, time - Previous[0].StartTime, overall_decay_base);

private double applyDecay(double value, double deltaTime, double decayBase)
=> value * Math.Pow(decayBase, deltaTime / 1000);
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ private double strainValueOf(DifficultyHitObject current)

private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000);

protected override double CalculateInitialStrain(double time) => currentStrain * strainDecay(time - Previous[0].StartTime);
protected override double StrainAtTime(double time) => currentStrain * strainDecay(time - Previous[0].StartTime);

protected override double StrainValueAt(DifficultyHitObject current)
{
Expand Down
4 changes: 2 additions & 2 deletions osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public Flashlight(Mod[] mods)

private double skillMultiplier => 0.15;
private double strainDecayBase => 0.15;
protected override double DecayWeight => 1.0;
protected override double DifficultySumWeight => 1.0;
protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations.
private double currentStrain = 1;

Expand Down Expand Up @@ -66,7 +66,7 @@ private double strainValueOf(DifficultyHitObject current)

private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000);

protected override double CalculateInitialStrain(double time) => currentStrain * strainDecay(time - Previous[0].StartTime);
protected override double StrainAtTime(double time) => currentStrain * strainDecay(time - Previous[0].StartTime);

protected override double StrainValueAt(DifficultyHitObject current)
{
Expand Down
18 changes: 4 additions & 14 deletions osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Difficulty.Utils;
using osu.Game.Rulesets.Mods;
using System.Linq;
using osu.Framework.Utils;
Expand All @@ -28,16 +29,13 @@ public abstract class OsuStrainSkill : StrainSkill
/// </summary>
protected virtual double DifficultyMultiplier => 1.06;

protected OsuStrainSkill(Mod[] mods)
: base(mods)
protected OsuStrainSkill(Mod[] mods, int sectionLength = 400)
: base(mods, sectionLength)
{
}

public override double DifficultyValue()
{
double difficulty = 0;
double weight = 1;

List<double> strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList();

// We are reducing the highest strains first to account for extreme difficulty spikes
Expand All @@ -47,15 +45,7 @@ public override double DifficultyValue()
strains[i] *= Interpolation.Lerp(ReducedStrainBaseline, 1.0, scale);
}

// Difficulty is the weighted sum of the highest strains from every section.
// We're sorting from highest to lowest strain.
foreach (double strain in strains.OrderByDescending(d => d))
{
difficulty += strain * weight;
weight *= DecayWeight;
}

return difficulty * DifficultyMultiplier;
return strains.SortedExponentialWeightedSum(DifficultySumWeight) * DifficultyMultiplier;
}
}
}
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ private double strainValueOf(DifficultyHitObject current)

private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000);

protected override double CalculateInitialStrain(double time) => (currentStrain * currentRhythm) * strainDecay(time - Previous[0].StartTime);
protected override double StrainAtTime(double time) => (currentStrain * currentRhythm) * strainDecay(time - Previous[0].StartTime);

protected override double StrainValueAt(DifficultyHitObject current)
{
Expand Down
3 changes: 1 addition & 2 deletions osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
public class Colour : StrainDecaySkill
{
protected override double SkillMultiplier => 1;
protected override double StrainDecayBase => 0.4;

/// <summary>
/// Maximum number of entries to keep in <see cref="monoHistory"/>.
Expand All @@ -41,7 +40,7 @@ public class Colour : StrainDecaySkill
private int currentMonoLength;

public Colour(Mod[] mods)
: base(mods)
: base(mods, strainDecayBase: 0.4)
{
}

Expand Down
14 changes: 6 additions & 8 deletions osu.Game.Rulesets.Taiko/Difficulty/Skills/Rhythm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
/// <summary>
/// Calculates the rhythm coefficient of taiko difficulty.
/// </summary>
public class Rhythm : StrainDecaySkill
public class Rhythm : StrainSkill
{
protected override double SkillMultiplier => 10;
protected override double StrainDecayBase => 0;
private const double skill_multiplier = 10;

/// <summary>
/// The note-based decay for rhythm strain.
/// </summary>
/// <remarks>
/// <see cref="StrainDecayBase"/> is not used here, as it's time- and not note-based.
/// </remarks>
private const double strain_decay = 0.96;

/// <summary>
Expand Down Expand Up @@ -53,7 +49,9 @@ public Rhythm(Mod[] mods)
{
}

protected override double StrainValueOf(DifficultyHitObject current)
protected override double StrainAtTime(double time) => 0;

protected override double StrainValueAt(DifficultyHitObject current)
{
// drum rolls and swells are exempt.
if (!(current.BaseObject is Hit))
Expand Down Expand Up @@ -83,7 +81,7 @@ protected override double StrainValueOf(DifficultyHitObject current)
notesSinceRhythmChange = 0;

currentStrain += objectStrain;
return currentStrain;
return skill_multiplier * currentStrain;
}

/// <summary>
Expand Down
3 changes: 1 addition & 2 deletions osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
public class Stamina : StrainDecaySkill
{
protected override double SkillMultiplier => 1;
protected override double StrainDecayBase => 0.4;

/// <summary>
/// Maximum number of entries to keep in <see cref="notePairDurationHistory"/>.
Expand Down Expand Up @@ -52,7 +51,7 @@ public class Stamina : StrainDecaySkill
/// <param name="mods">Mods for use in skill calculations.</param>
/// <param name="rightHand">Whether this instance is performing calculations for the right hand.</param>
public Stamina(Mod[] mods, bool rightHand)
: base(mods)
: base(mods, strainDecayBase: 0.4)
{
hand = rightHand ? 1 : 0;
}
Expand Down
2 changes: 1 addition & 1 deletion osu.Game/Rulesets/Difficulty/Skills/Skill.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
namespace osu.Game.Rulesets.Difficulty.Skills
{
/// <summary>
/// A bare minimal abstract skill for fully custom skill implementations.
/// Process the <see cref="DifficultyHitObject"/> in a map and produce a difficulty value.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsure if this is a better description than the previous one. They're both pretty barebones.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thought was that the previous docs pretty much say public abstract class Skill in words. I can revert or even just delete the doc

/// </summary>
public abstract class Skill
{
Expand Down
46 changes: 22 additions & 24 deletions osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs
Original file line number Diff line number Diff line change
@@ -1,54 +1,52 @@
// 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;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Utils;
using osu.Game.Rulesets.Mods;

namespace osu.Game.Rulesets.Difficulty.Skills
{
/// <summary>
/// Used to processes strain values of <see cref="DifficultyHitObject"/>s, keep track of strain levels caused by the processed objects
/// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
/// Convenience base class for a <see cref="Skill"/> making use of <see cref="DecayingStrainPeaks"/>
/// </summary>
public abstract class StrainDecaySkill : StrainSkill
public abstract class StrainDecaySkill : Skill
{
/// <summary>
/// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
/// The weight for the exponential sum of strains which produces the final difficulty value
/// </summary>
protected abstract double SkillMultiplier { get; }
protected virtual double DifficultySumWeight => 0.9;

/// <summary>
/// Determines how quickly strain decays for the given skill.
/// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second.
/// Scales the value of <see cref="StrainValueOf(DifficultyHitObject)"/> to produce a final strain.
/// </summary>
protected abstract double StrainDecayBase { get; }
protected abstract double SkillMultiplier { get; }

/// <summary>
/// The current strain level.
/// </summary>
protected double CurrentStrain { get; private set; }
protected readonly DecayingStrainPeaks Strain;

protected StrainDecaySkill(Mod[] mods)
protected StrainDecaySkill(Mod[] mods, double strainDecayBase, int sectionLength = 400)
: base(mods)
{
Strain = new DecayingStrainPeaks(strainDecayBase, sectionLength);
}

protected override double CalculateInitialStrain(double time) => CurrentStrain * strainDecay(time - Previous[0].StartTime);

protected override double StrainValueAt(DifficultyHitObject current)
protected sealed override void Process(DifficultyHitObject hitObject)
{
CurrentStrain *= strainDecay(current.DeltaTime);
CurrentStrain += StrainValueOf(current) * SkillMultiplier;

return CurrentStrain;
Strain.IncrementStrainAtTime(hitObject.StartTime, SkillMultiplier * StrainValueOf(hitObject));
}

/// <summary>
/// Calculates the strain value of a <see cref="DifficultyHitObject"/>. This value is affected by previously processed objects.
/// Returns a strain increment representing the difficulty of the <see cref="DifficultyHitObject"/>.
/// This will be scaled by <see cref="SkillMultiplier"/> and added onto the current strain.
/// </summary>
protected abstract double StrainValueOf(DifficultyHitObject current);
protected abstract double StrainValueOf(DifficultyHitObject hitObject);

public override double DifficultyValue()
{
return Strain.StrainPeaks.SortedExponentialWeightedSum(DifficultySumWeight);
}

private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000);
public IEnumerable<double> GetCurrentStrainPeaks() => Strain.StrainPeaks;
}
}
Loading