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

Use total deviation to scale accuracy on aim, general aim buff #31498

Merged
merged 8 commits into from
Jan 14, 2025
12 changes: 6 additions & 6 deletions osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,21 @@ public class OsuDifficultyCalculatorTest : DifficultyCalculatorTest
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu.Tests";

[TestCase(6.6860329680488437d, 239, "diffcalc-test")]
[TestCase(1.4485740324170036d, 54, "zero-length-sliders")]
[TestCase(6.7443067697205539d, 239, "diffcalc-test")]
[TestCase(1.4630292101418947d, 54, "zero-length-sliders")]
[TestCase(0.43052813047866129d, 4, "very-fast-slider")]
[TestCase(0.14143808967817237d, 2, "nan-slider")]
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
=> base.Test(expectedStarRating, expectedMaxCombo, name);

[TestCase(9.6300773538770041d, 239, "diffcalc-test")]
[TestCase(1.7550155729445993d, 54, "zero-length-sliders")]
[TestCase(9.7058844423552308d, 239, "diffcalc-test")]
[TestCase(1.7724929629205366d, 54, "zero-length-sliders")]
[TestCase(0.55785578988249407d, 4, "very-fast-slider")]
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime());

[TestCase(6.6860329680488437d, 239, "diffcalc-test")]
[TestCase(1.4485740324170036d, 54, "zero-length-sliders")]
[TestCase(6.7443067697205539d, 239, "diffcalc-test")]
[TestCase(1.4630292101418947d, 54, "zero-length-sliders")]
[TestCase(0.43052813047866129d, 4, "very-fast-slider")]
public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name)
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with

// Penalize angle repetition.
wideAngleBonus *= 1 - Math.Min(wideAngleBonus, Math.Pow(calcWideAngleBonus(lastAngle), 3));
acuteAngleBonus *= 0.1 + 0.9 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastAngle), 3)));
acuteAngleBonus *= 0.09 + 0.91 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastAngle), 3)));

// Apply full wide angle bonus for distance more than one diameter
wideAngleBonus *= angleBonus * DifficultyCalculationUtils.Smootherstep(osuCurrObj.LazyJumpDistance, 0, diameter);
Expand Down
3 changes: 3 additions & 0 deletions osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public class OsuPerformanceAttributes : PerformanceAttributes
[JsonProperty("effective_miss_count")]
public double EffectiveMissCount { get; set; }

[JsonProperty("total_deviation")]
public double? TotalDeviation { get; set; }

[JsonProperty("speed_deviation")]
public double? SpeedDeviation { get; set; }

Expand Down
57 changes: 52 additions & 5 deletions osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Difficulty.Utils;
using osu.Game.Rulesets.Osu.Difficulty.Skills;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
Expand Down Expand Up @@ -41,6 +42,7 @@ public class OsuPerformanceCalculator : PerformanceCalculator
/// </summary>
private double effectiveMissCount;

private double? totalDeviation;
private double? speedDeviation;

public OsuPerformanceCalculator()
Expand Down Expand Up @@ -113,6 +115,7 @@ protected override PerformanceAttributes CreatePerformanceAttributes(ScoreInfo s
effectiveMissCount = Math.Min(effectiveMissCount + countOk * okMultiplier + countMeh * mehMultiplier, totalHits);
}

totalDeviation = calculateTotalDeviation(osuAttributes);
speedDeviation = calculateSpeedDeviation(osuAttributes);

double aimValue = computeAimValue(score, osuAttributes);
Expand All @@ -135,6 +138,7 @@ protected override PerformanceAttributes CreatePerformanceAttributes(ScoreInfo s
Accuracy = accuracyValue,
Flashlight = flashlightValue,
EffectiveMissCount = effectiveMissCount,
TotalDeviation = totalDeviation,
SpeedDeviation = speedDeviation,
Total = totalValue
};
Expand All @@ -145,6 +149,9 @@ private double computeAimValue(ScoreInfo score, OsuDifficultyAttributes attribut
if (score.Mods.Any(h => h is OsuModAutopilot))
return 0.0;

if (totalDeviation == null)
return 0;

double aimDifficulty = attributes.AimDifficulty;

if (attributes.SliderCount > 0 && attributes.AimDifficultSliderCount > 0)
Expand Down Expand Up @@ -196,9 +203,7 @@ private double computeAimValue(ScoreInfo score, OsuDifficultyAttributes attribut
aimValue *= 1.0 + 0.04 * (12.0 - attributes.ApproachRate);
}

aimValue *= accuracy;
// It is important to consider accuracy difficulty when scaling with accuracy.
aimValue *= 0.98 + Math.Pow(Math.Max(0, attributes.OverallDifficulty), 2) / 2500;
aimValue *= SpecialFunctions.Erf(25.0 / (Math.Sqrt(2) * totalDeviation.Value));

return aimValue;
}
Expand Down Expand Up @@ -317,6 +322,48 @@ private double computeFlashlightValue(ScoreInfo score, OsuDifficultyAttributes a
return flashlightValue;
}

