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 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"); } }
private async Task SubmitAndReply(Success verification, Solution submission, bool save) { var previous = await _solutions.GetSolution(Context.User.Id, submission.ChallengeId); var avgIters = verification.Iterations / (float)verification.TotalTests; var reply = $"You score {verification.Score} points using {verification.Length} chars and {avgIters:0.###} ticks per test case ({verification.Iterations} total ticks)."; // Handle submitting something that scores worse than your existing solution if (previous.HasValue && previous.Value.Score > submission.Score) { await ReplyAsync($"{reply} Less than your current best of {previous.Value.Score}"); if (verification.Hint != null) { await ReplyAsync(verification.Hint); } return; } // This submission was better than previous, but saving is not enabled (i.e. submitting to an old competition). Reply with score and early out. if (!save) { await ReplyAsync(reply); if (verification.Hint != null) { await ReplyAsync(verification.Hint); } return; } // Submission was better than previous and saving is enabled. // We might need to do a rank alert! // Get the current top solution(s) var topSolutionsBefore = await _solutions.GetTopRank(submission.ChallengeId).Select(a => a.Solution).ToListAsync(); var topUsersBefore = topSolutionsBefore.Select(a => a.UserId).ToList(); // Save this solution and reply to user with result await _solutions.SetSolution(submission); var rank = await _solutions.GetRank(submission.ChallengeId, Context.User.Id); await ReplyAsync($"{reply} You are currently rank {rank?.Rank} for this challenge."); if (verification.Hint != null) { await ReplyAsync(verification.Hint); } // There should always be a rank after the call to `SetSolution`. if there isn't just early out here ¯\_(ツ)_/¯ if (!rank.HasValue) { return; } // If this is not the top ranking score, or there was no top ranking score before there is no need to put out a rank alert. if (rank.Value.Rank != 1 || topUsersBefore.Count == 0) { return; } var topScoreBefore = topSolutionsBefore[0].Score; // There are three possible rank alerts: // 1) User moved from below to above: "X takes rank #1 from Y" // 2) User moved from below to tie: "X ties for rank #1" // 3) User moved from tie to above: "X breaks a tie to take #1 from Y" // Create the embed to fill in with details of the rank alert later var embed = new EmbedBuilder { Title = "Rank Alert", Color = Color.Gold, Footer = new EmbedFooterBuilder().WithText("A Cylon Project") }; var self = await _client.GetUserName(Context.User.Id); // Case #1/#2 if (!topUsersBefore.Contains(Context.User.Id) && submission.Score >= topScoreBefore) { var prev = (await topUsersBefore.ToAsyncEnumerable().SelectAwait(async a => await _client.GetUserName(a)).ToArrayAsync()).Humanize("&"); if (submission.Score > topScoreBefore) { embed.Description = $"{self} takes rank #1 from {prev}!"; } else if (submission.Score == topScoreBefore) { embed.Description = $"{self} ties for rank #1"; } else { return; } } else if (topUsersBefore.Contains(Context.User.Id) && topUsersBefore.Count > 1 && submission.Score > topScoreBefore) { topUsersBefore.Remove(Context.User.Id); var prev = (await topUsersBefore.ToAsyncEnumerable().SelectAwait(async a => await _client.GetUserName(a)).ToArrayAsync()).Humanize("&"); embed.Description = $"{self} breaks a tie to take #1 from {prev}!"; } else { return; } // Broadcast embed out to all subscribed channels await _broadcast.Broadcast(embed.Build()).LastAsync(); }
public async Task SubmitSolution([Remainder] string input) { var code = input.ExtractYololCodeBlock(); if (code == null) { await ReplyAsync(@"Failed to parse a yolol program from message - ensure you have enclosed your solution in triple backticks \`\`\`like this\`\`\`"); return; } var challenge = await _challenges.GetCurrentChallenge(); if (challenge == null) { await ReplyAsync("There is no currently running challenge!"); return; } var(success, failure) = await _verification.Verify(challenge, code); if (failure != null) { var message = failure.Type switch { FailureType.ParseFailed => $"Code is not valid Yolol code: {failure.Hint}", FailureType.RuntimeTooLong => "Program took too long to produce a result", FailureType.IncorrectResult => $"Program produced an incorrect value! {failure.Hint}", FailureType.ProgramTooLarge => "Program is too large - it must be 20 lines by 70 characters per line", _ => throw new ArgumentOutOfRangeException() }; await ReplyAsync($"Verification failed! {message}."); } else if (success != null) { var solution = await _solutions.GetSolution(Context.User.Id, challenge.Id); if (solution.HasValue && success.Score < solution.Value.Score) { await ReplyAsync($"Verification complete! You score {success.Score} points. Less than your current best of {solution.Value.Score}"); } else { // Get the current top solution var topBefore = await _solutions.GetTopRank(challenge.Id).ToArrayAsync(); // Submit this solution await _solutions.SetSolution(new Solution(challenge.Id, Context.User.Id, success.Score, code)); var rank = await _solutions.GetRank(challenge.Id, Context.User.Id); var rankNum = uint.MaxValue; if (rank.HasValue) { rankNum = rank.Value.Rank; } await ReplyAsync($"Verification complete! You scored {success.Score} points. You are currently rank {rankNum} for this challenge."); // If this is the top ranking score, and there was a top ranking score before, and it wasn't this user: alert everyone if (rankNum == 1 && topBefore.Length > 0 && topBefore.All(a => a.Solution.UserId != Context.User.Id)) { var embed = new EmbedBuilder { Title = "Rank Alert", Color = Color.Gold, Footer = new EmbedFooterBuilder().WithText("A Cylon Project") }; var self = await _client.GetUserName(Context.User.Id); var prev = (await topBefore.ToAsyncEnumerable().SelectAwait(async a => await _client.GetUserName(a.Solution.UserId)).ToArrayAsync()).Humanize("&"); embed.Description = success.Score == topBefore[0].Solution.Score ? $"{self} ties for rank #1" : $"{self} takes rank #1 from {prev}!"; await _broadcast.Broadcast(embed.Build()); } } } else { throw new InvalidOperationException("Failed to verify solution (this is a bug, please contact @Martin#2468)"); } }