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

Cognition difficulty calculation rework: Low AR part #28110

Open
wants to merge 168 commits into
base: pp-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
168 commits
Select commit Hold shift + click to select a range
b95d69b
initial
apollo-dw Jun 21, 2022
ee8f207
Make changes which I forgot lol
apollo-dw Jun 21, 2022
ec543c1
Clarify that variable names currently suck
apollo-dw Jun 21, 2022
d8f1866
Wave of changes
apollo-dw Jun 21, 2022
d83f1c4
Adjust note-density and hidden buffs
apollo-dw Jun 24, 2022
2527ad1
Merge branch 'master' into cognition
apollo-dw Jul 19, 2022
06b1425
WIP
apollo-dw Jul 19, 2022
8991a73
Merge branch 'ppy:master' into cognition
apollo-dw Aug 9, 2022
422c24b
Merge branch 'cognition' of https://github.com/apollo-dw/osu into cog…
apollo-dw Aug 9, 2022
c09e5ae
Merge branch 'ppy:master' into cognition
apollo-dw Aug 9, 2022
f7720e7
Merge branch 'cognition' of https://github.com/apollo-dw/osu into cog…
apollo-dw Aug 9, 2022
3c38c9e
Fix null objects
apollo-dw Aug 9, 2022
e6193e8
Use past objects for note density
apollo-dw Aug 10, 2022
152a195
Add HD support
apollo-dw Aug 10, 2022
2d5f2fc
Buff stream-spacing objects slightly, reduce velocity factor in HD
apollo-dw Aug 10, 2022
ca55fec
Merge branch 'master' into cognition
apollo-dw Aug 11, 2022
f1f1e10
Change low spacing influence, add time separation factor, and moar hi…
apollo-dw Aug 25, 2022
35c7201
Various high AR tweaks
apollo-dw Aug 27, 2022
b0d47e4
Allow hidden difficulty and high AR difficulty to co-exist.
apollo-dw Aug 27, 2022
97f3f28
Time factor, improve spacing and rhythm buffs
apollo-dw Aug 28, 2022
f1e14b3
Add constant rhythm nerf, reduce repeat angle nerf, buff spacing
apollo-dw Aug 31, 2022
c690ecf
Merge branch 'master' into cognition
apollo-dw Sep 7, 2022
9797493
Nerf linear, readjust hidden parameters
apollo-dw Sep 13, 2022
8e614e2
Don't nerf object influence if stream
apollo-dw Oct 5, 2022
cd0b2b2
Change some AR11 calc
apollo-dw Oct 7, 2022
08fa391
Merge branch 'ppymaster' into cognition
apollo-dw Oct 7, 2022
f42004f
Remove outdated argument
apollo-dw Oct 7, 2022
b6d4fbf
Add constant angle nerf to note density difficulty
apollo-dw Oct 10, 2022
d50e67f
Remove WIP ar11 stuff from Cognition
apollo-dw Oct 17, 2022
441bea5
Make angle nerf requirement narrower
apollo-dw Oct 17, 2022
090b408
Apply various changes
apollo-dw Oct 18, 2022
82cbdcc
Rename cognition to reading
apollo-dw Oct 18, 2022
a7070b5
Improve code quality
apollo-dw Oct 19, 2022
a9e9e50
Capitalize member
apollo-dw Oct 19, 2022
4a535fe
Revert high AR changes
apollo-dw Oct 24, 2022
ed07dbe
Merge branch 'ppy:master' into cognition
apollo-dw Nov 9, 2022
8937080
Merge branch 'ppy:master' into cognition
apollo-dw Jul 29, 2023
c171274
Balance streams and HD aim
apollo-dw Jul 30, 2023
07df5d9
Further balance values
apollo-dw Aug 9, 2023
ef30961
Merge branch 'ppy:master' into cognition
Givikap120 Jan 5, 2024
ffeb46a
Initial overlap calc
Givikap120 Jan 8, 2024
c64430f
Improved overlap calc
Givikap120 Jan 16, 2024
8c2405d
Big bump
Givikap120 Jan 20, 2024
f429f21
Fixed low AR
Givikap120 Jan 23, 2024
77ce1ae
Slight balancing
Givikap120 Jan 23, 2024
f6df247
Added infra and hidden
Givikap120 Jan 24, 2024
cede416
Added full-memory cap
Givikap120 Jan 26, 2024
394af04
Added inpredictability calculation
Givikap120 Jan 26, 2024
58159a5
Update Reading.cs
Givikap120 Jan 26, 2024
8ba3f2e
Changed reading cap
Givikap120 Jan 27, 2024
7c294c8
Merge branch 'ppy:master' into cognition
Givikap120 Jan 27, 2024
51eb5c0
Alternating angle nerf
Givikap120 Jan 28, 2024
ba265ac
Update LegacyScoreDecoder.cs
Givikap120 Jan 28, 2024
1a68e29
Big amount of changes
Givikap120 Feb 2, 2024
639f877
Minor SR adjust for high AR bonus
Givikap120 Feb 4, 2024
e6f1a40
Change scaling to make high AR woth more on low SR
Givikap120 Feb 12, 2024
83d391e
Merge branch 'master' into cognition
Givikap120 Feb 13, 2024
9e6ae35
Changes to highAR and angle nerf
Givikap120 Feb 23, 2024
5d4c782
added density aim multiplier
Givikap120 Feb 23, 2024
c8e9602
Update ReadingEvaluator.cs
Givikap120 Feb 28, 2024
c2e5d76
Fixed reading cap
Givikap120 Mar 7, 2024
4b5d463
Update Reading.cs
Givikap120 Mar 7, 2024
a8b6ae9
high AR speed nerf
Givikap120 Mar 8, 2024
d3cdb67
low AR streams balancing
Givikap120 Mar 16, 2024
5e35121
Merge branch 'ppy:master' into cognition
Givikap120 Mar 16, 2024
5e2f3e3
Increased stability
Givikap120 Mar 17, 2024
ab47d39
Balancing
Givikap120 Mar 21, 2024
333bfd2
added acc bonus for low AR
Givikap120 Mar 21, 2024
cead94d
minor balancing update
Givikap120 Mar 23, 2024
53b918e
deleted more db-stuff
Givikap120 Mar 23, 2024
529bd84
more clean-up
Givikap120 Mar 23, 2024
4d2cb57
fixed very stupid bug
Givikap120 Mar 23, 2024
6b1320e
increased stability of the overlaps
Givikap120 Mar 23, 2024
c016ed0
Update OsuDifficultyHitObject.cs
Givikap120 Mar 23, 2024
403dc5b
Walk This Way bandaid
Givikap120 Mar 23, 2024
4051413
rubik's cube bandaid
Givikap120 Mar 24, 2024
98873e2
fixed high AR (i hope)
Givikap120 Mar 24, 2024
b4fadc3
change SR scalig
Givikap120 Mar 24, 2024
d96eeeb
high AR changes
Givikap120 Mar 25, 2024
6ec5bb5
Fixed the overlap bug
Givikap120 Mar 26, 2024
c4af2bb
optimisation
Givikap120 Mar 26, 2024
71df659
bandaid for Rainbow Dash +EZ
Givikap120 Mar 26, 2024
23808be
new experimental overlap summing
Givikap120 Mar 30, 2024
2dbdd4f
balancing
Givikap120 Apr 4, 2024
ae4f0a1
Many changes
Givikap120 Apr 7, 2024
801843b
Update ReadingEvaluator.cs
Givikap120 Apr 7, 2024
a2e4cb8
Minor fixes
Givikap120 Apr 7, 2024
af92559
fixed the bug
Givikap120 Apr 8, 2024
faf18e1
heavy optimisations
Givikap120 Apr 8, 2024
33d1e2f
bit of high AR balancing
Givikap120 Apr 8, 2024
8d080cc
Angle penalty change
Givikap120 Apr 14, 2024
9a7c21b
high AR clean-up and balancing
Givikap120 Apr 14, 2024
84b7bad
minor changes
Givikap120 Apr 15, 2024
6a4bd64
better reading cap curve
Givikap120 Apr 16, 2024
463a6ce
many HD-related changes
Givikap120 Apr 18, 2024
0d6a4c5
balancing HD
Givikap120 Apr 22, 2024
81f1136
added old HD bonus to merge with master
Givikap120 Apr 25, 2024
7750b43
Update OsuPerformanceCalculator.cs
Givikap120 Apr 25, 2024
d4f8340
Merge branch 'ppy:master' into cognition
Givikap120 Apr 25, 2024
3888bd4
removed old HD bonus
Givikap120 Apr 25, 2024
856b631
Merge branch 'cognition' of https://github.com/Givikap120/osu into co…
Givikap120 Apr 25, 2024
07cb701
Balancing
Givikap120 Apr 28, 2024
3d9584d
more balancing
Givikap120 Apr 28, 2024
e4d8ed9
optimizations
Givikap120 May 2, 2024
86f7b7d
optimization and code quality
Givikap120 May 2, 2024
fd4beb5
lil bit clean-up
Givikap120 May 5, 2024
d50df4e
Update OsuPerformanceCalculator.cs
Givikap120 May 5, 2024
325e18d
cutting only low AR
Givikap120 May 5, 2024
de91242
added TD and RX support
Givikap120 May 5, 2024
f06bdef
removed AngleSigned
Givikap120 May 5, 2024
bb25b54
Update OsuDifficultyAttributes.cs
Givikap120 May 5, 2024
e58eccc
refactoring and high AR rebalance
Givikap120 May 18, 2024
0374bfe
Merge branch 'ppy:master' into cognition
Givikap120 Jul 13, 2024
0af7315
rebalanced high AR
Givikap120 Jul 13, 2024
74f24b0
better high AR curve for <1 star maps
Givikap120 Aug 29, 2024
30fe7a8
more convenient FL vs reading separation
Givikap120 Aug 29, 2024
e4b50e0
fixed NaN FL pp
Givikap120 Aug 29, 2024
3e32aa7
renamed cognition back to reading
Givikap120 Sep 3, 2024
4f12a3a
added sliders bandaid (overbuffed on purpose)
Givikap120 Sep 6, 2024
ae32853
slight multiplier increase
Givikap120 Sep 6, 2024
c3edf06
multiple fixes
Givikap120 Sep 7, 2024
657ec26
Merge branch 'master' into cognition
Givikap120 Sep 7, 2024
3069cdd
reverted universal HDFL attribute adding
Givikap120 Sep 10, 2024
7b72e1c
Update OsuDifficultyCalculator.cs
Givikap120 Sep 10, 2024
2df8763
fixed FL bonus being added always
Givikap120 Sep 10, 2024
ec90604
Merge branch 'master' into cognition
Givikap120 Sep 15, 2024
16456bc
Update Flashlight.cs
Givikap120 Sep 15, 2024
49e7686
fixed TD nerf being applied incorrectly
Givikap120 Sep 15, 2024
82cde30
added bandaid to restore some balancing
Givikap120 Sep 26, 2024
aefd4df
some updates
Givikap120 Oct 3, 2024
fde948c
more balancing
Givikap120 Oct 3, 2024
27a5fba
added passive aim multiplier for all density
Givikap120 Oct 3, 2024
2a9b0d6
changed scaling formula
Givikap120 Oct 3, 2024
5565b35
made curve slightly harsher and smoother
Givikap120 Oct 3, 2024
0d0beb0
fixed wrong combo calc
Givikap120 Oct 3, 2024
64e6c74
better and smoother curve
Givikap120 Oct 4, 2024
e348b17
some balancing stuff
Givikap120 Oct 4, 2024
7f3093e
Merge branch 'master' into cognition
Givikap120 Oct 7, 2024
e72ae6d
ported CSR correctly
Givikap120 Oct 7, 2024
bed67f1
fixed the indexing bug
Givikap120 Oct 7, 2024
7c5c9a0
fixed the lazeracc being added twice
Givikap120 Oct 8, 2024
f8e2874
Merge branch 'master' into cognition
Givikap120 Oct 17, 2024
eefaa0f
some refactorings
Givikap120 Oct 19, 2024
a0b2653
moved skill multiplier
Givikap120 Oct 19, 2024
cba409d
numerous simplifications
Givikap120 Oct 20, 2024
d70b7f4
Merge branch 'master' into cognition
Givikap120 Oct 31, 2024
94bbd1c
refactorings
Givikap120 Oct 31, 2024
5fb9fdd
Merge branch 'master' into cognition
Givikap120 Nov 14, 2024
7880b22
Update Skill.cs
Givikap120 Nov 14, 2024
d18812d
renamed to difficulty
Givikap120 Nov 14, 2024
0abb925
nerf flow to compensate reading buff
Givikap120 Nov 17, 2024
236fe85
refactor high ar
Givikap120 Nov 17, 2024
5850e6a
remove graphskill
Givikap120 Nov 17, 2024
d620094
Merge branch 'master' into cognition
Givikap120 Nov 17, 2024
63444c9
Update Skill.cs
Givikap120 Nov 17, 2024
cf5d62f
moved global multiplier from difficulty to performance
Givikap120 Nov 18, 2024
ac30542
refactor
Givikap120 Nov 18, 2024
3cae490
add visual adjust for skill pp
Givikap120 Nov 18, 2024
06fe094
Merge branch 'cognition' into cognition_low_ar_part
Givikap120 Dec 18, 2024
a329c1e
some cleaning and balancing
Givikap120 Dec 18, 2024
4eb63d4
more clean-up
Givikap120 Dec 18, 2024
5d193b2
Update StrainSkill.cs
Givikap120 Dec 18, 2024
c4a1f69
fix CI and more cleaning
Givikap120 Dec 18, 2024
3b86aff
Update OsuPerformanceCalculator.cs
Givikap120 Dec 18, 2024
afce62b
fix CI again
Givikap120 Dec 18, 2024
e13cc46
fix DI (again)
Givikap120 Dec 18, 2024
41a4689
cap sliderbonus for density aim
Givikap120 Jan 21, 2025
2089cc4
Merge branch 'pp-dev' into cognition_low_ar_part
Givikap120 Jan 21, 2025
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
224 changes: 224 additions & 0 deletions osu.Game.Rulesets.Osu/Difficulty/Evaluators/ReadingEvaluator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Utils;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Objects;

namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
{
// Main class with some util functions
public static class ReadingEvaluator
{
private const double reading_window_size = 3000;

private const double overlap_multiplier = 1;

private const double slider_body_length_multiplier = 1.3;

public static double EvaluateDensityOf(DifficultyHitObject current, bool applyDistanceNerf = true, bool applySliderbodyDensity = true, double angleNerfMultiplier = 1.0)
{
var currObj = (OsuDifficultyHitObject)current;

double density = 0;
double densityAnglesNerf = -2; // we have threshold of 2

// Despite being called prev, it's actually more late in time
OsuDifficultyHitObject prevObj0 = currObj;

var readingObjects = currObj.ReadingObjects;

for (int i = 0; i < readingObjects.Count; i++)
{
var loopObj = readingObjects[i].HitObject;

if (loopObj.Index < 1)
continue; // Don't look on the first object of the map

double loopDifficulty = currObj.OpacityAt(loopObj.BaseObject.StartTime, false);

// Small distances means objects may be cheesed, so it doesn't matter whether they are arranged confusingly
if (applyDistanceNerf) loopDifficulty *= (DifficultyCalculationUtils.Logistic(-(loopObj.LazyJumpDistance - 80) / 10) + 0.2) / 1.2;

// Additional buff for long sliderbodies
if (applySliderbodyDensity && loopObj.BaseObject is Slider slider)
{
// In radiuses, with minimal of 1
double sliderBodyLength = Math.Max(1, slider.Velocity * slider.SpanDuration / slider.Radius);

// Bandaid to fix abuze
sliderBodyLength = Math.Min(sliderBodyLength, 1 + slider.LazyTravelDistance / 8);

// The maximum is 3x buff
double sliderBodyBuff = Math.Log10(sliderBodyLength);

// Limit the max buff to prevent abuse with very long sliders.
// With explicit coverage of cases like one very long slider on the map, or just very few objects visible before/after.
double maxBuff = 0.5;
if (i > 0) maxBuff += 1;
if (i < readingObjects.Count - 1) maxBuff += 1;

loopDifficulty *= 1 + slider_body_length_multiplier * Math.Min(sliderBodyBuff, maxBuff);
}

// Reduce density bonus for this object if they're too apart in time
// Nerf starts on 1500ms and reaches maximum (*=0) on 3000ms
double timeBetweenCurrAndLoopObj = currObj.StartTime - loopObj.StartTime;
loopDifficulty *= getTimeNerfFactor(timeBetweenCurrAndLoopObj);

// Only if next object is slower, representing break from many notes in a row
if (loopObj.StrainTime > prevObj0.StrainTime)
{
// Get rhythm similarity: 1 on same rhythms, 0.5 on 1/4 to 1/2
double rhythmSimilarity = DifficultyCalculationUtils.GetRatio(loopObj.StrainTime, prevObj0.StrainTime);

// Make differentiation going from 1/4 to 1/2 and bigger difference
// To 1/3 to 1/2 and smaller difference
rhythmSimilarity = Math.Clamp(rhythmSimilarity, 0.5, 0.75);
rhythmSimilarity = 4 * (rhythmSimilarity - 0.5);

// Reduce density for this objects if rhythms are different
loopDifficulty *= rhythmSimilarity;
}

density += loopDifficulty;

// Angles nerf
// Why it's /2 + 0.5?
// Because there was a bug initially that made angle predictability to be from 0.5 to 1
// And removing this bug caused balance to be destroyed
double angleNerf = (loopObj.AnglePredictability / 2) + 0.5;

densityAnglesNerf += angleNerf * loopDifficulty * angleNerfMultiplier;

prevObj0 = loopObj;
}

// Apply angles nerf
density -= Math.Max(0, densityAnglesNerf);
return density;
}

public static double EvaluateOverlapDifficultyOf(DifficultyHitObject current)
{
var currObj = (OsuDifficultyHitObject)current;
double screenOverlapDifficulty = 0;

if (currObj.ReadingObjects.Count == 0)
return 0;

var overlapDifficulties = new List<(OsuDifficultyHitObject HitObject, double Difficulty)>();
var readingObjects = currObj.ReadingObjects;

// Find initial overlap values
for (int i = 0; i < readingObjects.Count; i++)
{
var loopObj = readingObjects[i].HitObject;
var loopReadingObjects = (List<OsuDifficultyHitObject.ReadingObject>)loopObj.ReadingObjects;

if (loopReadingObjects.Count == 0)
continue;

double targetStartTime = currObj.StartTime - currObj.Preempt;
double overlapness = boundBinarySearch(loopReadingObjects, targetStartTime);

if (overlapness > 0) overlapDifficulties.Add((loopObj, overlapness));
}

if (overlapDifficulties.Count == 0)
return 0;

var sortedDifficulties = overlapDifficulties.OrderByDescending(d => d.Difficulty).ToList();

// Nerf overlap values of easier notes that are in the same place as hard notes
for (int i = 0; i < sortedDifficulties.Count; i++)
{
var harderObject = sortedDifficulties[i];

// Look for all easier objects
for (int j = i + 1; j < sortedDifficulties.Count; j++)
{
var easierObject = sortedDifficulties[j];

// Get the overlap value
double overlapValue;

// OverlapValues dict only contains prev objects, so be sure to use right object
if (harderObject.HitObject.Index > easierObject.HitObject.Index)
harderObject.HitObject.OverlapValues.TryGetValue(easierObject.HitObject.Index, out overlapValue);
else
easierObject.HitObject.OverlapValues.TryGetValue(harderObject.HitObject.Index, out overlapValue);

// Nerf easier object if it overlaps in the same place as hard one
easierObject.Difficulty *= Math.Pow(1 - overlapValue, 2);
}
}

const double decay_weight = 0.5;
const double threshold = 0.6;
double weight = 1.0;

// Sum the overlap values to get difficulty
foreach (var diffObject in sortedDifficulties.Where(d => d.Difficulty > threshold).OrderByDescending(d => d.Difficulty))
{
// Add weighted difficulty
screenOverlapDifficulty += Math.Max(0, diffObject.Difficulty - threshold) * weight;
weight *= decay_weight;
}

return overlap_multiplier * Math.Max(0, screenOverlapDifficulty);
}

public static double EvaluateDifficultyOf(DifficultyHitObject current)
{
if (current.BaseObject is Spinner || current.Index == 0)
return 0;

double difficulty = Math.Pow(4 * Math.Log(Math.Max(1, EvaluateDensityOf(current, true, true))), 2.5);

double overlapBonus = EvaluateOverlapDifficultyOf(current) * difficulty;
difficulty += overlapBonus;

return difficulty;
}

public static double EvaluateAimingDensityFactorOf(DifficultyHitObject current)
{
double difficulty = EvaluateDensityOf(current, true, false, 0.5);

return Math.Max(0, Math.Pow(difficulty, 1.37) - 1);
}

// This factor nerfs AR below 0 as extra safety measure
private static double getTimeNerfFactor(double deltaTime) => Math.Clamp(2 - deltaTime / (reading_window_size / 2), 0, 1);

// Finds the overlapness of the last object for which StartTime lower than target
private static double boundBinarySearch(List<OsuDifficultyHitObject.ReadingObject> arr, double target)
{
int low = 0;
int high = arr.Count;

int result = -1;

while (low < high)
{
int mid = low + (high - low) / 2;

if (arr[mid].HitObject.StartTime >= target)
{
result = mid;
low = mid + 1;
}
else high = mid - 1;
}

if (result == -1) return 0;

return arr[result].Overlapness;
}
}
}
14 changes: 11 additions & 3 deletions osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@
[JsonProperty("speed_note_count")]
public double SpeedNoteCount { get; set; }

/// <summary>
/// The difficulty corresponding to the reading skill. Low AR branch.
/// </summary>
[JsonProperty("reading_low_ar_difficulty")]
public double ReadingDifficultyLowAr { get; set; }

/// <summary>
/// The difficulty corresponding to the flashlight skill.
/// </summary>
Expand All @@ -59,6 +65,9 @@
[JsonProperty("speed_difficult_strain_count")]
public double SpeedDifficultStrainCount { get; set; }

[JsonProperty("low_ar_difficult_strain_count")]
public double LowArDifficultStrainCount { get; set; }

/// <summary>
/// The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
/// </summary>
Expand Down Expand Up @@ -119,10 +128,9 @@
yield return (ATTRIB_ID_OVERALL_DIFFICULTY, OverallDifficulty);
yield return (ATTRIB_ID_APPROACH_RATE, ApproachRate);
yield return (ATTRIB_ID_DIFFICULTY, StarRating);

Check failure on line 131 in osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0055)

Check failure on line 131 in osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0055)
yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow);

if (ShouldSerializeFlashlightDifficulty())
yield return (ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty);
yield return (ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty);

yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor);

Expand Down
53 changes: 33 additions & 20 deletions osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
public class OsuDifficultyCalculator : DifficultyCalculator
{
private const double difficulty_multiplier = 0.0675;

public const double SUM_POWER = 1.1;
public const double FL_SUM_POWER = 1.5;
public override int Version => 20241007;

public OsuDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
Expand All @@ -44,26 +45,31 @@
var aimWithoutSliders = skills.OfType<Aim>().Single(a => !a.IncludeSliders);
double aimRatingNoSliders = Math.Sqrt(aimWithoutSliders.DifficultyValue()) * difficulty_multiplier;
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;

Check failure on line 48 in osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0055)

