예제 #1
0
        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);
        }
예제 #3
0
        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");
            }
        }
예제 #4
0
        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)));
            }
        }
예제 #5
0
        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.");
        }