1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-13 23:07:25 +08:00

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

65 lines
2.7 KiB
C#
Raw Normal View History

2024-01-23 16:08:29 -05:00
// 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.Beatmaps;
using osu.Game.Rulesets.Objects.Legacy;
2024-01-23 16:08:29 -05:00
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Ranking.Statistics;
using osuTK;
namespace osu.Game.Rulesets.Osu.Statistics
{
/// <summary>
2024-01-23 23:48:12 -05:00
/// Displays the aim error statistic for a given play.
2024-01-23 16:08:29 -05:00
/// </summary>
public partial class AimError : SimpleStatisticItem<double?>
{
private readonly List<Vector2> hitPoints = new List<Vector2>();
/// <summary>
/// Creates and computes an <see cref="AimError"/> statistic.
/// </summary>
2024-01-23 23:48:12 -05:00
/// <param name="hitEvents">Sequence of <see cref="HitEvent"/>s to calculate the aim error based on.</param>
/// <param name="playableBeatmap">The <see cref="IBeatmap"/> containing the radii of the circles, used to compute the variance of misses.</param>
public AimError(IEnumerable<HitEvent> hitEvents, IBeatmap playableBeatmap)
2024-01-23 16:08:29 -05:00
: base("Aim Error")
{
Value = calculateAimError(hitEvents, playableBeatmap);
2024-01-23 16:08:29 -05:00
}
private double? calculateAimError(IEnumerable<HitEvent> hitEvents, IBeatmap playableBeatmap)
2024-01-23 16:08:29 -05:00
{
2024-01-24 18:08:34 -05:00
IEnumerable<HitEvent> hitCircleEvents = hitEvents.Where(e => e.HitObject is HitCircle && !(e.HitObject is SliderTailCircle)).ToList();
2024-01-23 16:08:29 -05:00
double nonMissCount = hitCircleEvents.Count(e => e.Result.IsHit());
double missCount = hitCircleEvents.Count() - nonMissCount;
if (nonMissCount == 0)
2024-01-23 16:08:29 -05:00
return null;
foreach (var e in hitCircleEvents)
2024-01-23 16:08:29 -05:00
{
if (e.Position == null)
2024-01-23 16:08:29 -05:00
continue;
hitPoints.Add((e.Position - ((OsuHitObject)e.HitObject).StackedEndPosition).Value);
2024-01-23 16:08:29 -05:00
}
double radius = OsuHitObject.OBJECT_RADIUS * LegacyRulesetExtensions.CalculateScaleFromCircleSize(playableBeatmap.Difficulty.CircleSize, true);
2024-01-23 16:08:29 -05:00
// We don't get data for miss locations, so we estimate the total variance using the Rayleigh distribution.
// Deriving the Rayleigh distribution in this form results in a 2 in the denominator,
// but it is removed to take the variance across both axes, instead of across just one.
2024-01-24 18:08:34 -05:00
double variance = (missCount * Math.Pow(radius, 2) + hitPoints.Sum(point => point.LengthSquared)) / nonMissCount;
2024-01-23 16:08:29 -05:00
return Math.Sqrt(variance) * 10;
2024-01-23 16:08:29 -05:00
}
protected override string DisplayValue(double? value) => value == null ? "(not available)" : value.Value.ToString(@"N2");
}
}