Check failure on line 48 in osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0055)
var speed = skills.OfType<Speed>().Single();
double speedRating = Math.Sqrt(speed.DifficultyValue()) * difficulty_multiplier;
double speedNotes = speed.RelevantNoteCount();
double speedDifficultyStrainCount = speed.CountTopWeightedStrains();

var flashlight = skills.OfType<Flashlight>().SingleOrDefault();
double flashlightRating = flashlight == null ? 0.0 : Math.Sqrt(flashlight.DifficultyValue()) * difficulty_multiplier;
var readingLowAr = skills.OfType<ReadingLowAr>().Single();
double readingLowArRating = Math.Sqrt(readingLowAr.DifficultyValue()) * difficulty_multiplier;
double lowArDifficultyStrainCount = readingLowAr.CountTopWeightedStrains();

double flashlightRating = Math.Sqrt(skills.OfType<Flashlight>().Single().DifficultyValue()) * difficulty_multiplier;

if (mods.Any(m => m is OsuModTouchDevice))
{
aimRating = Math.Pow(aimRating, 0.8);
flashlightRating = Math.Pow(flashlightRating, 0.8);
readingLowArRating = Math.Pow(readingLowArRating, 0.9);
}

if (mods.Any(h => h is OsuModRelax))
{
aimRating *= 0.9;
speedRating = 0.0;
flashlightRating *= 0.7;
readingLowArRating *= 0.95;
}
else if (mods.Any(h => h is OsuModAutopilot))
{
Expand All @@ -72,19 +78,25 @@
flashlightRating *= 0.4;
}

