mirror of https://github.com/ppy/osu
Fix HUD unstable rate counter not including judgements before load complete
Also unifies UR calculation logic with the results screen statistic item to reduce duplication.
This commit is contained in:
parent
40cffd1682
commit
69809390d3
|
@ -22,7 +22,8 @@ public void TestDistributedHits()
|
|||
|
||||
var unstableRate = new UnstableRate(events);
|
||||
|
||||
Assert.IsTrue(Precision.AlmostEquals(unstableRate.Value, 10 * Math.Sqrt(10)));
|
||||
Assert.IsNotNull(unstableRate.Value);
|
||||
Assert.IsTrue(Precision.AlmostEquals(unstableRate.Value.Value, 10 * Math.Sqrt(10)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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;
|
||||
|
||||
namespace osu.Game.Rulesets.Scoring
|
||||
{
|
||||
public static class HitEventExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculates the "unstable rate" for a sequence of <see cref="HitEvent"/>s.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A non-null <see langword="double"/> value if unstable rate could be calculated,
|
||||
/// and <see langword="null"/> if unstable rate cannot be calculated due to <paramref name="hitEvents"/> being empty.
|
||||
/// </returns>
|
||||
public static double? CalculateUnstableRate(this IEnumerable<HitEvent> hitEvents)
|
||||
{
|
||||
double[] timeOffsets = hitEvents.Where(affectsUnstableRate).Select(ev => ev.TimeOffset).ToArray();
|
||||
return 10 * standardDeviation(timeOffsets);
|
||||
}
|
||||
|
||||
private static bool affectsUnstableRate(HitEvent e) => !(e.HitObject.HitWindows is HitWindows.EmptyHitWindows) && e.Result.IsHit();
|
||||
|
||||
private static double? standardDeviation(double[] timeOffsets)
|
||||
{
|
||||
if (timeOffsets.Length == 0)
|
||||
return null;
|
||||
|
||||
double mean = timeOffsets.Average();
|
||||
double squares = timeOffsets.Select(offset => Math.Pow(offset - mean, 2)).Sum();
|
||||
return Math.Sqrt(squares / timeOffsets.Length);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,6 @@
|
|||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
|
@ -29,8 +26,6 @@ public class UnstableRateCounter : RollingCounter<int>, ISkinnableDrawable
|
|||
private const float alpha_when_invalid = 0.3f;
|
||||
private readonly Bindable<bool> valid = new Bindable<bool>();
|
||||
|
||||
private readonly List<double> hitOffsets = new List<double>();
|
||||
|
||||
[Resolved]
|
||||
private ScoreProcessor scoreProcessor { get; set; }
|
||||
|
||||
|
@ -54,41 +49,20 @@ protected override void LoadComplete()
|
|||
{
|
||||
base.LoadComplete();
|
||||
|
||||
scoreProcessor.NewJudgement += onJudgementAdded;
|
||||
scoreProcessor.JudgementReverted += onJudgementReverted;
|
||||
}
|
||||
|
||||
private void onJudgementAdded(JudgementResult judgement)
|
||||
{
|
||||
if (!changesUnstableRate(judgement)) return;
|
||||
|
||||
hitOffsets.Add(judgement.TimeOffset);
|
||||
scoreProcessor.NewJudgement += updateDisplay;
|
||||
scoreProcessor.JudgementReverted += updateDisplay;
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
private void onJudgementReverted(JudgementResult judgement)
|
||||
{
|
||||
if (judgement.FailedAtJudgement || !changesUnstableRate(judgement)) return;
|
||||
|
||||
hitOffsets.RemoveAt(hitOffsets.Count - 1);
|
||||
updateDisplay();
|
||||
}
|
||||
private void updateDisplay(JudgementResult _) => Scheduler.AddOnce(updateDisplay);
|
||||
|
||||
private void updateDisplay()
|
||||
{
|
||||
// At Count = 0, we get NaN, While we are allowing count = 1, it will be 0 since average = offset.
|
||||
if (hitOffsets.Count > 0)
|
||||
{
|
||||
double mean = hitOffsets.Average();
|
||||
double squares = hitOffsets.Select(offset => Math.Pow(offset - mean, 2)).Sum();
|
||||
Current.Value = (int)(Math.Sqrt(squares / hitOffsets.Count) * 10);
|
||||
valid.Value = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Current.Value = 0;
|
||||
valid.Value = false;
|
||||
}
|
||||
double? unstableRate = scoreProcessor.HitEvents.CalculateUnstableRate();
|
||||
|
||||
valid.Value = unstableRate != null;
|
||||
if (unstableRate != null)
|
||||
Current.Value = (int)unstableRate.Value;
|
||||
}
|
||||
|
||||
protected override IHasText CreateText() => new TextComponent
|
||||
|
@ -102,8 +76,8 @@ protected override void Dispose(bool isDisposing)
|
|||
|
||||
if (scoreProcessor == null) return;
|
||||
|
||||
scoreProcessor.NewJudgement -= onJudgementAdded;
|
||||
scoreProcessor.JudgementReverted -= onJudgementReverted;
|
||||
scoreProcessor.NewJudgement -= updateDisplay;
|
||||
scoreProcessor.JudgementReverted -= updateDisplay;
|
||||
}
|
||||
|
||||
private class TextComponent : CompositeDrawable, IHasText
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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.Scoring;
|
||||
|
||||
namespace osu.Game.Screens.Ranking.Statistics
|
||||
|
@ -11,7 +9,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
|||
/// <summary>
|
||||
/// Displays the unstable rate statistic for a given play.
|
||||
/// </summary>
|
||||
public class UnstableRate : SimpleStatisticItem<double>
|
||||
public class UnstableRate : SimpleStatisticItem<double?>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates and computes an <see cref="UnstableRate"/> statistic.
|
||||
|
@ -20,21 +18,9 @@ public class UnstableRate : SimpleStatisticItem<double>
|
|||
public UnstableRate(IEnumerable<HitEvent> hitEvents)
|
||||
: base("Unstable Rate")
|
||||
{
|
||||
double[] timeOffsets = hitEvents.Where(e => !(e.HitObject.HitWindows is HitWindows.EmptyHitWindows) && e.Result.IsHit())
|
||||
.Select(ev => ev.TimeOffset).ToArray();
|
||||
Value = 10 * standardDeviation(timeOffsets);
|
||||
Value = hitEvents.CalculateUnstableRate();
|
||||
}
|
||||
|
||||
private static double standardDeviation(double[] timeOffsets)
|
||||
{
|
||||
if (timeOffsets.Length == 0)
|
||||
return double.NaN;
|
||||
|
||||
double mean = timeOffsets.Average();
|
||||
double squares = timeOffsets.Select(offset => Math.Pow(offset - mean, 2)).Sum();
|
||||
return Math.Sqrt(squares / timeOffsets.Length);
|
||||
}
|
||||
|
||||
protected override string DisplayValue(double value) => double.IsNaN(value) ? "(not available)" : value.ToString("N2");
|
||||
protected override string DisplayValue(double? value) => value == null ? "(not available)" : value.Value.ToString(@"N2");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue