예제 #1
0
        private async Task NotifyEnd(Challenge.Challenge challenge, IAsyncEnumerable <RankedSolution> solutions)
        {
            var top = await solutions.Take(10).ToArrayAsync();

            var(othersCount, othersScore) = await solutions.Skip(10).Select(a => (1, a.Solution.Score)).AggregateAsync((0u, 0L), (a, b) => ((uint)(a.Item1 + b.Item1), a.Item1 + b.Score));

            EmbedBuilder embed = new EmbedBuilder {
                Title  = $"Competition `{challenge.Name}` Complete!",
                Color  = Color.Blue,
                Footer = new EmbedFooterBuilder().WithText("A Cylon Project")
            };

            if (top.Length == 0)
            {
                embed.Description = "Challenge ended with no entries.";
            }
            else
            {
                embed.Description = $"**{await _client.GetUserName(top[0].Solution.UserId)}** is victorious with a score of **{top[0].Solution.Score}**\n\n```{top[0].Solution.Yolol}```";

                var leaderboardStr = new StringBuilder();
                foreach (var item in top)
                {
                    leaderboardStr.AppendLine($"{item.Rank}. {await _client.GetUserName(item.Solution.UserId)} **{item.Solution.Score}**");
                }
                if (othersCount > 0)
                {
                    leaderboardStr.AppendLine($"\n{othersCount} other entrants scored {othersScore} combined points.");
                }

                embed.AddField("Leaderboard", leaderboardStr.ToString());
            }

            await SendEmbedToSubs(embed.Build());
        }
예제 #2
0
        private async Task UpdateLeaderboard(Challenge.Challenge challenge, IAsyncEnumerable <RankedSolution> solutions)
        {
            // Update trueskill
            await _skillUpdate.ApplyChallengeResults(challenge.Id);

            const uint maxScore = 10;
            var        score    = maxScore;

            // Enumerate through each group of equally ranked users
            var ranks = solutions.GroupBy(a => a.Rank).OrderBy(a => a.Key);

            await foreach (var rank in ranks)
            {
                // Award all users at the same rank some points
                var count = 0;
                await foreach (var solution in rank)
                {
                    count++;
                    await _leaderboard.AddScore(solution.Solution.UserId, score *(uint)challenge.Difficulty);
                }

                // If there was only one user in the top rank, award them a bonus
                if (count == 1 && score == maxScore)
                {
                    await _leaderboard.AddScore((await rank.SingleAsync()).Solution.UserId, (uint)challenge.Difficulty);
                }

                // Award at least one point to every entrant
                if (score > 1)
                {
                    score--;
                }
            }

            // Find the smallest solution, if there's only one of them (i.e. no tie for smallest) award a bonus point
            var smallestGroup = await solutions.GroupBy(a => a.Solution.Yolol.Length).AggregateAsync((a, b) => a.Key < b.Key ? a : b);

            if (await smallestGroup.CountAsync() == 1)
            {
                await _leaderboard.AddScore((await smallestGroup.FirstAsync()).Solution.UserId, (uint)challenge.Difficulty);
            }
        }
        public async Task <(Success?, Failure?)> Verify(Challenge.Challenge challenge, string yolol)
        {
            await Task.CompletedTask;

            IScore scoreMode = challenge.ScoreMode switch {
                ScoreMode.BasicScoring => new BasicScoring(),
                ScoreMode.Approximate => new ApproximateScoring(),
                ScoreMode.Unknown => throw new InvalidOperationException("Cannot use `Unknown` score mode (challenge is broken - contact Martin#2468)"),
                      _ => throw new NotImplementedException($"Score mode `{challenge.ScoreMode}` is not implemented")
            };

            // Retrieve the test cases for the challenge
            var(inputs, outputs) = GetTests(challenge);

            // Check input program fits within 20x70
            var lines = yolol.Split("\n");

            if (lines.Length > 20 || lines.Any(l => l.Length > 70))
            {
                return(null, new Failure(FailureType.ProgramTooLarge, null));
            }

            // parse the program
            var result = Parser.ParseProgram(yolol);

            if (!result.IsOk)
            {
                return(null, new Failure(FailureType.ParseFailed, result.Err.ToString()));
            }
            var entry = result.Ok;

            // Get the variable which the program uses to indicate it is ready to move to the next round
            var state = new MachineState(new DefaultValueDeviceNetwork());
            var done  = state.GetVariable($":{challenge.CheckIndicator}");

            // Begin counting how long it takes to verify (for profiling purposes)
            var timer = new Stopwatch();

            timer.Start();

            // Run through test cases one by one
            var overflowIters = _config.MaxItersOverflow;
            var totalRuntime  = 0u;
            var pc            = 0;

            for (var i = 0; i < Math.Min(inputs.Count, outputs.Count); i++)
            {
                // Set inputs
                var input = inputs[i];
                foreach (var(key, value) in input)
                {
                    state.GetVariable($":{key}").Value = value;
                }

                // Clear completion indicator
                done.Value = 0;

                // Run lines until completion indicator is set or execution time limit is exceeded
                var limit = 0;
                while (!done.Value.ToBool())
                {
                    // Check if this test has exceed it's time limit
                    if (limit++ > _config.MaxTestIters)
                    {
                        //If so use iterations from the overflow pool
                        overflowIters--;

                        //Once the overflow pool is empty too, fail
                        if (overflowIters <= 0)
                        {
                            return(null, new Failure(FailureType.RuntimeTooLong, null));
                        }
                    }

                    totalRuntime++;
                    try
                    {
                        // If line if blank, just move to the next line
                        if (pc >= entry.Lines.Count)
                        {
                            pc++;
                        }
                        else
                        {
                            pc = entry.Lines[pc].Evaluate(pc, state);
                        }
                    }
                    catch (ExecutionException)
                    {
                        pc++;
                    }

                    // loop around if program counter goes over max
                    if (pc >= 20)
                    {
                        pc = 0;
                    }
                }

                // Check this test case with the current scoring mode
                var scoreFailure = scoreMode.CheckCase(input, outputs[i], state);
                if (scoreFailure != null)
                {
                    return(null, scoreFailure);
                }
            }

            Console.WriteLine($"Verified {totalRuntime} ticks, {timer.ElapsedMilliseconds}ms runtime");

            // Calculate score
            var codeLength = yolol.Replace("\n", "").Length;
            var score      = scoreMode.FinalizeScore(
                (uint)inputs.Count,
                totalRuntime,
                codeLength
                );

            return(new Success((uint)score, (uint)totalRuntime, (uint)codeLength), null);
        }
 private static (IReadOnlyList <IReadOnlyDictionary <string, Value> >, IReadOnlyList <IReadOnlyDictionary <string, Value> >) GetTests(Challenge.Challenge challenge)
 {
     if (challenge.ShuffleTests)
     {
         var shuffled = challenge.Inputs.Zip(challenge.Outputs).Shuffle().ToArray();
         return(
             shuffled.Select(a => a.First).ToArray(),
             shuffled.Select(a => a.Second).ToArray()
             );
     }
     else
     {
         return(challenge.Inputs, challenge.Outputs);
     }
 }
예제 #5
0
 private async Task NotifyStart(Challenge.Challenge challenge)
 {
     await SendEmbedToSubs(challenge.ToEmbed().Build());
 }