double baseAimPerformance = OsuStrainSkill.DifficultyToPerformance(aimRating);
double baseSpeedPerformance = OsuStrainSkill.DifficultyToPerformance(speedRating);
double baseFlashlightPerformance = 0.0;
double aimPerformance = OsuStrainSkill.DifficultyToPerformance(aimRating);
double speedPerformance = OsuStrainSkill.DifficultyToPerformance(speedRating);

// Cognition
double readingLowArPerformance = ReadingLowAr.DifficultyToPerformance(readingLowArRating);
double readingArPerformance = readingLowArPerformance;

if (mods.Any(h => h is OsuModFlashlight))
baseFlashlightPerformance = Flashlight.DifficultyToPerformance(flashlightRating);
double potentialFlashlightPerformance = Flashlight.DifficultyToPerformance(flashlightRating);
double flashlightPerformance = mods.Any(h => h is OsuModFlashlight) ? potentialFlashlightPerformance : 0;

double basePerformance =
Math.Pow(
Math.Pow(baseAimPerformance, 1.1) +
Math.Pow(baseSpeedPerformance, 1.1) +
Math.Pow(baseFlashlightPerformance, 1.1), 1.0 / 1.1
);
double flashlightArPerformance = Math.Pow(Math.Pow(flashlightPerformance, FL_SUM_POWER) + Math.Pow(readingArPerformance, FL_SUM_POWER), 1.0 / FL_SUM_POWER);

