1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-27 15:43:21 +08:00

Use new own profile statistics in difficulty recommender

This commit is contained in:
Endrik Tombak 2022-06-08 17:44:57 +03:00
parent 187086e4ec
commit bf67b35ade
2 changed files with 32 additions and 75 deletions

View File

@ -5,12 +5,11 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Extensions; using osu.Game.Extensions;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mania;
@ -23,38 +22,28 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
public class TestSceneBeatmapRecommendations : OsuGameTestScene public class TestSceneBeatmapRecommendations : OsuGameTestScene
{ {
[Resolved]
private IRulesetStore rulesetStore { get; set; }
[SetUpSteps] [SetUpSteps]
public override void SetUpSteps() public override void SetUpSteps()
{ {
AddStep("register request handling", () => base.SetUpSteps();
{
((DummyAPIAccess)API).HandleRequest = req =>
{
switch (req)
{
case GetUserRequest userRequest:
userRequest.TriggerSuccess(getUser(userRequest.Ruleset.OnlineID));
return true;
}
return false; AddStep("populate ruleset statistics", () =>
{
Dictionary<string, UserStatistics> rulesetStatistics = new Dictionary<string, UserStatistics>();
rulesetStore.AvailableRulesets.Where(ruleset => ruleset.IsLegacyRuleset()).ForEach(rulesetInfo =>
{
rulesetStatistics[rulesetInfo.ShortName] = new UserStatistics
{
PP = getNecessaryPP(rulesetInfo.OnlineID)
}; };
}); });
base.SetUpSteps(); API.LocalUser.Value.RulesetsStatistics = rulesetStatistics;
});
APIUser getUser(int? rulesetID)
{
return new APIUser
{
Username = @"Dummy",
Id = 1001,
Statistics = new UserStatistics
{
PP = getNecessaryPP(rulesetID)
}
};
}
decimal getNecessaryPP(int? rulesetID) decimal getNecessaryPP(int? rulesetID)
{ {

View File

@ -7,11 +7,8 @@ using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Extensions;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Rulesets; using osu.Game.Rulesets;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
@ -25,26 +22,15 @@ namespace osu.Game.Beatmaps
[Resolved] [Resolved]
private IAPIProvider api { get; set; } private IAPIProvider api { get; set; }
[Resolved]
private IRulesetStore rulesets { get; set; }
[Resolved] [Resolved]
private Bindable<RulesetInfo> ruleset { get; set; } private Bindable<RulesetInfo> ruleset { get; set; }
/// <summary> private readonly Dictionary<string, double> recommendedDifficultyMapping = new Dictionary<string, double>();
/// The user for which the last requests were run.
/// </summary>
private int? requestedUserId;
private readonly Dictionary<IRulesetInfo, double> recommendedDifficultyMapping = new Dictionary<IRulesetInfo, double>();
private readonly IBindable<APIState> apiState = new Bindable<APIState>();
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
apiState.BindTo(api.State); api.LocalUser.BindValueChanged(_ => populateValues(), true);
apiState.BindValueChanged(onlineStateChanged, true);
} }
/// <summary> /// <summary>
@ -58,12 +44,12 @@ namespace osu.Game.Beatmaps
[CanBeNull] [CanBeNull]
public BeatmapInfo GetRecommendedBeatmap(IEnumerable<BeatmapInfo> beatmaps) public BeatmapInfo GetRecommendedBeatmap(IEnumerable<BeatmapInfo> beatmaps)
{ {
foreach (var r in orderedRulesets) foreach (string r in orderedRulesets)
{ {
if (!recommendedDifficultyMapping.TryGetValue(r, out double recommendation)) if (!recommendedDifficultyMapping.TryGetValue(r, out double recommendation))
continue; continue;
BeatmapInfo beatmapInfo = beatmaps.Where(b => b.Ruleset.Equals(r)).OrderBy(b => BeatmapInfo beatmapInfo = beatmaps.Where(b => b.Ruleset.ShortName.Equals(r)).OrderBy(b =>
{ {
double difference = b.StarRating - recommendation; double difference = b.StarRating - recommendation;
return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder
@ -76,55 +62,37 @@ namespace osu.Game.Beatmaps
return null; return null;
} }
private void fetchRecommendedValues() private void populateValues()
{ {
if (recommendedDifficultyMapping.Count > 0 && api.LocalUser.Value.Id == requestedUserId) if (api.LocalUser.Value.RulesetsStatistics == null)
return; return;
requestedUserId = api.LocalUser.Value.Id; foreach (var statistic in api.LocalUser.Value.RulesetsStatistics)
// only query API for built-in rulesets
rulesets.AvailableRulesets.Where(ruleset => ruleset.IsLegacyRuleset()).ForEach(rulesetInfo =>
{
var req = new GetUserRequest(api.LocalUser.Value.Id, rulesetInfo);
req.Success += result =>
{ {
decimal? pp = api.LocalUser.Value.RulesetsStatistics[statistic.Key].PP;
// algorithm taken from https://github.com/ppy/osu-web/blob/e6e2825516449e3d0f3f5e1852c6bdd3428c3437/app/Models/User.php#L1505 // algorithm taken from https://github.com/ppy/osu-web/blob/e6e2825516449e3d0f3f5e1852c6bdd3428c3437/app/Models/User.php#L1505
recommendedDifficultyMapping[rulesetInfo] = Math.Pow((double)(result.Statistics.PP ?? 0), 0.4) * 0.195; double recommended = Math.Pow((double)(pp ?? 0), 0.4) * 0.195;
}; recommendedDifficultyMapping[statistic.Key] = recommended;
}
api.Queue(req);
});
} }
/// <returns> /// <returns>
/// Rulesets ordered descending by their respective recommended difficulties. /// Rulesets ordered descending by their respective recommended difficulties.
/// The currently selected ruleset will always be first. /// The currently selected ruleset will always be first.
/// </returns> /// </returns>
private IEnumerable<IRulesetInfo> orderedRulesets private IEnumerable<string> orderedRulesets
{ {
get get
{ {
if (LoadState < LoadState.Ready || ruleset.Value == null) if (LoadState < LoadState.Ready || ruleset.Value == null)
return Enumerable.Empty<RulesetInfo>(); return Enumerable.Empty<string>();
return recommendedDifficultyMapping return recommendedDifficultyMapping
.OrderByDescending(pair => pair.Value) .OrderByDescending(pair => pair.Value)
.Select(pair => pair.Key) .Select(pair => pair.Key)
.Where(r => !r.Equals(ruleset.Value)) .Where(r => !r.Equals(ruleset.Value.ShortName))
.Prepend(ruleset.Value); .Prepend(ruleset.Value.ShortName);
} }
} }
private void onlineStateChanged(ValueChangedEvent<APIState> state) => Schedule(() =>
{
switch (state.NewValue)
{
case APIState.Online:
fetchRecommendedValues();
break;
}
});
} }
} }