From b62d0403aad045d3bc8f8374a49f534daee0ccf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Oct 2024 09:43:33 +0200 Subject: [PATCH 1/4] Fix incorrect grade cutoffs when displaying lazer scores and stable scores in lazer mode As reported on discord: https://discord.com/channels/188630481301012481/1097318920991559880/1293089915394985985 a --- resources/js/scores-show/dial.tsx | 39 +------------------ resources/js/scores-show/info.tsx | 4 +- resources/js/utils/score-helper.ts | 61 ++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 39 deletions(-) diff --git a/resources/js/scores-show/dial.tsx b/resources/js/scores-show/dial.tsx index d99dd709578..a03f06a7618 100644 --- a/resources/js/scores-show/dial.tsx +++ b/resources/js/scores-show/dial.tsx @@ -10,6 +10,7 @@ interface Props { accuracy: number; mode: Ruleset; rank: Rank; + rank_cutoffs: number[]; } const displayRank: Record = { @@ -24,46 +25,10 @@ const displayRank: Record = { XH: 'SS', }; -const refDataMap: Record = { - // : minimum acc => (higher rank acc - current acc) - // for SS, use minimum accuracy of 0.99 (any less and it's too small) - // actual array is reversed as it's rendered from D to SS clockwise - - // SS: 0.99 => 0.01 - // S: 0.9801 => 0.0099 - // A: 0.9401 => 0.04 - // B: 0.9001 => 0.04 - // C: 0.8501 => 0.05 - // D: 0 => 0.8501 - fruits: [0.8501, 0.05, 0.04, 0.04, 0.0099, 0.01], - // SS: 0.99 => 0.01 - // S: 0.95 => 0.04 - // A: 0.9 => 0.05 - // B: 0.8 => 0.1 - // C: 0.7 => 0.1 - // D: 0 => 0.7 - mania: [0.7, 0.1, 0.1, 0.05, 0.04, 0.01], - // SS: 0.99 => 0.01 - // S: (0.9 * 300 + 0.1 * 100) / 300 = 0.933 => 0.057 - // A: (0.8 * 300 + 0.2 * 100) / 300 = 0.867 => 0.066 - // B: (0.7 * 300 + 0.3 * 100) / 300 = 0.8 => 0.067 - // C: 0.6 => 0.2 - // D: 0 => 0.6 - osu: [0.6, 0.2, 0.067, 0.066, 0.057, 0.01], - // SS: 0.99 => 0.01 - // S: (0.9 * 300 + 0.1 * 50) / 300 = 0.917 => 0.073 - // A: (0.8 * 300 + 0.2 * 50) / 300 = 0.833 => 0.084 - // B: (0.7 * 300 + 0.3 * 50) / 300 = 0.75 => 0.083 - // C: 0.6 => 0.15 - // D: 0 => 0.6 - taiko: [0.6, 0.15, 0.083, 0.084, 0.073, 0.01], -}; - export default function Dial(props: Props) { const arc = d3.arc(); const pie = d3.pie().sortValues(null); const valueData = [props.accuracy, 1 - props.accuracy]; - const refData = refDataMap[props.mode]; return (
@@ -76,7 +41,7 @@ export default function Dial(props: Props) { - {pie(refData).map((d) => ( + {pie(props.rank_cutoffs).map((d) => (
- +
diff --git a/resources/js/utils/score-helper.ts b/resources/js/utils/score-helper.ts index 7cb82f4ba32..750236a9308 100644 --- a/resources/js/utils/score-helper.ts +++ b/resources/js/utils/score-helper.ts @@ -105,6 +105,67 @@ export function rank(score: SoloScoreJson) { : score.rank; } +export function rankCutoffs(score: SoloScoreJson): number[] { + // : minimum acc => (higher rank acc - current acc) + // for SS, use minimum accuracy of 0.99 (any less and it's too small) + // actual array is reversed as it's rendered from D to SS clockwise + + if (shouldReturnLegacyValue(score)) { + return { + // SS: 0.99 => 0.01 + // S: 0.9801 => 0.0099 + // A: 0.9401 => 0.04 + // B: 0.9001 => 0.04 + // C: 0.8501 => 0.05 + // D: 0 => 0.8501 + fruits: [0.8501, 0.05, 0.04, 0.04, 0.0099, 0.01], + // SS: 0.99 => 0.01 + // S: 0.95 => 0.04 + // A: 0.9 => 0.05 + // B: 0.8 => 0.1 + // C: 0.7 => 0.1 + // D: 0 => 0.7 + mania: [0.7, 0.1, 0.1, 0.05, 0.04, 0.01], + // SS: 0.99 => 0.01 + // S: (0.9 * 300 + 0.1 * 100) / 300 = 0.933 => 0.057 + // A: (0.8 * 300 + 0.2 * 100) / 300 = 0.867 => 0.066 + // B: (0.7 * 300 + 0.3 * 100) / 300 = 0.8 => 0.067 + // C: 0.6 => 0.2 + // D: 0 => 0.6 + osu: [0.6, 0.2, 0.067, 0.066, 0.057, 0.01], + // SS: 0.99 => 0.01 + // S: (0.9 * 300 + 0.1 * 50) / 300 = 0.917 => 0.073 + // A: (0.8 * 300 + 0.2 * 50) / 300 = 0.833 => 0.084 + // B: (0.7 * 300 + 0.3 * 50) / 300 = 0.75 => 0.083 + // C: 0.6 => 0.15 + // D: 0 => 0.6 + taiko: [0.6, 0.15, 0.083, 0.084, 0.073, 0.01], + }[rulesetName(score.ruleset_id)]; + } + + return { + // SS: 0.99 => 0.01 + // S: 0.98 => 0.01 + // A: 0.94 => 0.04 + // B: 0.9 => 0.04 + // C: 0.85 => 0.05 + // D: 0 => 0.85 + // cross-reference: https://github.com/ppy/osu/blob/b658d9a681a04101900d5ce6c5b84d56320e08e7/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs#L108-L135 + fruits: [0.85, 0.05, 0.04, 0.04, 0.01, 0.01], + // remaining rulesets use the same cutoffs + // SS: 0.99 => 0.01 + // S: 0.95 => 0.04 + // A: 0.9 => 0.05 + // B: 0.8 => 0.1 + // C: 0.7 => 0.1 + // D: 0 => 0.7 + // cross-reference: https://github.com/ppy/osu/blob/b658d9a681a04101900d5ce6c5b84d56320e08e7/osu.Game/Rulesets/Scoring/ScoreProcessor.cs#L541-L572 + mania: [0.7, 0.1, 0.1, 0.05, 0.04, 0.01], + osu: [0.7, 0.1, 0.1, 0.05, 0.04, 0.01], + taiko: [0.7, 0.1, 0.1, 0.05, 0.04, 0.01], + }[rulesetName(score.ruleset_id)]; +} + export function scoreDownloadUrl(score: SoloScoreJson) { if (score.type === 'solo_score') { return route('scores.download', { score: score.id }); From ea78b9a98f84adb068c24bbfa5996ccfa79c8959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Oct 2024 09:44:22 +0200 Subject: [PATCH 2/4] Match grade colours with client --- resources/css/colors.less | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/resources/css/colors.less b/resources/css/colors.less index 957b4a686cc..fd9cc072ad8 100644 --- a/resources/css/colors.less +++ b/resources/css/colors.less @@ -203,9 +203,10 @@ body { @group-colour-default: #0087ca; -@colour-rank-d: #ff5858; -@colour-rank-c: #ea7948; -@colour-rank-b: #d99d03; -@colour-rank-a: #72c904; -@colour-rank-s: #0096a2; -@colour-rank-ss: #be0089; +// cross-reference: https://github.com/ppy/osu/blob/b658d9a681a04101900d5ce6c5b84d56320e08e7/osu.Game/Graphics/OsuColour.cs#L42-L73 +@colour-rank-d: #ff5a5a; +@colour-rank-c: #ff8e5d; +@colour-rank-b: #e3b130; +@colour-rank-a: #88da20; +@colour-rank-s: #02b5c3; +@colour-rank-ss: #de31ae; From 251c493497fb6b80b6d486dd4cc90965b2ea089b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 11 Oct 2024 09:13:37 +0200 Subject: [PATCH 3/4] Clean up props --- resources/js/scores-show/dial.tsx | 6 ++---- resources/js/scores-show/info.tsx | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/resources/js/scores-show/dial.tsx b/resources/js/scores-show/dial.tsx index a03f06a7618..fa37e6dcbb1 100644 --- a/resources/js/scores-show/dial.tsx +++ b/resources/js/scores-show/dial.tsx @@ -3,14 +3,12 @@ import * as d3 from 'd3'; import Rank from 'interfaces/rank'; -import Ruleset from 'interfaces/ruleset'; import * as React from 'react'; interface Props { accuracy: number; - mode: Ruleset; rank: Rank; - rank_cutoffs: number[]; + rankCutoffs: number[]; } const displayRank: Record = { @@ -41,7 +39,7 @@ export default function Dial(props: Props) { - {pie(props.rank_cutoffs).map((d) => ( + {pie(props.rankCutoffs).map((d) => (
- +
From a86ce4138c86fa0a834860f180321a0587703228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 11 Oct 2024 09:28:23 +0200 Subject: [PATCH 4/4] Refactor helper methods for grade circles --- resources/js/utils/score-helper.ts | 103 ++++++++++++++--------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/resources/js/utils/score-helper.ts b/resources/js/utils/score-helper.ts index 750236a9308..b4ea926961f 100644 --- a/resources/js/utils/score-helper.ts +++ b/resources/js/utils/score-helper.ts @@ -106,64 +106,63 @@ export function rank(score: SoloScoreJson) { } export function rankCutoffs(score: SoloScoreJson): number[] { - // : minimum acc => (higher rank acc - current acc) // for SS, use minimum accuracy of 0.99 (any less and it's too small) // actual array is reversed as it's rendered from D to SS clockwise + let absoluteCutoffs: number[] = []; + const ruleset = rulesetName(score.ruleset_id); + if (shouldReturnLegacyValue(score)) { - return { - // SS: 0.99 => 0.01 - // S: 0.9801 => 0.0099 - // A: 0.9401 => 0.04 - // B: 0.9001 => 0.04 - // C: 0.8501 => 0.05 - // D: 0 => 0.8501 - fruits: [0.8501, 0.05, 0.04, 0.04, 0.0099, 0.01], - // SS: 0.99 => 0.01 - // S: 0.95 => 0.04 - // A: 0.9 => 0.05 - // B: 0.8 => 0.1 - // C: 0.7 => 0.1 - // D: 0 => 0.7 - mania: [0.7, 0.1, 0.1, 0.05, 0.04, 0.01], - // SS: 0.99 => 0.01 - // S: (0.9 * 300 + 0.1 * 100) / 300 = 0.933 => 0.057 - // A: (0.8 * 300 + 0.2 * 100) / 300 = 0.867 => 0.066 - // B: (0.7 * 300 + 0.3 * 100) / 300 = 0.8 => 0.067 - // C: 0.6 => 0.2 - // D: 0 => 0.6 - osu: [0.6, 0.2, 0.067, 0.066, 0.057, 0.01], - // SS: 0.99 => 0.01 - // S: (0.9 * 300 + 0.1 * 50) / 300 = 0.917 => 0.073 - // A: (0.8 * 300 + 0.2 * 50) / 300 = 0.833 => 0.084 - // B: (0.7 * 300 + 0.3 * 50) / 300 = 0.75 => 0.083 - // C: 0.6 => 0.15 - // D: 0 => 0.6 - taiko: [0.6, 0.15, 0.083, 0.084, 0.073, 0.01], - }[rulesetName(score.ruleset_id)]; + switch (ruleset) { + case 'fruits': + absoluteCutoffs = [0, 0.8501, 0.9001, 0.9401, 0.9801, 0.99, 1]; + break; + + case 'mania': + absoluteCutoffs = [0, 0.7, 0.8, 0.9, 0.95, 0.99, 1]; + break; + + case 'osu': + // S: (0.9 * 300 + 0.1 * 100) / 300 = 0.933 + // A: (0.8 * 300 + 0.2 * 100) / 300 = 0.867 + // B: (0.7 * 300 + 0.3 * 100) / 300 = 0.8 + absoluteCutoffs = [0, 0.6, 0.8, 0.867, 0.933, 0.99, 1]; + break; + + case 'taiko': + // S: (0.9 * 300 + 0.1 * 50) / 300 = 0.917 + // A: (0.8 * 300 + 0.2 * 50) / 300 = 0.833 + // B: (0.7 * 300 + 0.3 * 50) / 300 = 0.75 + absoluteCutoffs = [0, 0.6, 0.75, 0.833, 0.917, 0.99, 1]; + break; + } + } else { + switch (ruleset) { + case 'fruits': + // cross-reference: https://github.com/ppy/osu/blob/b658d9a681a04101900d5ce6c5b84d56320e08e7/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs#L108-L135 + absoluteCutoffs = [0, 0.85, 0.9, 0.94, 0.98, 0.99, 1]; + break; + + case 'mania': + case 'osu': + case 'taiko': + // cross-reference: https://github.com/ppy/osu/blob/b658d9a681a04101900d5ce6c5b84d56320e08e7/osu.Game/Rulesets/Scoring/ScoreProcessor.cs#L541-L572 + absoluteCutoffs = [0, 0.7, 0.8, 0.9, 0.95, 0.99, 1]; + break; + } + } + + return differenceBetweenConsecutiveElements(absoluteCutoffs); +} + +function differenceBetweenConsecutiveElements(arr: number[]): number[] { + const result = []; + + for (let i = 1; i < arr.length; ++i) { + result.push(arr[i] - arr[i - 1]); } - return { - // SS: 0.99 => 0.01 - // S: 0.98 => 0.01 - // A: 0.94 => 0.04 - // B: 0.9 => 0.04 - // C: 0.85 => 0.05 - // D: 0 => 0.85 - // cross-reference: https://github.com/ppy/osu/blob/b658d9a681a04101900d5ce6c5b84d56320e08e7/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs#L108-L135 - fruits: [0.85, 0.05, 0.04, 0.04, 0.01, 0.01], - // remaining rulesets use the same cutoffs - // SS: 0.99 => 0.01 - // S: 0.95 => 0.04 - // A: 0.9 => 0.05 - // B: 0.8 => 0.1 - // C: 0.7 => 0.1 - // D: 0 => 0.7 - // cross-reference: https://github.com/ppy/osu/blob/b658d9a681a04101900d5ce6c5b84d56320e08e7/osu.Game/Rulesets/Scoring/ScoreProcessor.cs#L541-L572 - mania: [0.7, 0.1, 0.1, 0.05, 0.04, 0.01], - osu: [0.7, 0.1, 0.1, 0.05, 0.04, 0.01], - taiko: [0.7, 0.1, 0.1, 0.05, 0.04, 0.01], - }[rulesetName(score.ruleset_id)]; + return result; } export function scoreDownloadUrl(score: SoloScoreJson) {