예제 #6
0
        public async Task <(Success?, Failure?)> Verify(Challenge.Challenge challenge, string yolol)
        {
            await Task.CompletedTask;

            if (challenge.ScoreMode != Challenge.ScoreMode.BasicScoring)
            {
                throw new NotImplementedException($"Score mode `{challenge.ScoreMode}` is not implemented");
            }

            // Retrieve the test cases for the challenge
            var(inputs, outputs) = GetTests(challenge);

            // Check input program is 20x70
            var lines = yolol.Split("\n");

            if (lines.Length > 20 || lines.Any(l => l.Length > 70))
            {
                return(null, new Failure(FailureType.ProgramTooLarge, null));
            }

            // parse the entry program
            var result = Parser.ParseProgram(yolol);

            if (!result.IsOk)
            {
                return(null, new Failure(FailureType.ParseFailed, result.Err.ToString()));
            }

            var entry = result.Ok;

            // Get the variable which the program uses to indicate it is ready to move to the next round
            var state = new MachineState(new DefaultValueDeviceNetwork());
            var done  = state.GetVariable($":{challenge.CheckIndicator}");

            // Begin counting how long it takes to verify
            var timer = new Stopwatch();

            timer.Start();

            // Run through test cases one by one
            var overflowIters = _config.MaxItersOverflow;
            var totalRuntime  = 0u;
            var pc            = 0;

            for (var i = 0; i < Math.Min(inputs.Count, outputs.Count); i++)
            {
                // Set inputs
                var input = inputs[i];
                foreach (var(key, value) in input)
                {
                    state.GetVariable($":{key}").Value = value;
                }

                // Clear completion indicator
                done.Value = 0;

                // Run lines until completion indicator is set or execution time limit is exceeded
                var limit = 0;
                while (!done.Value.ToBool())
                {
                    // Check if this test has exceed it's time limit
                    if (limit++ > _config.MaxTestIters)
                    {
                        //If so use iterations from the overflow pool
                        overflowIters--;

                        //Once the overflow pool is empty too, fail
                        if (overflowIters <= 0)
                        {
                            return(null, new Failure(FailureType.RuntimeTooLong, null));
                        }
                    }

                    totalRuntime++;
                    try
                    {
                        // If line if blank, just move to the next line
                        if (pc >= entry.Lines.Count)
                        {
                            pc++;
                        }
                        else
                        {
                            pc = entry.Lines[pc].Evaluate(pc, state);
                        }
                    }
                    catch (ExecutionException)
                    {
                        pc++;
                    }

                    // loop around if program counter goes over max
                    if (pc >= 20)
                    {
                        pc = 0;
                    }
                }

                // Check outputs
                foreach (var(key, value) in outputs[i])
                {
                    var v = state.GetVariable($":{key}");
                    if ((v.Value != value).ToBool())
                    {
                        var ii = string.Join(",", input.Select(b => $"`:{b.Key}={b.Value.ToHumanString()}`"));
                        var oo = string.Join(",", outputs[i].Select(b => $"`:{b.Key}={b.Value.ToHumanString()}`"));

                        return(null, new Failure(FailureType.IncorrectResult, $"For inputs {ii} expected outputs {oo}, got `{v.Value.ToHumanString()}` for `:{key}`"));
                    }
                }
            }

            Console.WriteLine($"Verified {totalRuntime} ticks, {timer.ElapsedMilliseconds}ms runtime");

            // Calculate score
            var codeLength = yolol.Replace("\n", "").Length;
            var score      = _score.Score(
                (uint)inputs.Count,
                totalRuntime,
                codeLength
                );

            return(new Success((uint)score, (uint)totalRuntime, (uint)codeLength), null);
        }
