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

Remove combo scaling from Aim and Speed from osu! performance calculation #16280

Open
wants to merge 43 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
cff9dab
Remove combo scaling and change miss penalty
apollo-dw Dec 14, 2021
86ad42a
Nerf length bonus
apollo-dw Dec 14, 2021
489aa43
Make miss penalty harsher
apollo-dw Dec 14, 2021
bac4cfe
Use frost's miss count penalty
apollo-dw Dec 16, 2021
60e2a8e
Use MathNet for miss penalty calculation, and use old penalty formula…
apollo-dw Dec 21, 2021
5640918
New miss penalty formula, using relevant difficult notes in each skil…
apollo-dw Dec 26, 2021
e9589e5
Fix logical error
apollo-dw Dec 27, 2021
d514567
Merge master
apollo-dw Dec 29, 2021
8ce6e3c
Remove mathnet
apollo-dw Dec 29, 2021
4f257d6
Clean up unsuccessful merge
apollo-dw Dec 29, 2021
fd1028f
Use clockrate in the difficult strain count method
apollo-dw Dec 29, 2021
d2b815b
Add miss penalty comment
apollo-dw Jan 2, 2022
75be4e8
Remove abusable 0.66 threshold by averaging
Luminiscental Jan 3, 2022
1320790
Remove unnecessary truncation
Luminiscental Jan 4, 2022
391110c
Remove abusable 0.66 threshold by averaging
apollo-dw Jan 4, 2022
443640a
Revert "Remove abusable 0.66 threshold by averaging"
apollo-dw Jan 4, 2022
f07bfcd
Merge pull request #6 from apollo-dw/revert-5-no-combo-scaling
apollo-dw Jan 4, 2022
dcb9693
Weight difficult strain count against the top strain
apollo-dw Jan 4, 2022
b3e90c3
Merge
apollo-dw Jan 4, 2022
400abc1
Add attribute ids to mapping functions
smoogipoo Jan 6, 2022
5989467
Reword comment and rename argument
apollo-dw Jan 12, 2022
1ae8ff0
Merge branch 'master' into no-combo-scaling
apollo-dw Feb 10, 2022
da31ca1
Use note strains instead of sectional strains
apollo-dw Feb 14, 2022
94a46ab
Rescale miss penalty for note strains
apollo-dw Feb 14, 2022
c18df86
Remove clockrate factor
apollo-dw Feb 19, 2022
580e43b
Merge branch 'master' into no-combo-scaling
apollo-dw Mar 3, 2022
0d4fe96
Merge branch 'master' into no-combo-scaling
apollo-dw Mar 17, 2022
2f335a7
Switch to using osuAttributes
apollo-dw Mar 17, 2022
23d0c03
Merge branch 'master' into no-combo-scaling
apollo-dw Oct 24, 2022
7d34542
use difficulty instead of topstrain
TextAdventurer12 Feb 22, 2024
0db910d
cap each note at adding 1 difficult strain count
TextAdventurer12 Feb 22, 2024
9f5f6b5
stop capping difficult strains per note
TextAdventurer12 Apr 6, 2024
b32d73e
adjust weighting function
TextAdventurer12 Apr 12, 2024
e2a5d19
adjust count difficult strains formula
TextAdventurer12 Apr 16, 2024
1d19bd2
Merge pull request #7 from TextAdventurer12/no-combo-scaling
apollo-dw May 11, 2024
9b60abe
Merge branch 'ppy:master' into no-combo-scaling
apollo-dw May 11, 2024
c1efcc0
Change miss penalty (nerf longer maps)
TextAdventurer12 May 21, 2024
9ff277c
Merge pull request #9 from TextAdventurer12/no-combo-scaling
apollo-dw May 21, 2024
20c54ab
Apply code quality changes
apollo-dw May 23, 2024
61afda1
Fix NaN case when difficulty is 0
apollo-dw May 26, 2024
c25e1bd
Use correct operation for 0 difficulty case
apollo-dw May 26, 2024
a7e1d35
Update osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs
apollo-dw Sep 11, 2024
f54a5a5
Merge branch 'master' into no-combo-scaling
tsunyoku Oct 3, 2024
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
12 changes: 11 additions & 1 deletion osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ public class OsuDifficultyAttributes : DifficultyAttributes
[JsonProperty("slider_factor")]
public double SliderFactor { get; set; }

[JsonProperty("aim_difficult_strain_count")]
public double AimDifficultStrainCount { get; set; }

[JsonProperty("speed_difficult_strain_count")]
public double SpeedDifficultStrainCount { get; set; }

