public override void Execute() { var workingBeatmap = new ProcessorWorkingBeatmap(Beatmap); var scoreParser = new ProcessorScoreDecoder(workingBeatmap); foreach (var f in Replays) { Score score; using (var stream = File.OpenRead(f)) score = scoreParser.Parse(stream); // Convert + process beatmap var categoryAttribs = new Dictionary <string, double>(); var performanceCalculator = score.ScoreInfo.Ruleset.CreateInstance().CreatePerformanceCalculator(workingBeatmap, score.ScoreInfo); Trace.Assert(performanceCalculator != null); double pp = performanceCalculator.Calculate(categoryAttribs); Console.WriteLine(f); writeAttribute("Player", score.ScoreInfo.User.Username); writeAttribute("Mods", score.ScoreInfo.Mods.Length > 0 ? score.ScoreInfo.Mods.Select(m => m.Acronym).Aggregate((c, n) => $"{c}, {n}") : "None"); foreach (var kvp in categoryAttribs) { writeAttribute(kvp.Key, kvp.Value.ToString(CultureInfo.InvariantCulture)); } writeAttribute("pp", pp.ToString(CultureInfo.InvariantCulture)); Console.WriteLine(); } }
public override void Execute() { var workingBeatmap = new ProcessorWorkingBeatmap(Beatmap); var scoreParser = new ProcessorScoreDecoder(workingBeatmap); foreach (var f in Replays) { Score score; using (var stream = File.OpenRead(f)) score = scoreParser.Parse(stream); var ruleset = score.ScoreInfo.Ruleset.CreateInstance(); var difficultyCalculator = ruleset.CreateDifficultyCalculator(workingBeatmap); var difficultyAttributes = difficultyCalculator.Calculate(LegacyHelper.TrimNonDifficultyAdjustmentMods(ruleset, score.ScoreInfo.Mods).ToArray()); var performanceCalculator = score.ScoreInfo.Ruleset.CreateInstance().CreatePerformanceCalculator(difficultyAttributes, score.ScoreInfo); var categoryAttribs = new Dictionary <string, double>(); double pp = performanceCalculator.Calculate(categoryAttribs); Console.WriteLine(f); writeAttribute("Player", score.ScoreInfo.User.Username); writeAttribute("Mods", score.ScoreInfo.Mods.Length > 0 ? score.ScoreInfo.Mods.Select(m => m.Acronym).Aggregate((c, n) => $"{c}, {n}") : "None"); foreach (var kvp in categoryAttribs) { writeAttribute(kvp.Key, kvp.Value.ToString(CultureInfo.InvariantCulture)); } writeAttribute("pp", pp.ToString(CultureInfo.InvariantCulture)); Console.WriteLine(); } }
public static async Task <double> CalculatePerformancePoints(Score score) { var beatmapMd5 = await GetBeatmap(score.FileChecksum, score.Beatmap.Id); var beatmap = new ProcessorWorkingBeatmap($"./data/beatmaps/{beatmapMd5}.osu"); var psp = new ProcessorScoreDecoder(beatmap); var parsedScore = psp.Parse(score, $"./data/osr/{score.ReplayChecksum}.osr"); var categoryAttribs = new Dictionary <string, double>(); var pp = parsedScore.ScoreInfo.Ruleset .CreateInstance() .CreatePerformanceCalculator(beatmap, parsedScore.ScoreInfo) .Calculate(categoryAttribs); return(pp); }
public override void Execute() { var workingBeatmap = ProcessorWorkingBeatmap.FromFileOrId(Beatmap); var scoreParser = new ProcessorScoreDecoder(workingBeatmap); foreach (var f in Replays) { Score score; using (var stream = File.OpenRead(f)) score = scoreParser.Parse(stream); var ruleset = score.ScoreInfo.Ruleset.CreateInstance(); var difficultyCalculator = ruleset.CreateDifficultyCalculator(workingBeatmap); Mod[] mods = score.ScoreInfo.Mods; if (score.ScoreInfo.IsLegacyScore) { mods = LegacyHelper.ConvertToLegacyDifficultyAdjustmentMods(ruleset, mods); } var difficultyAttributes = difficultyCalculator.Calculate(mods); var performanceCalculator = score.ScoreInfo.Ruleset.CreateInstance().CreatePerformanceCalculator(); var ppAttributes = performanceCalculator?.Calculate(score.ScoreInfo, difficultyAttributes); Console.WriteLine(f); writeAttribute("Player", score.ScoreInfo.User.Username); writeAttribute("Mods", score.ScoreInfo.Mods.Length > 0 ? score.ScoreInfo.Mods.Select(m => m.Acronym).Aggregate((c, n) => $"{c}, {n}") : "None"); var ppAttributeValues = JsonConvert.DeserializeObject <Dictionary <string, object> >(JsonConvert.SerializeObject(ppAttributes)) ?? new Dictionary <string, object>(); foreach (var attrib in ppAttributeValues) { writeAttribute(attrib.Key.Humanize(), FormattableString.Invariant($"{attrib.Value:N2}")); } Console.WriteLine(); } }
public static async Task <double> CalculatePerformancePoints(DbScore score) { var beatmapMd5 = await GetBeatmapByMd5(score.FileChecksum); if (beatmapMd5 == string.Empty) { return(0.0); } var workingBeatmap = new ProcessorWorkingBeatmap($"./data/beatmaps/{beatmapMd5}.osu"); var psp = new ProcessorScoreDecoder(workingBeatmap); var parsedScore = psp.Parse(score); var categoryAttribs = new Dictionary <string, double>(); var pp = parsedScore.ScoreInfo.Ruleset .CreateInstance() .CreatePerformanceCalculator(workingBeatmap, parsedScore.ScoreInfo) .Calculate(categoryAttribs); return(pp); }
public async Task ExecuteAsync(Label status_block) { if (Key == "" || ProfileName == "") { return; } var displayPlays = new List <UserPlayInfo>(); var ruleset = LegacyHelper.GetRulesetFromLegacyID(0); status_block.Dispatcher.Invoke(() => { status_block.Content = "Getting user data..."; }); dynamic userData = await getJsonFromApi($"get_user?k={Key}&u={ProfileName}&m={0}"); if (!((JArray)userData).Any()) { ResultsDoc = new Document(new Span("Could not find user " + ProfileName)); return; } userData = userData[0]; status_block.Dispatcher.Invoke(() => { status_block.Content = "Getting user top scores..."; }); foreach (var play in await getJsonFromApi($"get_user_best?k={Key}&u={ProfileName}&m={0}&limit=100")) { string beatmapID = play.beatmap_id; string cachePath = Path.Combine("cache", $"{beatmapID}.osu"); if (!File.Exists(cachePath)) { status_block.Dispatcher.Invoke(() => { status_block.Content = $"Downloading {beatmapID}.osu..."; }); await new FileWebRequest(cachePath, $"{base_url}/osu/{beatmapID}").PerformAsync(); } Mod[] mods = ruleset.ConvertFromLegacyMods((LegacyMods)play.enabled_mods).ToArray(); var working = new ProcessorWorkingBeatmap(cachePath, (int)play.beatmap_id); var scoreInfo = new ScoreInfo { Ruleset = ruleset.RulesetInfo, MaxCombo = play.maxcombo, Mods = mods, Statistics = new Dictionary <HitResult, int> { { HitResult.Perfect, (int)play.countgeki }, { HitResult.Great, (int)play.count300 }, { HitResult.Good, (int)play.countkatu }, { HitResult.Ok, (int)play.count100 }, { HitResult.Meh, (int)play.count50 }, { HitResult.Miss, (int)play.countmiss } } }; var score = new ProcessorScoreDecoder(working).Parse(scoreInfo); var categoryAttribs = new Dictionary <string, double>(); OsuPerformanceCalculator calculator = (OsuPerformanceCalculator)ruleset.CreatePerformanceCalculator(working, score.ScoreInfo); double localPP = calculator.Calculate(categoryAttribs); var thisPlay = new UserPlayInfo { Beatmap = working.BeatmapInfo, LocalPP = localPP, AimPP = categoryAttribs["Aim"], TapPP = categoryAttribs["Speed"], AccPP = categoryAttribs["Accuracy"], LivePP = play.pp, Mods = mods.Length > 0 ? mods.Select(m => m.Acronym).Aggregate((c, n) => $"{c}, {n}") : "", PlayMaxCombo = scoreInfo.MaxCombo, BeatmapMaxCombo = calculator.Attributes.MaxCombo, PlayAccuracy = scoreInfo.Accuracy, MissCount = scoreInfo.Statistics[HitResult.Miss] }; displayPlays.Add(thisPlay); } var localOrdered = displayPlays.OrderByDescending(p => p.LocalPP).ToList(); var liveOrdered = displayPlays.OrderByDescending(p => p.LivePP).ToList(); int index = 0; double totalLocalPP = localOrdered.Sum(play => Math.Pow(0.95, index++) * play.LocalPP); double totalLivePP = userData.pp_raw; index = 0; double nonBonusLivePP = liveOrdered.Sum(play => Math.Pow(0.95, index++) * play.LivePP); //todo: implement properly. this is pretty damn wrong. var playcountBonusPP = (totalLivePP - nonBonusLivePP); totalLocalPP += playcountBonusPP; double totalDiffPP = totalLocalPP - totalLivePP; ResultsDoc = new Document( new Span($"User: {userData.username}"), "\n", new Span($"Live PP: {totalLivePP:F1} (including {playcountBonusPP:F1}pp from playcount)"), "\n", new Span($"Local PP: {totalLocalPP:F1} ({totalDiffPP:+0.0;-0.0;-})"), "\n", new Alba.CsConsoleFormat.Grid { Columns = { GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto }, Children = { new Cell("beatmap"), new Cell("mods") { Align = Align.Center }, new Cell("live pp"), new Cell("acc") { Align = Align.Center }, new Cell("miss"), new Cell("combo") { Align = Align.Center }, new Cell("aim pp"), new Cell("tap pp"), new Cell("acc pp"), new Cell("local pp"), new Cell("pp change"), new Cell("position change"), localOrdered.Select(item => new[] { new Cell($"{item.Beatmap.OnlineBeatmapID} - {item.Beatmap.ToString().Substring(0, Math.Min(80, item.Beatmap.ToString().Length))}"), new Cell(item.Mods) { Align = Align.Center }, new Cell($"{item.LivePP:F1}") { Align = Align.Right }, new Cell($"{item.PlayAccuracy * 100f:F2}" + " %") { Align = Align.Center }, new Cell($"{item.MissCount}") { Align = Align.Center }, new Cell($"{item.PlayMaxCombo}/{item.BeatmapMaxCombo}") { Align = Align.Center }, new Cell($"{item.AimPP:F1}") { Align = Align.Right }, new Cell($"{item.TapPP:F1}") { Align = Align.Right }, new Cell($"{item.AccPP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP - item.LivePP:F1}") { Align = Align.Right }, new Cell($"{liveOrdered.IndexOf(item) - localOrdered.IndexOf(item):+0;-0;-}") { Align = Align.Center }, }) } } ); }
public override void Execute() { var displayPlays = new List <UserPlayInfo>(); var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? 0); Console.WriteLine("Getting user data..."); dynamic userData = getJsonFromApi($"get_user?k={Key}&u={ProfileName}&m={Ruleset}")[0]; Console.WriteLine("Getting user top scores..."); foreach (var play in getJsonFromApi($"get_user_best?k={Key}&u={ProfileName}&m={Ruleset}&limit=100")) { string beatmapID = play.beatmap_id; string cachePath = Path.Combine("cache", $"{beatmapID}.osu"); if (!File.Exists(cachePath)) { Console.WriteLine($"Downloading {beatmapID}.osu..."); new FileWebRequest(cachePath, $"{base_url}/osu/{beatmapID}").Perform(); } Mod[] mods = ruleset.ConvertFromLegacyMods((LegacyMods)play.enabled_mods).ToArray(); var working = new ProcessorWorkingBeatmap(cachePath, (int)play.beatmap_id); var score = new ProcessorScoreDecoder(working).Parse(new ScoreInfo { Ruleset = ruleset.RulesetInfo, MaxCombo = play.maxcombo, Mods = mods, Statistics = new Dictionary <HitResult, int> { { HitResult.Perfect, (int)play.countgeki }, { HitResult.Great, (int)play.count300 }, { HitResult.Good, (int)play.count100 }, { HitResult.Ok, (int)play.countkatu }, { HitResult.Meh, (int)play.count50 }, { HitResult.Miss, (int)play.countmiss } } }); var thisPlay = new UserPlayInfo { Beatmap = working.BeatmapInfo, LocalPP = ruleset.CreatePerformanceCalculator(working, score.ScoreInfo).Calculate(), LivePP = play.pp, Mods = mods.Length > 0 ? mods.Select(m => m.Acronym).Aggregate((c, n) => $"{c}, {n}") : "None" }; displayPlays.Add(thisPlay); } var localOrdered = displayPlays.OrderByDescending(p => p.LocalPP).ToList(); var liveOrdered = displayPlays.OrderByDescending(p => p.LivePP).ToList(); int index = 0; double totalLocalPP = localOrdered.Sum(play => Math.Pow(0.95, index++) * play.LocalPP); double totalLivePP = userData.pp_raw; index = 0; double nonBonusLivePP = liveOrdered.Sum(play => Math.Pow(0.95, index++) * play.LivePP); //todo: implement properly. this is pretty damn wrong. var playcountBonusPP = (totalLivePP - nonBonusLivePP); totalLocalPP += playcountBonusPP; double totalDiffPP = totalLocalPP - totalLivePP; OutputDocument(new Document( new Span($"User: {userData.username}"), "\n", new Span($"Live PP: {totalLivePP:F1} (including {playcountBonusPP:F1}pp from playcount)"), "\n", new Span($"Local PP: {totalLocalPP:F1} ({totalDiffPP:+0.0;-0.0;-})"), "\n", new Grid { Columns = { GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto }, Children = { new Cell("beatmap"), new Cell("live pp"), new Cell("local pp"), new Cell("pp change"), new Cell("position change"), localOrdered.Select(item => new[] { new Cell($"{item.Beatmap.OnlineBeatmapID} - {item.Beatmap}"), new Cell($"{item.LivePP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP - item.LivePP:F1}") { Align = Align.Right }, new Cell($"{liveOrdered.IndexOf(item) - localOrdered.IndexOf(item):+0;-0;-}") { Align = Align.Center }, }) } } )); }
public override void Execute() { var displayPlays = new List <UserPlayInfo>(); var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? 0); var rulesetApiName = LegacyHelper.GetRulesetShortNameFromId(Ruleset ?? 0); Console.WriteLine("Getting user data..."); dynamic userData = GetJsonFromApi($"users/{ProfileName}/{rulesetApiName}"); Console.WriteLine("Getting user top scores..."); foreach (var play in GetJsonFromApi($"users/{userData.id}/scores/best?mode={rulesetApiName}&limit=100")) { var working = ProcessorWorkingBeatmap.FromFileOrId((string)play.beatmap.id); var modsAcronyms = ((JArray)play.mods).Select(x => x.ToString()).ToArray(); Mod[] mods = ruleset.CreateAllMods().Where(m => modsAcronyms.Contains(m.Acronym)).ToArray(); var scoreInfo = new ScoreInfo(working.BeatmapInfo, ruleset.RulesetInfo) { TotalScore = play.score, MaxCombo = play.max_combo, Mods = mods, Statistics = new Dictionary <HitResult, int>() }; scoreInfo.SetCount300((int)play.statistics.count_300); scoreInfo.SetCountGeki((int)play.statistics.count_geki); scoreInfo.SetCount100((int)play.statistics.count_100); scoreInfo.SetCountKatu((int)play.statistics.count_katu); scoreInfo.SetCount50((int)play.statistics.count_50); scoreInfo.SetCountMiss((int)play.statistics.count_miss); var score = new ProcessorScoreDecoder(working).Parse(scoreInfo); var difficultyCalculator = ruleset.CreateDifficultyCalculator(working); var difficultyAttributes = difficultyCalculator.Calculate(LegacyHelper.ConvertToLegacyDifficultyAdjustmentMods(ruleset, scoreInfo.Mods).ToArray()); var performanceCalculator = ruleset.CreatePerformanceCalculator(); var ppAttributes = performanceCalculator?.Calculate(score.ScoreInfo, difficultyAttributes); var thisPlay = new UserPlayInfo { Beatmap = working.BeatmapInfo, LocalPP = ppAttributes?.Total ?? 0, LivePP = play.pp, Mods = scoreInfo.Mods.Select(m => m.Acronym).ToArray(), MissCount = play.statistics.count_miss, Accuracy = scoreInfo.Accuracy * 100, Combo = play.max_combo, MaxCombo = difficultyAttributes.MaxCombo }; displayPlays.Add(thisPlay); } var localOrdered = displayPlays.OrderByDescending(p => p.LocalPP).ToList(); var liveOrdered = displayPlays.OrderByDescending(p => p.LivePP).ToList(); int index = 0; double totalLocalPP = localOrdered.Sum(play => Math.Pow(0.95, index++) * play.LocalPP); double totalLivePP = userData.statistics.pp; index = 0; double nonBonusLivePP = liveOrdered.Sum(play => Math.Pow(0.95, index++) * play.LivePP); //todo: implement properly. this is pretty damn wrong. var playcountBonusPP = (totalLivePP - nonBonusLivePP); totalLocalPP += playcountBonusPP; double totalDiffPP = totalLocalPP - totalLivePP; if (OutputJson) { var json = JsonConvert.SerializeObject(new { Username = userData.username, LivePp = totalLivePP, LocalPp = totalLocalPP, PlaycountPp = playcountBonusPP, Scores = localOrdered.Select(item => new { BeatmapId = item.Beatmap.OnlineID, BeatmapName = item.Beatmap.ToString(), item.Combo, item.Accuracy, item.MissCount, item.Mods, LivePp = item.LivePP, LocalPp = item.LocalPP, PositionChange = liveOrdered.IndexOf(item) - localOrdered.IndexOf(item) }) }); Console.Write(json); if (OutputFile != null) { File.WriteAllText(OutputFile, json); } } else { OutputDocument(new Document( new Span($"User: {userData.username}"), "\n", new Span($"Live PP: {totalLivePP:F1} (including {playcountBonusPP:F1}pp from playcount)"), "\n", new Span($"Local PP: {totalLocalPP:F1} ({totalDiffPP:+0.0;-0.0;-})"), "\n", new Grid { Columns = { GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto }, Children = { new Cell("#"), new Cell("beatmap"), new Cell("max combo"), new Cell("accuracy"), new Cell("misses"), new Cell("mods"), new Cell("live pp"), new Cell("local pp"), new Cell("pp change"), new Cell("position change"), localOrdered.Select(item => new[] { new Cell($"{localOrdered.IndexOf(item) + 1}"), new Cell($"{item.Beatmap.OnlineID} - {item.Beatmap}"), new Cell($"{item.Combo}/{item.MaxCombo}x") { Align = Align.Right }, new Cell($"{Math.Round(item.Accuracy, 2)}%") { Align = Align.Right }, new Cell($"{item.MissCount}") { Align = Align.Right }, new Cell($"{(item.Mods.Length > 0 ? string.Join(", ", item.Mods) : "None")}") { Align = Align.Right }, new Cell($"{item.LivePP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP - item.LivePP:F1}") { Align = Align.Right }, new Cell($"{liveOrdered.IndexOf(item) - localOrdered.IndexOf(item):+0;-0;-}") { Align = Align.Center }, }) } }) ); } }
public override void Execute() { var rulesetApiName = LegacyHelper.GetRulesetShortNameFromId(Ruleset ?? 0); var leaderboard = GetJsonFromApi($"rankings/{rulesetApiName}/performance?cursor[page]={LeaderboardPage - 1}"); var calculatedPlayers = new List <LeaderboardPlayerInfo>(); foreach (var player in leaderboard.ranking) { if (calculatedPlayers.Count >= Limit) { break; } var plays = new List <(double, double)>(); // (local, live) var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? 0); Console.WriteLine($"Calculating {player.user.username} top scores..."); foreach (var play in GetJsonFromApi($"users/{player.user.id}/scores/best?mode={rulesetApiName}&limit=100")) { var working = ProcessorWorkingBeatmap.FromFileOrId((string)play.beatmap.id); var modsAcronyms = ((JArray)play.mods).Select(x => x.ToString()).ToArray(); Mod[] mods = ruleset.CreateAllMods().Where(m => modsAcronyms.Contains(m.Acronym)).ToArray(); var scoreInfo = new ScoreInfo(working.BeatmapInfo, ruleset.RulesetInfo) { TotalScore = play.score, MaxCombo = play.max_combo, Mods = mods, Statistics = new Dictionary <HitResult, int>() }; scoreInfo.SetCount300((int)play.statistics.count_300); scoreInfo.SetCountGeki((int)play.statistics.count_geki); scoreInfo.SetCount100((int)play.statistics.count_100); scoreInfo.SetCountKatu((int)play.statistics.count_katu); scoreInfo.SetCount50((int)play.statistics.count_50); scoreInfo.SetCountMiss((int)play.statistics.count_miss); var score = new ProcessorScoreDecoder(working).Parse(scoreInfo); var difficultyCalculator = ruleset.CreateDifficultyCalculator(working); var difficultyAttributes = difficultyCalculator.Calculate(LegacyHelper.ConvertToLegacyDifficultyAdjustmentMods(ruleset, scoreInfo.Mods).ToArray()); var performanceCalculator = ruleset.CreatePerformanceCalculator(); plays.Add((performanceCalculator?.Calculate(score.ScoreInfo, difficultyAttributes).Total ?? 0, play.pp)); } var localOrdered = plays.Select(x => x.Item1).OrderByDescending(x => x).ToList(); var liveOrdered = plays.Select(x => x.Item2).OrderByDescending(x => x).ToList(); int index = 0; double totalLocalPP = localOrdered.Sum(play => Math.Pow(0.95, index++) * play); double totalLivePP = player.pp; index = 0; double nonBonusLivePP = liveOrdered.Sum(play => Math.Pow(0.95, index++) * play); //todo: implement properly. this is pretty damn wrong. var playcountBonusPP = (totalLivePP - nonBonusLivePP); totalLocalPP += playcountBonusPP; calculatedPlayers.Add(new LeaderboardPlayerInfo { LivePP = totalLivePP, LocalPP = totalLocalPP, Username = player.user.username }); } calculatedPlayers = calculatedPlayers.OrderByDescending(x => x.LocalPP).ToList(); var liveOrderedPlayers = calculatedPlayers.OrderByDescending(x => x.LivePP).ToList(); if (OutputJson) { var json = JsonConvert.SerializeObject(calculatedPlayers); Console.Write(json); if (OutputFile != null) { File.WriteAllText(OutputFile, json); } } else { OutputDocument(new Document( new Grid { Columns = { GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto }, Children = { new Cell("#"), new Cell("username"), new Cell("live pp"), new Cell("local pp"), new Cell("pp change"), calculatedPlayers.Select(item => new[] { new Cell($"{liveOrderedPlayers.IndexOf(item) - calculatedPlayers.IndexOf(item):+0;-0;-}"), new Cell($"{item.Username}"), new Cell($"{item.LivePP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP - item.LivePP:F1}") { Align = Align.Right } }) } }) ); } }
public override void Execute() { var displayPlays = new List <UserPlayInfo>(); var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? 0); Console.WriteLine("Getting user data..."); dynamic userData = getJsonFromApi($"get_user?k={Key}&u={ProfileName}&m={Ruleset}")[0]; Console.WriteLine("Getting user top scores..."); foreach (var play in getJsonFromApi($"get_user_best?k={Key}&u={ProfileName}&m={Ruleset}&limit=100")) { string beatmapID = play.beatmap_id; string cachePath = Path.Combine("cache", $"{beatmapID}.osu"); if (!File.Exists(cachePath)) { Console.WriteLine($"Downloading {beatmapID}.osu..."); new FileWebRequest(cachePath, $"{base_url}/osu/{beatmapID}").Perform(); } var working = new ProcessorWorkingBeatmap(cachePath, (int)play.beatmap_id); var scoreInfo = new ScoreInfo { Ruleset = ruleset.RulesetInfo, TotalScore = play.score, MaxCombo = play.maxcombo, Mods = ruleset.ConvertFromLegacyMods((LegacyMods)play.enabled_mods).ToArray(), Statistics = new Dictionary <HitResult, int>() }; scoreInfo.SetCount300((int)play.count300); scoreInfo.SetCountGeki((int)play.countgeki); scoreInfo.SetCount100((int)play.count100); scoreInfo.SetCountKatu((int)play.countkatu); scoreInfo.SetCount50((int)play.count50); scoreInfo.SetCountMiss((int)play.countmiss); var score = new ProcessorScoreDecoder(working).Parse(scoreInfo); var difficultyCalculator = ruleset.CreateDifficultyCalculator(working); var difficultyAttributes = difficultyCalculator.Calculate(LegacyHelper.TrimNonDifficultyAdjustmentMods(ruleset, scoreInfo.Mods).ToArray()); var performanceCalculator = ruleset.CreatePerformanceCalculator(difficultyAttributes, score.ScoreInfo); var categories = new Dictionary <string, double>(); var localPP = performanceCalculator.Calculate(categories); var thisPlay = new UserPlayInfo { Beatmap = working.BeatmapInfo, LocalPP = localPP, LivePP = play.pp, Mods = scoreInfo.Mods.Select(m => m.Acronym).ToArray(), MissCount = play.countmiss, Accuracy = scoreInfo.Accuracy * 100, Combo = play.maxcombo, MaxCombo = (int)categories.GetValueOrDefault("Max Combo") }; displayPlays.Add(thisPlay); } var localOrdered = displayPlays.OrderByDescending(p => p.LocalPP).ToList(); var liveOrdered = displayPlays.OrderByDescending(p => p.LivePP).ToList(); int index = 0; double totalLocalPP = localOrdered.Sum(play => Math.Pow(0.95, index++) * play.LocalPP); double totalLivePP = userData.pp_raw; index = 0; double nonBonusLivePP = liveOrdered.Sum(play => Math.Pow(0.95, index++) * play.LivePP); //todo: implement properly. this is pretty damn wrong. var playcountBonusPP = (totalLivePP - nonBonusLivePP); totalLocalPP += playcountBonusPP; double totalDiffPP = totalLocalPP - totalLivePP; if (OutputJson) { var json = JsonConvert.SerializeObject(new { Username = userData.username, LivePp = totalLivePP, LocalPp = totalLocalPP, PlaycountPp = playcountBonusPP, Scores = localOrdered.Select(item => new { BeatmapId = item.Beatmap.OnlineBeatmapID, BeatmapName = item.Beatmap.ToString(), item.Combo, item.Accuracy, item.MissCount, item.Mods, LivePp = item.LivePP, LocalPp = item.LocalPP, PositionChange = liveOrdered.IndexOf(item) - localOrdered.IndexOf(item) }) }); Console.Write(json); if (OutputFile != null) { File.WriteAllText(OutputFile, json); } } else { OutputDocument(new Document( new Span($"User: {userData.username}"), "\n", new Span($"Live PP: {totalLivePP:F1} (including {playcountBonusPP:F1}pp from playcount)"), "\n", new Span($"Local PP: {totalLocalPP:F1} ({totalDiffPP:+0.0;-0.0;-})"), "\n", new Grid { Columns = { GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto }, Children = { new Cell("#"), new Cell("beatmap"), new Cell("max combo"), new Cell("accuracy"), new Cell("misses"), new Cell("mods"), new Cell("live pp"), new Cell("local pp"), new Cell("pp change"), new Cell("position change"), localOrdered.Select(item => new[] { new Cell($"{localOrdered.IndexOf(item) + 1}"), new Cell($"{item.Beatmap.OnlineBeatmapID} - {item.Beatmap}"), new Cell($"{item.Combo}/{item.MaxCombo}x") { Align = Align.Right }, new Cell($"{Math.Round(item.Accuracy, 2)}%") { Align = Align.Right }, new Cell($"{item.MissCount}") { Align = Align.Right }, new Cell($"{(item.Mods.Length > 0 ? string.Join(", ", item.Mods) : "None")}") { Align = Align.Right }, new Cell($"{item.LivePP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP - item.LivePP:F1}") { Align = Align.Right }, new Cell($"{liveOrdered.IndexOf(item) - localOrdered.IndexOf(item):+0;-0;-}") { Align = Align.Center }, }) } }) ); } }
public override void Execute() { var displayPlays = new List <UserPlayInfo>(); var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? 0); Console.WriteLine("Getting user data..."); dynamic userData = getJsonFromApi($"get_user?k={Key}&u={ProfileName}&m={Ruleset}")[0]; Console.WriteLine("Getting user top scores..."); foreach (var play in getJsonFromApi($"get_user_best?k={Key}&u={ProfileName}&m={Ruleset}&limit=100")) { string beatmapID = play.beatmap_id; string cachePath = Path.Combine("cache", $"{beatmapID}.osu"); if (!File.Exists(cachePath)) { Console.WriteLine($"Downloading {beatmapID}.osu..."); new FileWebRequest(cachePath, $"{base_url}/osu/{beatmapID}").Perform(); } Mod[] mods = ruleset.ConvertFromLegacyMods((LegacyMods)play.enabled_mods).ToArray(); var working = new ProcessorWorkingBeatmap(cachePath, (int)play.beatmap_id); var scoreInfo = new ScoreInfo { Ruleset = ruleset.RulesetInfo, MaxCombo = play.maxcombo, Mods = mods, Statistics = new Dictionary <HitResult, int> { { HitResult.Perfect, (int)play.countgeki }, { HitResult.Great, (int)play.count300 }, { HitResult.Good, (int)play.countkatu }, { HitResult.Ok, (int)play.count100 }, { HitResult.Meh, (int)play.count50 }, { HitResult.Miss, (int)play.countmiss } } }; var score = new ProcessorScoreDecoder(working).Parse(scoreInfo); var categoryAttribs = new Dictionary <string, double>(); OsuPerformanceCalculator calculator = (OsuPerformanceCalculator)ruleset.CreatePerformanceCalculator(working, score.ScoreInfo); double localPP = calculator.Calculate(categoryAttribs); var thisPlay = new UserPlayInfo { Beatmap = working.BeatmapInfo, LocalPP = localPP, MapCategoryAttribs = categoryAttribs, LivePP = play.pp, Mods = mods.Length > 0 ? mods.Select(m => m.Acronym).Aggregate((c, n) => $"{c}|{n}") : "", PlayMaxCombo = scoreInfo.MaxCombo, BeatmapMaxCombo = calculator.Attributes.MaxCombo, PlayAccuracy = scoreInfo.Accuracy, MissCount = scoreInfo.Statistics[HitResult.Miss] }; displayPlays.Add(thisPlay); } var localOrdered = displayPlays.OrderByDescending(p => p.LocalPP).ToList(); var liveOrdered = displayPlays.OrderByDescending(p => p.LivePP).ToList(); int index = 0; double totalLocalPP = localOrdered.Sum(play => Math.Pow(0.95, index++) * play.LocalPP); double totalLivePP = userData.pp_raw; index = 0; double nonBonusLivePP = liveOrdered.Sum(play => Math.Pow(0.95, index++) * play.LivePP); //todo: implement properly. this is pretty damn wrong. var playcountBonusPP = (totalLivePP - nonBonusLivePP); totalLocalPP += playcountBonusPP; double totalDiffPP = totalLocalPP - totalLivePP; for (int i = 0; i < localOrdered.Count; i++) { localOrdered[i].Position = i + 1; } if (SortColumnName != null) { localOrdered.Sort((s1, s2) => s2.MapCategoryAttribs[SortColumnName].CompareTo(s1.MapCategoryAttribs[SortColumnName])); } foreach (var playInfo in localOrdered) { if (playInfo.Beatmap.ToString().Length > max_name_length) { playInfo.MapName = "..." + playInfo.Beatmap.ToString().Substring(playInfo.Beatmap.ToString().Length - max_name_length); } else { playInfo.MapName = playInfo.Beatmap.ToString(); } } Grid grid = new Grid(); ExtraColumns = new string[] { "Aim", "Speed", "Accuracy" }; grid.Columns.Add(createColumns(10 + (ExtraColumns?.Length ?? 0))); grid.Children.Add( new Cell("#") { Align = Align.Center }, new Cell("beatmap"), new Cell("mods") { Align = Align.Center }, new Cell("live pp"), new Cell("acc") { Align = Align.Center }, new Cell("miss"), new Cell("combo") { Align = Align.Center } ); if (ExtraColumns != null) { foreach (var extraColumn in ExtraColumns) { grid.Children.Add(new Cell(extraColumn) { Align = Align.Center }); } } grid.Children.Add( new Cell("local pp"), new Cell("pp change"), new Cell("position change") ); grid.Children.Add(localOrdered.Select((item) => { List <Cell> cells = new List <Cell> { new Cell(item.Position) { Align = Align.Left }, new Cell($" {item.Beatmap.OnlineBeatmapID} - {item.MapName}"), new Cell(item.Mods) { Align = Align.Right }, new Cell($"{item.LivePP:F1}") { Align = Align.Right }, new Cell($"{item.PlayAccuracy * 100f:F2}" + " %") { Align = Align.Right }, new Cell($"{item.MissCount}") { Align = Align.Right }, new Cell($"{item.PlayMaxCombo}/{item.BeatmapMaxCombo}") { Align = Align.Right }, }; if (ExtraColumns != null) { cells.AddRange(ExtraColumns.Select(extraColumn => new Cell($"{item.MapCategoryAttribs[extraColumn]:F1}") { Align = Align.Right })); } cells.AddRange(new List <Cell> { new Cell($"{item.LocalPP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP - item.LivePP:F1}") { Align = Align.Right }, new Cell($"{liveOrdered.IndexOf(item) - localOrdered.IndexOf(item):+0;-0;-}") { Align = Align.Center }, } ); return(cells); })); OutputDocument(new Document( new Span($"User: {userData.username}"), "\n", new Span($"Live PP: {totalLivePP:F1} (including {playcountBonusPP:F1}pp from playcount)"), "\n", new Span($"Local PP: {totalLocalPP:F1} ({totalDiffPP:+0.0;-0.0;-})"), "\n", grid )); }