double cognitionPerformance = flashlightArPerformance;
double mechanicalPerformance = Math.Pow(Math.Pow(aimPerformance, SUM_POWER) + Math.Pow(speedPerformance, SUM_POWER), 1.0 / SUM_POWER);

// Limit cognition by full memorisation difficulty, what is assumed to be mechanicalPerformance + flashlightPerformance
cognitionPerformance = OsuPerformanceCalculator.AdjustCognitionPerformance(cognitionPerformance, mechanicalPerformance, potentialFlashlightPerformance);

double basePerformance = mechanicalPerformance + cognitionPerformance;

double starRating = basePerformance > 0.00001
? Math.Cbrt(OsuPerformanceCalculator.PERFORMANCE_BASE_MULTIPLIER) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4)
Expand Down Expand Up @@ -112,11 +124,13 @@
AimDifficultSliderCount = difficultSliders,
SpeedDifficulty = speedRating,
SpeedNoteCount = speedNotes,
ReadingDifficultyLowAr = readingLowArRating,
FlashlightDifficulty = flashlightRating,
SliderFactor = sliderFactor,
AimDifficultStrainCount = aimDifficultyStrainCount,
SpeedDifficultStrainCount = speedDifficultyStrainCount,
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
LowArDifficultStrainCount = lowArDifficultyStrainCount,
ApproachRate = IBeatmapDifficultyInfo.InverseDifficultyRange(preempt, 1800, 1200, 450),
OverallDifficulty = (80 - hitWindowGreat) / 6,
GreatHitWindow = hitWindowGreat,
OkHitWindow = hitWindowOk,
Expand Down Expand Up @@ -152,12 +166,11 @@
{
new Aim(mods, true),
new Aim(mods, false),
new Speed(mods)
new Speed(mods),
new Flashlight(mods),
new ReadingLowAr(mods),
};

if (mods.Any(h => h is OsuModFlashlight))
skills.Add(new Flashlight(mods));

return skills.ToArray();
}

Expand Down
6 changes: 3 additions & 3 deletions osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ public class OsuPerformanceAttributes : PerformanceAttributes
[JsonProperty("accuracy")]
public double Accuracy { get; set; }

[JsonProperty("flashlight")]
public double Flashlight { get; set; }
[JsonProperty("cognition")]
public double Cognition { get; set; }

[JsonProperty("effective_miss_count")]
public double EffectiveMissCount { get; set; }
Expand All @@ -35,7 +35,7 @@ public override IEnumerable<PerformanceDisplayAttribute> GetAttributesForDisplay
yield return new PerformanceDisplayAttribute(nameof(Aim), "Aim", Aim);
yield return new PerformanceDisplayAttribute(nameof(Speed), "Speed", Speed);
yield return new PerformanceDisplayAttribute(nameof(Accuracy), "Accuracy", Accuracy);
yield return new PerformanceDisplayAttribute(nameof(Flashlight), "Flashlight Bonus", Flashlight);
yield return new PerformanceDisplayAttribute(nameof(Cognition), "Cognition", Cognition);
}
}
}
Loading
Loading