/// <summary>
/// The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
/// </summary>
Expand Down Expand Up @@ -99,6 +105,9 @@ public class OsuDifficultyAttributes : DifficultyAttributes
yield return (ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty);

yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor);

yield return (ATTRIB_ID_AIM_DIFFICULT_STRAIN_COUNT, AimDifficultStrainCount);
yield return (ATTRIB_ID_SPEED_DIFFICULT_STRAIN_COUNT, SpeedDifficultStrainCount);
yield return (ATTRIB_ID_SPEED_NOTE_COUNT, SpeedNoteCount);
}

Expand All @@ -113,8 +122,9 @@ public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> val
StarRating = values[ATTRIB_ID_DIFFICULTY];
FlashlightDifficulty = values.GetValueOrDefault(ATTRIB_ID_FLASHLIGHT);
SliderFactor = values[ATTRIB_ID_SLIDER_FACTOR];
AimDifficultStrainCount = values[ATTRIB_ID_AIM_DIFFICULT_STRAIN_COUNT];
SpeedDifficultStrainCount = values[ATTRIB_ID_SPEED_DIFFICULT_STRAIN_COUNT];
SpeedNoteCount = values[ATTRIB_ID_SPEED_NOTE_COUNT];

DrainRate = onlineInfo.DrainRate;
HitCircleCount = onlineInfo.CircleCount;
SliderCount = onlineInfo.SliderCount;
Expand Down
5 changes: 5 additions & 0 deletions osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat

double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;

double aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountDifficultStrains();
double speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountDifficultStrains();
Comment on lines +51 to +52
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

At some point I'd really like to rid of the indexing of skills because it makes me anxious but future problem..


if (mods.Any(m => m is OsuModTouchDevice))
{
aimRating = Math.Pow(aimRating, 0.8);
Expand Down Expand Up @@ -101,6 +104,8 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat
SpeedNoteCount = speedNotes,
FlashlightDifficulty = flashlightRating,
SliderFactor = sliderFactor,
AimDifficultStrainCount = aimDifficultyStrainCount,
SpeedDifficultStrainCount = speedDifficultyStrainCount,
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
OverallDifficulty = (80 - hitWindowGreat) / 6,
DrainRate = drainRate,
Expand Down
14 changes: 6 additions & 8 deletions osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,8 @@ private double computeAimValue(ScoreInfo score, OsuDifficultyAttributes attribut
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
aimValue *= lengthBonus;

// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
if (effectiveMissCount > 0)
aimValue *= 0.97 * Math.Pow(1 - Math.Pow(effectiveMissCount / totalHits, 0.775), effectiveMissCount);

aimValue *= getComboScalingFactor(attributes);
aimValue *= calculateMissPenalty(effectiveMissCount, attributes.AimDifficultStrainCount);

double approachRateFactor = 0.0;
if (attributes.ApproachRate > 10.33)
Expand Down Expand Up @@ -145,11 +142,8 @@ private double computeSpeedValue(ScoreInfo score, OsuDifficultyAttributes attrib
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
speedValue *= lengthBonus;

// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
if (effectiveMissCount > 0)
speedValue *= 0.97 * Math.Pow(1 - Math.Pow(effectiveMissCount / totalHits, 0.775), Math.Pow(effectiveMissCount, .875));

speedValue *= getComboScalingFactor(attributes);
speedValue *= calculateMissPenalty(effectiveMissCount, attributes.SpeedDifficultStrainCount);

double approachRateFactor = 0.0;
if (attributes.ApproachRate > 10.33)
Expand Down Expand Up @@ -264,6 +258,10 @@ private double calculateEffectiveMissCount(OsuDifficultyAttributes attributes)
return Math.Max(countMiss, comboBasedMissCount);
}

// Miss penalty assumes that a player will miss on the hardest parts of a map,
// so we use the amount of relatively difficult sections to adjust miss penalty
// to make it more punishing on maps with lower amount of hard sections.
private double calculateMissPenalty(double missCount, double difficultStrainCount) => 0.96 / ((missCount / (4 * Math.Pow(Math.Log(difficultStrainCount), 0.94))) + 1);
private double getComboScalingFactor(OsuDifficultyAttributes attributes) => attributes.MaxCombo <= 0 ? 1.0 : Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(attributes.MaxCombo, 0.8), 1.0);
private int totalHits => countGreat + countOk + countMeh + countMiss;
}
Expand Down
1 change: 1 addition & 0 deletions osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ protected override double StrainValueAt(DifficultyHitObject current)
{
currentStrain *= strainDecay(current.DeltaTime);
currentStrain += AimEvaluator.EvaluateDifficultyOf(current, withSliders) * skillMultiplier;
ObjectStrains.Add(currentStrain);

return currentStrain;
}
Expand Down
23 changes: 20 additions & 3 deletions osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,17 @@ public abstract class OsuStrainSkill : StrainSkill
/// </summary>
protected virtual double DifficultyMultiplier => DEFAULT_DIFFICULTY_MULTIPLIER;

