From 6ace6bfee17fbf0ffcb4d8f3c88a23d22acf2332 Mon Sep 17 00:00:00 2001 From: Walavouchey <36758269+Walavouchey@users.noreply.github.com> Date: Tue, 7 Feb 2023 11:16:36 +0100 Subject: [PATCH 01/10] ensure `AccuracyCircle` doesn't land in gaps created by `RankNotch`es --- .../Visual/Ranking/TestSceneAccuracyCircle.cs | 9 +++++ .../Expanded/Accuracy/AccuracyCircle.cs | 34 ++++++++++++++++++- .../Ranking/Expanded/Accuracy/RankNotch.cs | 2 +- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs index 0145a1dfef..314e275ca3 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs @@ -24,11 +24,20 @@ namespace osu.Game.Tests.Visual.Ranking { public partial class TestSceneAccuracyCircle : OsuTestScene { + [TestCase(0, ScoreRank.D)] [TestCase(0.2, ScoreRank.D)] [TestCase(0.5, ScoreRank.D)] + [TestCase(0.699, ScoreRank.D)] + [TestCase(0.7, ScoreRank.C)] [TestCase(0.75, ScoreRank.C)] + [TestCase(0.799, ScoreRank.C)] + [TestCase(0.80, ScoreRank.B)] [TestCase(0.85, ScoreRank.B)] + [TestCase(0.899, ScoreRank.B)] + [TestCase(0.9, ScoreRank.A)] [TestCase(0.925, ScoreRank.A)] + [TestCase(0.949, ScoreRank.A)] + [TestCase(0.95, ScoreRank.S)] [TestCase(0.975, ScoreRank.S)] [TestCase(0.9999, ScoreRank.S)] [TestCase(1, ScoreRank.X)] diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index 8e04bb68fb..3cb09734c5 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -73,6 +73,11 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy /// private const double virtual_ss_percentage = 0.01; + /// + /// The width of a in terms of accuracy. + /// + public const double NOTCH_WIDTH_PERCENTAGE = 0.002; + /// /// The easing for the circle filling transforms. /// @@ -263,7 +268,34 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy using (BeginDelayedSequence(ACCURACY_TRANSFORM_DELAY)) { - double targetAccuracy = score.Rank == ScoreRank.X || score.Rank == ScoreRank.XH ? 1 : Math.Min(1 - virtual_ss_percentage, score.Accuracy); + double targetAccuracy = score.Accuracy; + + // Ensure the gauge overshoots or undershoots a bit so it doesn't land in the gaps of the inner graded circle (caused by `RankNotch`es), + // to prevent ambiguity on what grade it's pointing at. + double[] notchPercentages = { 0.7, 0.8, 0.9, 0.95 }; + + foreach (double p in notchPercentages) + { + if (targetAccuracy > p - NOTCH_WIDTH_PERCENTAGE / 2 && targetAccuracy < p + NOTCH_WIDTH_PERCENTAGE / 2) + { + int tippingDirection = targetAccuracy - p >= 0 ? 1 : -1; // We "round up" here to match rank criteria + targetAccuracy = p + tippingDirection * (NOTCH_WIDTH_PERCENTAGE / 2); + break; + } + } + + // The final gap between 99.999...% (S) and 100% (SS) is exaggerated by `virtual_ss_percentage`. We don't want to land there either. + if (score.Rank == ScoreRank.X || score.Rank == ScoreRank.XH) + targetAccuracy = 1; + else + targetAccuracy = Math.Min(1 - virtual_ss_percentage - NOTCH_WIDTH_PERCENTAGE / 2, targetAccuracy); + + // The accuracy circle gauge visually fills up a bit too much. + // This wouldn't normally matter but we want it to align properly with the inner graded circle in the above cases. + const double visual_alignment_offset = 0.001; + + if (targetAccuracy < 1 && targetAccuracy >= visual_alignment_offset) + targetAccuracy -= visual_alignment_offset; accuracyCircle.FillTo(targetAccuracy, ACCURACY_TRANSFORM_DURATION, ACCURACY_TRANSFORM_EASING); diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/RankNotch.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/RankNotch.cs index 7e73767318..32f2eb2fa5 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/RankNotch.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/RankNotch.cs @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.Y, Height = AccuracyCircle.RANK_CIRCLE_RADIUS, - Width = 1f, + Width = (float)AccuracyCircle.NOTCH_WIDTH_PERCENTAGE * 360f, Colour = OsuColour.Gray(0.3f), EdgeSmoothness = new Vector2(1f) } From 0531c010ebeb17037059d34b21aa948849ca67d2 Mon Sep 17 00:00:00 2001 From: Walavouchey <36758269+Walavouchey@users.noreply.github.com> Date: Tue, 7 Feb 2023 12:08:37 +0100 Subject: [PATCH 02/10] display `RankBadge`s on on their sector centres the D `RankBadge` does this anyway. the A and S badges are slightly off centre to prevent overlap with the SS badge --- .../Ranking/Expanded/Accuracy/AccuracyCircle.cs | 12 ++++++------ .../Screens/Ranking/Expanded/Accuracy/RankBadge.cs | 11 +++++++++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index 3cb09734c5..9ee63b40dd 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -220,12 +220,12 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy Padding = new MarginPadding { Vertical = -15, Horizontal = -20 }, Children = new[] { - new RankBadge(1, getRank(ScoreRank.X)), - new RankBadge(0.95, getRank(ScoreRank.S)), - new RankBadge(0.9, getRank(ScoreRank.A)), - new RankBadge(0.8, getRank(ScoreRank.B)), - new RankBadge(0.7, getRank(ScoreRank.C)), - new RankBadge(0.35, getRank(ScoreRank.D)), + new RankBadge(1, 1, getRank(ScoreRank.X)), + new RankBadge(0.95, 0.95 + (0.025 - virtual_ss_percentage) / 2, getRank(ScoreRank.S)), + new RankBadge(0.9, 0.9125, getRank(ScoreRank.A)), + new RankBadge(0.8, 0.85, getRank(ScoreRank.B)), + new RankBadge(0.7, 0.75, getRank(ScoreRank.C)), + new RankBadge(0, 0.35, getRank(ScoreRank.D)), } }, rankText = new RankText(score.Rank) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/RankBadge.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/RankBadge.cs index 5432b4cbeb..7af327828e 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/RankBadge.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/RankBadge.cs @@ -27,6 +27,11 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy /// public readonly double Accuracy; + /// + /// The position around the to display this badge. + /// + private readonly double displayPosition; + private readonly ScoreRank rank; private Drawable rankContainer; @@ -36,10 +41,12 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy /// Creates a new . /// /// The accuracy value corresponding to . + /// The position around the to display this badge. /// The to be displayed in this . - public RankBadge(double accuracy, ScoreRank rank) + public RankBadge(double accuracy, double position, ScoreRank rank) { Accuracy = accuracy; + displayPosition = position; this.rank = rank; RelativeSizeAxes = Axes.Both; @@ -92,7 +99,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy base.Update(); // Starts at -90deg (top) and moves counter-clockwise by the accuracy - rankContainer.Position = circlePosition(-MathF.PI / 2 - (1 - (float)Accuracy) * MathF.PI * 2); + rankContainer.Position = circlePosition(-MathF.PI / 2 - (1 - (float)displayPosition) * MathF.PI * 2); } private Vector2 circlePosition(float t) From ee40444fd3c96b37b32d3defb8b7509267244b45 Mon Sep 17 00:00:00 2001 From: Walavouchey <36758269+Walavouchey@users.noreply.github.com> Date: Wed, 8 Feb 2023 23:59:19 +0100 Subject: [PATCH 03/10] use `Precision.AlmostEquals` for bounds check --- osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index 9ee63b40dd..24daea21f3 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -276,7 +276,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy foreach (double p in notchPercentages) { - if (targetAccuracy > p - NOTCH_WIDTH_PERCENTAGE / 2 && targetAccuracy < p + NOTCH_WIDTH_PERCENTAGE / 2) + if (Precision.AlmostEquals(p, targetAccuracy, NOTCH_WIDTH_PERCENTAGE / 2)) { int tippingDirection = targetAccuracy - p >= 0 ? 1 : -1; // We "round up" here to match rank criteria targetAccuracy = p + tippingDirection * (NOTCH_WIDTH_PERCENTAGE / 2); From 9544a1d26d6cf819aa192c689fea10e78bb4ac11 Mon Sep 17 00:00:00 2001 From: Walavouchey <36758269+Walavouchey@users.noreply.github.com> Date: Thu, 9 Feb 2023 00:02:32 +0100 Subject: [PATCH 04/10] use values closer to the extremities for test cases --- .../Visual/Ranking/TestSceneAccuracyCircle.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs index 314e275ca3..f94cf9eb58 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs @@ -27,16 +27,16 @@ namespace osu.Game.Tests.Visual.Ranking [TestCase(0, ScoreRank.D)] [TestCase(0.2, ScoreRank.D)] [TestCase(0.5, ScoreRank.D)] - [TestCase(0.699, ScoreRank.D)] + [TestCase(0.6999, ScoreRank.D)] [TestCase(0.7, ScoreRank.C)] [TestCase(0.75, ScoreRank.C)] - [TestCase(0.799, ScoreRank.C)] - [TestCase(0.80, ScoreRank.B)] + [TestCase(0.7999, ScoreRank.C)] + [TestCase(0.8, ScoreRank.B)] [TestCase(0.85, ScoreRank.B)] - [TestCase(0.899, ScoreRank.B)] + [TestCase(0.8999, ScoreRank.B)] [TestCase(0.9, ScoreRank.A)] [TestCase(0.925, ScoreRank.A)] - [TestCase(0.949, ScoreRank.A)] + [TestCase(0.9499, ScoreRank.A)] [TestCase(0.95, ScoreRank.S)] [TestCase(0.975, ScoreRank.S)] [TestCase(0.9999, ScoreRank.S)] From fd93bd3f501de37fa7ca43e7e0a52efe2566cb5f Mon Sep 17 00:00:00 2001 From: Walavouchey <36758269+Walavouchey@users.noreply.github.com> Date: Thu, 9 Feb 2023 00:34:28 +0100 Subject: [PATCH 05/10] move rank accuracy requirements to class-local constants --- .../Expanded/Accuracy/AccuracyCircle.cs | 62 ++++++++++++------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index 24daea21f3..9606c2bf22 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -28,6 +28,18 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy /// public partial class AccuracyCircle : CompositeDrawable { + private const double accuracy_x = 1; + + private const double accuracy_s = 0.95; + + private const double accuracy_a = 0.9; + + private const double accuracy_b = 0.8; + + private const double accuracy_c = 0.7; + + private const double accuracy_d = 0; + /// /// Duration for the transforms causing this component to appear. /// @@ -150,49 +162,49 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy RelativeSizeAxes = Axes.Both, Colour = OsuColour.ForRank(ScoreRank.X), InnerRadius = RANK_CIRCLE_RADIUS, - Current = { Value = 1 } + Current = { Value = accuracy_x } }, new CircularProgress { RelativeSizeAxes = Axes.Both, Colour = OsuColour.ForRank(ScoreRank.S), InnerRadius = RANK_CIRCLE_RADIUS, - Current = { Value = 1 - virtual_ss_percentage } + Current = { Value = accuracy_x - virtual_ss_percentage } }, new CircularProgress { RelativeSizeAxes = Axes.Both, Colour = OsuColour.ForRank(ScoreRank.A), InnerRadius = RANK_CIRCLE_RADIUS, - Current = { Value = 0.95f } + Current = { Value = accuracy_s } }, new CircularProgress { RelativeSizeAxes = Axes.Both, Colour = OsuColour.ForRank(ScoreRank.B), InnerRadius = RANK_CIRCLE_RADIUS, - Current = { Value = 0.9f } + Current = { Value = accuracy_a } }, new CircularProgress { RelativeSizeAxes = Axes.Both, Colour = OsuColour.ForRank(ScoreRank.C), InnerRadius = RANK_CIRCLE_RADIUS, - Current = { Value = 0.8f } + Current = { Value = accuracy_b } }, new CircularProgress { RelativeSizeAxes = Axes.Both, Colour = OsuColour.ForRank(ScoreRank.D), InnerRadius = RANK_CIRCLE_RADIUS, - Current = { Value = 0.7f } + Current = { Value = accuracy_c } }, - new RankNotch(0), - new RankNotch((float)(1 - virtual_ss_percentage)), - new RankNotch(0.95f), - new RankNotch(0.9f), - new RankNotch(0.8f), - new RankNotch(0.7f), + new RankNotch((float)accuracy_x), + new RankNotch((float)(accuracy_x - virtual_ss_percentage)), + new RankNotch((float)accuracy_s), + new RankNotch((float)accuracy_a), + new RankNotch((float)accuracy_b), + new RankNotch((float)accuracy_c), new BufferedContainer { Name = "Graded circle mask", @@ -220,12 +232,13 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy Padding = new MarginPadding { Vertical = -15, Horizontal = -20 }, Children = new[] { - new RankBadge(1, 1, getRank(ScoreRank.X)), - new RankBadge(0.95, 0.95 + (0.025 - virtual_ss_percentage) / 2, getRank(ScoreRank.S)), - new RankBadge(0.9, 0.9125, getRank(ScoreRank.A)), - new RankBadge(0.8, 0.85, getRank(ScoreRank.B)), - new RankBadge(0.7, 0.75, getRank(ScoreRank.C)), - new RankBadge(0, 0.35, getRank(ScoreRank.D)), + // The S and A badges are moved down slightly to prevent collision with the SS badge. + new RankBadge(accuracy_x, accuracy_x, getRank(ScoreRank.X)), + new RankBadge(accuracy_s, Interpolation.Lerp(accuracy_s, (accuracy_x - virtual_ss_percentage), 0.25), getRank(ScoreRank.S)), + new RankBadge(accuracy_a, Interpolation.Lerp(accuracy_a, accuracy_s, 0.25), getRank(ScoreRank.A)), + new RankBadge(accuracy_b, Interpolation.Lerp(accuracy_b, accuracy_a, 0.5), getRank(ScoreRank.B)), + new RankBadge(accuracy_c, Interpolation.Lerp(accuracy_c, accuracy_b, 0.5), getRank(ScoreRank.C)), + new RankBadge(accuracy_d, Interpolation.Lerp(accuracy_d, accuracy_c, 0.5), getRank(ScoreRank.D)), } }, rankText = new RankText(score.Rank) @@ -269,11 +282,16 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy using (BeginDelayedSequence(ACCURACY_TRANSFORM_DELAY)) { double targetAccuracy = score.Accuracy; + double[] notchPercentages = + { + accuracy_s, + accuracy_a, + accuracy_b, + accuracy_c, + }; // Ensure the gauge overshoots or undershoots a bit so it doesn't land in the gaps of the inner graded circle (caused by `RankNotch`es), // to prevent ambiguity on what grade it's pointing at. - double[] notchPercentages = { 0.7, 0.8, 0.9, 0.95 }; - foreach (double p in notchPercentages) { if (Precision.AlmostEquals(p, targetAccuracy, NOTCH_WIDTH_PERCENTAGE / 2)) @@ -288,7 +306,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy if (score.Rank == ScoreRank.X || score.Rank == ScoreRank.XH) targetAccuracy = 1; else - targetAccuracy = Math.Min(1 - virtual_ss_percentage - NOTCH_WIDTH_PERCENTAGE / 2, targetAccuracy); + targetAccuracy = Math.Min(accuracy_x - virtual_ss_percentage - NOTCH_WIDTH_PERCENTAGE / 2, targetAccuracy); // The accuracy circle gauge visually fills up a bit too much. // This wouldn't normally matter but we want it to align properly with the inner graded circle in the above cases. @@ -325,7 +343,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy if (badge.Accuracy > score.Accuracy) continue; - using (BeginDelayedSequence(inverseEasing(ACCURACY_TRANSFORM_EASING, Math.Min(1 - virtual_ss_percentage, badge.Accuracy) / targetAccuracy) * ACCURACY_TRANSFORM_DURATION)) + using (BeginDelayedSequence(inverseEasing(ACCURACY_TRANSFORM_EASING, Math.Min(accuracy_x - virtual_ss_percentage, badge.Accuracy) / targetAccuracy) * ACCURACY_TRANSFORM_DURATION)) { badge.Appear(); From 57312279f02049028fbc97f41f94be8192f323c0 Mon Sep 17 00:00:00 2001 From: Walavouchey <36758269+Walavouchey@users.noreply.github.com> Date: Thu, 9 Feb 2023 02:20:05 +0100 Subject: [PATCH 06/10] adjust `RankNotch` gap size to match original value --- osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index 9606c2bf22..31ce3ddf95 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -88,7 +88,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy /// /// The width of a in terms of accuracy. /// - public const double NOTCH_WIDTH_PERCENTAGE = 0.002; + public const double NOTCH_WIDTH_PERCENTAGE = 1.0 / 360; /// /// The easing for the circle filling transforms. From cf009432cc5af59b8a35e809468ece6472eb2561 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Feb 2023 15:41:45 +0900 Subject: [PATCH 07/10] Centralise accuracy cutoff constants and add lookup helper methods --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 93 +++++++++++++++---- .../Expanded/Accuracy/AccuracyCircle.cs | 18 ++-- 2 files changed, 81 insertions(+), 30 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index b5f42ec2cc..1e03a3ff80 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -7,21 +7,28 @@ using System.Diagnostics; using System.Diagnostics.Contracts; using System.Linq; using osu.Framework.Bindables; +using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Extensions; +using osu.Game.Localisation; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Replays; using osu.Game.Scoring; -using osu.Framework.Localisation; -using osu.Game.Localisation; namespace osu.Game.Rulesets.Scoring { public partial class ScoreProcessor : JudgementProcessor { + private const double accuracy_cutoff_x = 1; + private const double accuracy_cutoff_s = 0.95; + private const double accuracy_cutoff_a = 0.9; + private const double accuracy_cutoff_b = 0.8; + private const double accuracy_cutoff_c = 0.7; + private const double accuracy_cutoff_d = 0; + private const double max_score = 1000000; /// @@ -160,7 +167,7 @@ namespace osu.Game.Rulesets.Scoring Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue); Accuracy.ValueChanged += accuracy => { - Rank.Value = rankFrom(accuracy.NewValue); + Rank.Value = RankFromAccuracy(accuracy.NewValue); foreach (var mod in Mods.Value.OfType()) Rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue); }; @@ -369,22 +376,6 @@ namespace osu.Game.Rulesets.Scoring } } - private ScoreRank rankFrom(double acc) - { - if (acc == 1) - return ScoreRank.X; - if (acc >= 0.95) - return ScoreRank.S; - if (acc >= 0.9) - return ScoreRank.A; - if (acc >= 0.8) - return ScoreRank.B; - if (acc >= 0.7) - return ScoreRank.C; - - return ScoreRank.D; - } - /// /// Resets this ScoreProcessor to a default state. /// @@ -583,6 +574,70 @@ namespace osu.Game.Rulesets.Scoring hitEvents.Clear(); } + #region Static helper methods + + /// + /// Given an accuracy (0..1), return the correct . + /// + public static ScoreRank RankFromAccuracy(double accuracy) + { + switch (accuracy) + { + case accuracy_cutoff_x: + return ScoreRank.X; + + case >= accuracy_cutoff_s: + return ScoreRank.S; + + case >= accuracy_cutoff_a: + return ScoreRank.A; + + case >= accuracy_cutoff_b: + return ScoreRank.B; + + case >= accuracy_cutoff_c: + return ScoreRank.C; + + default: + return ScoreRank.D; + } + } + + /// + /// Given a , return the cutoff accuracy (0..1). + /// Accuracy must be great or equal to the cutoff to qualify for the provided rank. + /// + public static double AccuracyCutoffFromRank(ScoreRank rank) + { + switch (rank) + { + case ScoreRank.X: + case ScoreRank.XH: + return accuracy_cutoff_x; + + case ScoreRank.S: + case ScoreRank.SH: + return accuracy_cutoff_s; + + case ScoreRank.A: + return accuracy_cutoff_a; + + case ScoreRank.B: + return accuracy_cutoff_b; + + case ScoreRank.C: + return accuracy_cutoff_c; + + case ScoreRank.D: + return accuracy_cutoff_d; + + default: + throw new ArgumentOutOfRangeException(nameof(rank), rank, null); + } + } + + #endregion + /// /// Stores the required scoring data that fulfils the minimum requirements for a to calculate score. /// diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index 31ce3ddf95..2ec4270c3c 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -17,6 +17,7 @@ using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Skinning; using osuTK; @@ -28,17 +29,12 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy /// public partial class AccuracyCircle : CompositeDrawable { - private const double accuracy_x = 1; - - private const double accuracy_s = 0.95; - - private const double accuracy_a = 0.9; - - private const double accuracy_b = 0.8; - - private const double accuracy_c = 0.7; - - private const double accuracy_d = 0; + private static readonly double accuracy_x = ScoreProcessor.AccuracyCutoffFromRank(ScoreRank.X); + private static readonly double accuracy_s = ScoreProcessor.AccuracyCutoffFromRank(ScoreRank.S); + private static readonly double accuracy_a = ScoreProcessor.AccuracyCutoffFromRank(ScoreRank.A); + private static readonly double accuracy_b = ScoreProcessor.AccuracyCutoffFromRank(ScoreRank.B); + private static readonly double accuracy_c = ScoreProcessor.AccuracyCutoffFromRank(ScoreRank.C); + private static readonly double accuracy_d = ScoreProcessor.AccuracyCutoffFromRank(ScoreRank.D); /// /// Duration for the transforms causing this component to appear. From 92eb6b6717852dfeb7f3babcadc5a707af4423bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Feb 2023 15:41:55 +0900 Subject: [PATCH 08/10] Simplify `TestSceneAccuracyCircle` test specifications --- .../Visual/Ranking/TestSceneAccuracyCircle.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs index f94cf9eb58..bf18bd3e51 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs @@ -24,26 +24,26 @@ namespace osu.Game.Tests.Visual.Ranking { public partial class TestSceneAccuracyCircle : OsuTestScene { - [TestCase(0, ScoreRank.D)] - [TestCase(0.2, ScoreRank.D)] - [TestCase(0.5, ScoreRank.D)] - [TestCase(0.6999, ScoreRank.D)] - [TestCase(0.7, ScoreRank.C)] - [TestCase(0.75, ScoreRank.C)] - [TestCase(0.7999, ScoreRank.C)] - [TestCase(0.8, ScoreRank.B)] - [TestCase(0.85, ScoreRank.B)] - [TestCase(0.8999, ScoreRank.B)] - [TestCase(0.9, ScoreRank.A)] - [TestCase(0.925, ScoreRank.A)] - [TestCase(0.9499, ScoreRank.A)] - [TestCase(0.95, ScoreRank.S)] - [TestCase(0.975, ScoreRank.S)] - [TestCase(0.9999, ScoreRank.S)] - [TestCase(1, ScoreRank.X)] - public void TestRank(double accuracy, ScoreRank rank) + [TestCase(0)] + [TestCase(0.2)] + [TestCase(0.5)] + [TestCase(0.6999)] + [TestCase(0.7)] + [TestCase(0.75)] + [TestCase(0.7999)] + [TestCase(0.8)] + [TestCase(0.85)] + [TestCase(0.8999)] + [TestCase(0.9)] + [TestCase(0.925)] + [TestCase(0.9499)] + [TestCase(0.95)] + [TestCase(0.975)] + [TestCase(0.9999)] + [TestCase(1)] + public void TestRank(double accuracy) { - var score = createScore(accuracy, rank); + var score = createScore(accuracy, ScoreProcessor.RankFromAccuracy(accuracy)); addCircleStep(score); } From 96c1832af407a9f8cf3d014ea6f4f17e609b02eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Feb 2023 00:07:24 +0100 Subject: [PATCH 09/10] Fix grammar in xmldoc Co-authored-by: Walavouchey <36758269+Walavouchey@users.noreply.github.com> --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 1e03a3ff80..7fc8980cf5 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -605,7 +605,7 @@ namespace osu.Game.Rulesets.Scoring /// /// Given a , return the cutoff accuracy (0..1). - /// Accuracy must be great or equal to the cutoff to qualify for the provided rank. + /// Accuracy must be greater than or equal to the cutoff to qualify for the provided rank. /// public static double AccuracyCutoffFromRank(ScoreRank rank) { From 1df6fc631104e0338eeced59f6b7fa91836ac9a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Feb 2023 17:03:27 +0900 Subject: [PATCH 10/10] Use `if` statements rather than conditional `case` --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 30 ++++++++------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 7fc8980cf5..96f6922224 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -581,26 +581,18 @@ namespace osu.Game.Rulesets.Scoring /// public static ScoreRank RankFromAccuracy(double accuracy) { - switch (accuracy) - { - case accuracy_cutoff_x: - return ScoreRank.X; + if (accuracy == accuracy_cutoff_x) + return ScoreRank.X; + if (accuracy >= accuracy_cutoff_s) + return ScoreRank.S; + if (accuracy >= accuracy_cutoff_a) + return ScoreRank.A; + if (accuracy >= accuracy_cutoff_b) + return ScoreRank.B; + if (accuracy >= accuracy_cutoff_c) + return ScoreRank.C; - case >= accuracy_cutoff_s: - return ScoreRank.S; - - case >= accuracy_cutoff_a: - return ScoreRank.A; - - case >= accuracy_cutoff_b: - return ScoreRank.B; - - case >= accuracy_cutoff_c: - return ScoreRank.C; - - default: - return ScoreRank.D; - } + return ScoreRank.D; } ///