/// <summary>
/// Using <see cref="calculateDeviation"/> estimates player's deviation on accuracy objects.
/// Returns deviation for circles and sliders if score was set with slideracc.
/// Returns the min between deviation of circles and deviation on circles and sliders (assuming slider hits are 50s), if score was set without slideracc.
/// </summary>
private double? calculateTotalDeviation(OsuDifficultyAttributes attributes)
{
if (totalSuccessfulHits == 0)
return null;

int accuracyObjectCount = attributes.HitCircleCount;

if (!usingClassicSliderAccuracy)
accuracyObjectCount += attributes.SliderCount;

// Assume worst case: all mistakes was on accuracy objects
int relevantCountMiss = Math.Min(countMiss, accuracyObjectCount);
int relevantCountMeh = Math.Min(countMeh, accuracyObjectCount - relevantCountMiss);
int relevantCountOk = Math.Min(countOk, accuracyObjectCount - relevantCountMiss - relevantCountMeh);
int relevantCountGreat = Math.Max(0, accuracyObjectCount - relevantCountMiss - relevantCountMeh - relevantCountOk);

// Calculate deviation on accuracy objects
double? deviation = calculateDeviation(attributes, relevantCountGreat, relevantCountOk, relevantCountMeh, relevantCountMiss);
if (deviation == null)
return null;

if (!usingClassicSliderAccuracy)
return deviation.Value;

// If score was set without slider accuracy - also compute deviation with sliders
// Assume that all hits was 50s
int totalCountWithSliders = attributes.HitCircleCount + attributes.SliderCount;
int missCountWithSliders = Math.Min(totalCountWithSliders, countMiss);
int hitCountWithSliders = totalCountWithSliders - missCountWithSliders;

double hitProbabilityWithSliders = hitCountWithSliders / (totalCountWithSliders + 1.0);
double deviationWithSliders = attributes.MehHitWindow / (Math.Sqrt(2) * SpecialFunctions.ErfInv(hitProbabilityWithSliders));

// Min is needed for edgecase maps with 1 circle and 999 sliders, as deviation on sliders can be lower in this case
return Math.Min(deviation.Value, deviationWithSliders);
}

/// <summary>
/// Estimates player's deviation on speed notes using <see cref="calculateDeviation"/>, assuming worst-case.
/// Treats all speed notes as hit circles.
Expand Down Expand Up @@ -412,8 +459,8 @@ private double calculateSpeedHighDeviationNerf(OsuDifficultyAttributes attribute
const double scale = 50;
double adjustedSpeedValue = scale * (Math.Log((speedValue - excessSpeedDifficultyCutoff) / scale + 1) + excessSpeedDifficultyCutoff / scale);

// 200 UR and less are considered tapped correctly to ensure that normal scores will be punished as little as possible
double lerp = 1 - Math.Clamp((speedDeviation.Value - 20) / (24 - 20), 0, 1);
// 220 UR and less are considered tapped correctly to ensure that normal scores will be punished as little as possible
double lerp = 1 - DifficultyCalculationUtils.ReverseLerp(speedDeviation.Value, 22.0, 27.0);
adjustedSpeedValue = double.Lerp(adjustedSpeedValue, speedValue, lerp);

return adjustedSpeedValue / speedValue;
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 @@ -26,7 +26,7 @@ public Aim(Mod[] mods, bool withSliders)

private double currentStrain;

private double skillMultiplier => 25.18;
private double skillMultiplier => 25.7;
private double strainDecayBase => 0.15;

private readonly List<double> sliderStrains = new List<double>();
Expand Down
Loading