protected List<double> ObjectStrains = new List<double>();
protected double Difficulty;

protected OsuStrainSkill(Mod[] mods)
: base(mods)
{
}

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

// Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871).
Expand All @@ -61,11 +64,25 @@ public override double DifficultyValue()
// We're sorting from highest to lowest strain.
foreach (double strain in strains.OrderDescending())
{
difficulty += strain * weight;
Difficulty += strain * weight;
weight *= DecayWeight;
}

return difficulty * DifficultyMultiplier;
return Difficulty * DifficultyMultiplier;
}

/// <summary>
/// Returns the number of strains weighted against the top strain.
/// The result is scaled by clock rate as it affects the total number of strains.
/// </summary>
public double CountDifficultStrains()
{
if (Difficulty == 0)
return 0.0;

double consistentTopStrain = Difficulty / 10; // What would the top strain be if all strain values were identical
// Use a weighted sum of all strains. Constants are arbitrary and give nice values
return ObjectStrains.Sum(s => 1.1 / (1 + Math.Exp(-10 * (s / consistentTopStrain - 0.88))));
}
}
}
13 changes: 4 additions & 9 deletions osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Difficulty.Evaluators;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using System.Collections.Generic;
using System.Linq;

namespace osu.Game.Rulesets.Osu.Difficulty.Skills
Expand All @@ -25,8 +24,6 @@ public class Speed : OsuStrainSkill
protected override int ReducedSectionCount => 5;
protected override double DifficultyMultiplier => 1.04;

private readonly List<double> objectStrains = new List<double>();

public Speed(Mod[] mods)
: base(mods)
{
Expand All @@ -44,23 +41,21 @@ protected override double StrainValueAt(DifficultyHitObject current)
currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current);

double totalStrain = currentStrain * currentRhythm;

objectStrains.Add(totalStrain);
ObjectStrains.Add(totalStrain);

return totalStrain;
}

public double RelevantNoteCount()
{
if (objectStrains.Count == 0)
if (ObjectStrains.Count == 0)
return 0;

double maxStrain = objectStrains.Max();

double maxStrain = ObjectStrains.Max();
if (maxStrain == 0)
return 0;

return objectStrains.Sum(strain => 1.0 / (1.0 + Math.Exp(-(strain / maxStrain * 12.0 - 6.0))));
return ObjectStrains.Sum(strain => 1.0 / (1.0 + Math.Exp(-(strain / maxStrain * 12.0 - 6.0))));
}
}
}
2 changes: 2 additions & 0 deletions osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public class DifficultyAttributes
protected const int ATTRIB_ID_SCORE_MULTIPLIER = 15;
protected const int ATTRIB_ID_FLASHLIGHT = 17;
protected const int ATTRIB_ID_SLIDER_FACTOR = 19;
protected const int ATTRIB_ID_AIM_DIFFICULT_STRAIN_COUNT = 21;
Copy link
Contributor

Choose a reason for hiding this comment

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

is this really intended to be duplicate of ATTRIB_ID_SPEED_NOTE_COUNT ?

Copy link
Collaborator

@bdach bdach Jun 5, 2024

Choose a reason for hiding this comment

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

This is a good (and very scary) catch. I can see how this could have happened, the speed note count thing was added in #15035 which precedes the open date of this PR.

That said I'm not sure I would trust any sheet results at this point given this revelation because I'm not sure what this means for correct calculation. Probably needs a full diffcalc re-run and re-verification of results after fixing.

Copy link
Sponsor Member

@tsunyoku tsunyoku Jun 5, 2024

Choose a reason for hiding this comment

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

Oh dear.. luckily no sheets were generated for the latest changes. osu-tools isn't using these attribute IDs and the huismetbenen website that is used by users also doesn't use these so I'm not concerned about the values - but obviously, this needs to be fixed before anything which does use these can be relied upon.

protected const int ATTRIB_ID_SPEED_DIFFICULT_STRAIN_COUNT = 23;
protected const int ATTRIB_ID_SPEED_NOTE_COUNT = 21;
apollo-dw marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
Expand Down
Loading