예제 #7
0
        public async Task <(Success?, Failure?)> Verify(Challenge.Challenge challenge, string yolol)
        {
            await Task.CompletedTask;

            IScore scoreMode = challenge.ScoreMode switch {
                ScoreMode.BasicScoring => new BasicScoring(),
                ScoreMode.Approximate => new ApproximateScoring(),
                ScoreMode.Unknown => throw new InvalidOperationException("Cannot use `Unknown` score mode (challenge is broken - contact Martin#2468)"),
                      _ => throw new NotImplementedException($"Score mode `{challenge.ScoreMode}` is not implemented (contact Martin#2468)")
            };

            if (challenge.Chip == YololChip.Unknown)
            {
                throw new InvalidOperationException("Cannot submit to a challenge with `YololChip.Unknown` (challenge is broken - contact Martin#2468)");
            }

            if (!challenge.Intermediate.IsOk)
            {
                throw new InvalidOperationException("Cannot submit to a challenge with broken intermediate code (challenge is broken - contact Martin#2468)");
            }

            // Check input program fits within 20x70
            var lines = yolol.Split("\n");

            if (lines.Length > 20 || lines.Any(l => l.Length > 70))
            {
                return(null, new Failure(FailureType.ProgramTooLarge, null, null));
            }

            // parse the program
            var parsed = Parser.ParseProgram(yolol);

            if (!parsed.IsOk)
            {
                return(null, new Failure(FailureType.ParseFailed, parsed.Err.ToString(), null));
            }

            // Verify that code is allowed on the given chip level
            if (challenge.Chip != YololChip.Professional)
            {
                var fail = CheckChipLevel(challenge.Chip, parsed.Ok);
                if (fail != null)
                {
                    return(null, fail);
                }
            }

            // Prepare a machine state for execution.
            // Two states are needed - one for user code and one for code supplied by the challenge
            var executionsContexts = (await _executor.Prepare(new[] { parsed.Ok, challenge.Intermediate.Ok }, $":{challenge.CheckIndicator}")).ToList();
            var stateUser          = executionsContexts[0];
            var stateChallenge     = executionsContexts[1];

            // Retrieve the test cases for the challenge
            var(inputs, outputs) = GetTests(challenge);

            // Begin counting how long it takes to verify (for profiling purposes)
            var timer = new Stopwatch();

            timer.Start();

            // Run through test cases one by one
            var overflowIters = (long)MaxItersOverflow;

            for (var i = 0; i < Math.Max(inputs.Count, outputs.Count); i++)
            {
                // Set inputs in user execution state
                var input      = SetInputs(i < inputs.Count ? inputs[i] : new Dictionary <string, Value>(), stateUser);
                var output     = outputs[i];
                var savedState = stateUser.Serialize(output);

                // Check realtime timeout
                if (timer.Elapsed.TotalMilliseconds > TimeoutMillisecondsAll)
                {
                    Console.WriteLine($"Timed out verification: {stateUser.TotalLinesExecuted} ticks, {timer.ElapsedMilliseconds}ms runtime");
                    return(null, new Failure(FailureType.RuntimeTooLongMilliseconds, $"Execution Timed Out (executed {i} test cases in {timer.Elapsed.TotalMilliseconds}ms)", savedState));
                }

                // Run the user code until completion
                Failure?failure;
                (failure, overflowIters) = await RunToDone(stateUser, MaxTestIters, i, inputs.Count, overflowIters, savedState);

                if (failure != null)
                {
                    return(null, failure);
                }

                // Set the challenge inputs _and_ outputs into the challenge state
                SetInputs(input, stateChallenge, "input_");
                SetInputs(outputs[i], stateChallenge, "output_");

                // Run the challenge code
                (failure, _) = await RunToDone(stateChallenge, MaxTestIters, 0, 0, MaxItersOverflow, savedState);

                if (failure != null)
                {
                    return(null, new Failure(FailureType.ChallengeCodeFailed, failure.Hint, savedState));
                }

                // Check if the challenge code has forced a failure
                var forceFail = stateChallenge.TryGet(new VariableName(":fail"));
                if (forceFail?.Type == Yolol.Execution.Type.String && forceFail.Value.String.Length != 0)
                {
                    return(null, new Failure(FailureType.ChallengeForceFail, forceFail.Value.String.ToString(), savedState));
                }

                // Check this test case with the current scoring mode
                var scoreFailure = scoreMode.CheckCase(input, outputs[i], stateUser, savedState);
                if (scoreFailure != null)
                {
                    return(null, scoreFailure);
                }
            }

            Console.WriteLine($"Verified {stateUser.TotalLinesExecuted} ticks, {timer.ElapsedMilliseconds}ms runtime");

            // Calculate score
            var codeLength = yolol.Replace("\n", "").Length;
            var score      = scoreMode.FinalizeScore(
                (uint)Math.Max(inputs.Count, outputs.Count),
                (uint)stateUser.TotalLinesExecuted,
                codeLength
                );

            return(new Success(score, (uint)stateUser.TotalLinesExecuted, (uint)codeLength, scoreMode.Hint, (uint)challenge.Inputs.Count), null);
        }