private async Task DisplayChallengeLeaderboard(Challenge challenge) { // Get the top N solutions var top5 = _solutions.GetSolutions(challenge.Id, 20).Select(a => new RankInfo(a.Solution.UserId, a.Rank, a.Solution.Score)); // Get your own rank var self = await _solutions.GetRank(challenge.Id, Context.User.Id); RankInfo?selfRank = null; if (self.HasValue) { selfRank = new RankInfo(self.Value.Solution.UserId, self.Value.Rank, self.Value.Solution.Score); } await ReplyAsync(embed : await FormatLeaderboard(top5, selfRank, challenge)); }
public async Task ApplyChallengeResults(ulong challengeId) { // Convert all solutions for the current challenge into a set of relevant data var teamRanks = await _solutions.GetSolutions(challengeId, uint.MaxValue) .AsAsyncEnumerable() .OrderBy(a => a.Rank) .SelectAwait(async a => new { player = a.Solution.UserId, rank = a.Rank, rating = Convert(await GetOrAddRating(a.Solution.UserId)), }) .Select(a => new { team = new Dictionary <ulong, Rating> { { a.player, a.rating } }, a.rank }).ToArrayAsync(); // Trueskill can't be applied if there were less than 2 entrants if (teamRanks.Length > 1) { // Extract the data out of that into separate collections var teams = teamRanks.Select(a => a.team).ToArray(); var ranks = teamRanks.Select(a => (int)a.rank).ToArray(); // Calculate trueskill ratings var results = TrueSkillCalculator.CalculateNewRatings( GameInfo.DefaultGameInfo, teams, ranks ); // Update database with new ratings foreach (var(key, rating) in results) { await _ratings.SetRating(key, rating.Mean, rating.StandardDeviation); } } // Decay rank of all players who did not participate in this challenge await _ratings.Decay(4); }
public async Task ShowLeaderboard(LeaderboardType type = LeaderboardType.Global) { if (type == LeaderboardType.Global) { var top = _leaderboard.GetTopRank(15); await ReplyAsync(embed : await FormatLeaderboard(top)); } else if (type == LeaderboardType.Current) { var challenge = await _challenges.GetCurrentChallenge(); if (challenge == null) { await ReplyAsync("There is no currently running challenge"); return; } // Get the top N solutions var top5 = _solutions.GetSolutions(challenge.Id, 20).Select(a => new RankInfo(a.Solution.UserId, a.Rank, a.Solution.Score)); // Get your own rank var self = await _solutions.GetRank(challenge.Id, Context.User.Id); RankInfo?selfRank = null; if (self.HasValue) { selfRank = new RankInfo(self.Value.Solution.UserId, self.Value.Rank, self.Value.Solution.Score); } await ReplyAsync(embed : await FormatLeaderboard(top5, selfRank)); } else { throw new InvalidOperationException("Unknown leaderboard type"); } }
public async Task Start() { while (true) { State = SchedulerState.StartingChallenge; // Find the currently running challenge var current = await _challenges.GetCurrentChallenge(); // If there is no challenge running try to start a new one if (current == null) { Console.WriteLine("There is no current challenge - attempting to start a new one"); // Start the next challenge, if there isn't one wait a while var next = await _challenges.StartNext(); if (next == null) { Console.WriteLine("No challenges available, waiting for a while..."); State = SchedulerState.WaitingNoChallengesInPool; await Task.Delay(TimeSpan.FromMinutes(1)); continue; } Console.WriteLine($"Starting new challenge {next.Name}"); // Notify all subscribed channels about the new challenge await NotifyStart(next); current = next; } else { Console.WriteLine($"{current.Name} challenge is currently running"); } //There is a challenge running, wait until the end time or until someone externally pokes us awake var endTime = current.EndTime; while (endTime != null && endTime > DateTime.UtcNow) { var delay = endTime.Value - DateTime.UtcNow; if (delay > TimeSpan.Zero) { State = SchedulerState.WaitingChallengeEnd; await Task.WhenAny(_poker.WaitAsync(), Task.Delay(delay)); } } // If endtime is after now then something else poked the scheduler awake, reset scheduler logic if (endTime != null && endTime > DateTime.UtcNow) { continue; } State = SchedulerState.EndingChallenge; // Finish the current challenge await _challenges.EndCurrentChallenge(); // Transfer scores to leaderboard await UpdateLeaderboard(current, _solutions.GetSolutions(current.Id, uint.MaxValue)); // Get the leaderboard for the challenge that just finished and notify everyone await NotifyEnd(current, _solutions.GetSolutions(current.Id, uint.MaxValue)); // Wait for a cooldown period State = SchedulerState.WaitingCooldown; await Task.WhenAny(_poker.WaitAsync(), Task.Delay(TimeSpan.FromHours(23))); } }
public async Task Rescore(string id) { var uid = BalderHash.BalderHash32.Parse(id); if (!uid.HasValue) { await ReplyAsync($"Cannot parse `{id}` as a challenge ID"); return; } var challenges = await _challenges.GetChallenges(id : uid.Value.Value, includeUnstarted : true).ToArrayAsync(); if (challenges.Length == 0) { await ReplyAsync("Cannot find challenge with given ID"); return; } if (challenges.Length > 1) { await ReplyAsync("Found more than one challenge, please disambiguate:"); foreach (var challenge in challenges) { await ReplyAsync($" - {challenge.Name} (`{((uint)challenge.Id).BalderHash()}`)"); await Task.Delay(10); } return; } var c = challenges.Single(); await ReplyAsync($" - {c.Name} (`{((uint)c.Id).BalderHash()}`)"); await ReplyAsync("Rescore this challenge (yes/no)?"); var confirm = (await NextMessageAsync(timeout: TimeSpan.FromMilliseconds(-1))).Content; if (!confirm.Equals("yes", StringComparison.OrdinalIgnoreCase)) { await ReplyAsync("Not rescoring anything"); return; } var solutions = await _solutions.GetSolutions(c.Id, uint.MaxValue).ToArrayAsync(); const string pbarHeader = "Rescoring: "; var progress = await ReplyAsync(pbarHeader); var totalTicks = 0l; var failures = 0; var results = new List <RescoreItem>(solutions.Length); for (var i = 0; i < solutions.Length; i++) { await progress.ModifyAsync(a => a.Content = $"{pbarHeader} {i}/{solutions.Length}"); var s = solutions[i]; var(success, failure) = await _verification.Verify(c, s.Solution.Yolol); if (success != null) { totalTicks += success.Iterations; results.Add(new RescoreItem( s.Solution, new Solution(s.Solution.ChallengeId, s.Solution.UserId, success.Score, s.Solution.Yolol) )); } else if (failure != null) { failures++; results.Add(new RescoreItem( s.Solution, new Solution(s.Solution.ChallengeId, s.Solution.UserId, s.Solution.Score, s.Solution.Yolol), failure.Hint )); } else { throw new InvalidOperationException("Verification did not return success or failure"); } await Task.Delay(100); } await progress.ModifyAsync(a => a.Content = $"Completed rescoring ({totalTicks} total ticks)"); if (failures > 0) { await ReplyAsync($"{failures} solutions failed to verify"); } var report = new StringBuilder(); report.AppendLine($"Rescoring `{c.Name}`"); foreach (var rescore in results) { report.AppendLine($"{await UserName(rescore.Before.UserId)}: {rescore.Before.Score} => {rescore.After.Score}"); } await ReplyAsync(report.ToString()); await ReplyAsync("Apply rescoring to this challenge (yes/no)?"); var confirm2 = (await NextMessageAsync(timeout: TimeSpan.FromMilliseconds(-1))).Content; if (!confirm2.Equals("yes", StringComparison.OrdinalIgnoreCase)) { await ReplyAsync("Not applying rescoring"); return; } foreach (var rescore in results) { await _solutions.SetSolution(rescore.After); } await ReplyAsync("Done."); }