Beispiel #1
0
                    public void One_Player_Should_Get_A_Bye(SwissRankingEngine sut, Player A, Player B, Player C)
                    {
                        // Seed 7357 = Round 1 pairing order { B, C, A }
                        var context = new SwissTournamentContext(
                            7357,
                            TournamentState.TournamentCreated,
                            new[] { A, B, C },
                            null,
                            null);

                        var actual = sut.GetPairings(context);

                        Assert.Equal(actual.Count(), 2);

                        var firstPairing = actual.ElementAt(0);

                        Assert.Equal(2, firstPairing.Players.Count());
                        Assert.Contains(B, firstPairing.Players);
                        Assert.Contains(C, firstPairing.Players);

                        var byePairing = actual.ElementAt(1);

                        Assert.Equal(1, byePairing.Players.Count());
                        Assert.Contains(A, byePairing.Players);
                    }
Beispiel #2
0
                    public void All_Players_Should_Be_Paired(SwissRankingEngine sut, Player A, Player B, Player C, Player D)
                    {
                        // Seed 7357 = Round 1 pairing order { B, C, A, D }
                        var context = new SwissTournamentContext(
                            7357,
                            TournamentState.TournamentCreated,
                            new[] { A, B, C, D },
                            null,
                            null);

                        var actual = sut.GetPairings(context);

                        Assert.Equal(actual.Count(), 2);

                        var firstPairing = actual.ElementAt(0);

                        Assert.Equal(2, firstPairing.Players.Count());
                        Assert.Contains(B, firstPairing.Players);
                        Assert.Contains(C, firstPairing.Players);

                        var secondPairing = actual.ElementAt(1);

                        Assert.Equal(2, secondPairing.Players.Count());
                        Assert.Contains(A, secondPairing.Players);
                        Assert.Contains(D, secondPairing.Players);
                    }
        public void Should_Generate_All_Possible_Pairings()
        {
            var generator = new SwissRankingEngine(new SwissStatisticsProvider());

            var players = Enumerable
                .Range(0, 26)
                .Select(number  => new String(new[] { (char)('a' + number) }))
                .Select(name => new Player(name, null));

            // Careful with the playerCount. 12 will take almost a minute to execute.
            for(int playerCount = 2; playerCount <= 10; playerCount += 2)
            {
                var matchSets = generator.GeneratePairedMatches(players.Take(playerCount), Enumerable.Empty<MatchResult>(), 1);

                var uniquePairings = matchSets
                    .Select(matchSet => matchSet
                        .Select(match => match
                            .Players
                            .OrderBy(player => player.Identifier)
                            .Select(player => player.Identifier)
                        )
                        .Select(playerIdentifiers => String.Join("-", playerIdentifiers))
                        .OrderBy(pairing => pairing)
                    )
                    .Select(playerIdentifiers => String.Join("|", playerIdentifiers))
                    .OrderBy(matchSetPairings => matchSetPairings)
                    .Distinct()
                    .ToArray();

                // Expected number of pairings is (2n - 1)!! where n = playerCount / 2
                // See http://en.wikipedia.org/wiki/Perfect_matching and http://en.wikipedia.org/wiki/Double_factorial for reasoning
                // See http://mathworld.wolfram.com/DoubleFactorial.html for the definition of (2n - 1)!! used here
                var expectedUniquePairings = (int)(Factorial(playerCount) / (Math.Pow(2, playerCount / 2) * Factorial(playerCount / 2)));
                var actualUniquePairings = uniquePairings.Count();
                Assert.Equal(expectedUniquePairings, actualUniquePairings);
            }
        }
        public void Should_Generate_All_Possible_Pairings()
        {
            var generator = new SwissRankingEngine(new SwissStatisticsProvider());

            var players = Enumerable
                          .Range(0, 26)
                          .Select(number => new String(new[] { (char)('a' + number) }))
                          .Select(name => new Player(name, null));

            // Careful with the playerCount. 12 will take almost a minute to execute.
            for (int playerCount = 2; playerCount <= 10; playerCount += 2)
            {
                var matchSets = generator.GeneratePairedMatches(players.Take(playerCount), Enumerable.Empty <MatchResult>(), 1);

                var uniquePairings = matchSets
                                     .Select(matchSet => matchSet
                                             .Select(match => match
                                                     .Players
                                                     .OrderBy(player => player.Identifier)
                                                     .Select(player => player.Identifier)
                                                     )
                                             .Select(playerIdentifiers => String.Join("-", playerIdentifiers))
                                             .OrderBy(pairing => pairing)
                                             )
                                     .Select(playerIdentifiers => String.Join("|", playerIdentifiers))
                                     .OrderBy(matchSetPairings => matchSetPairings)
                                     .Distinct()
                                     .ToArray();

                // Expected number of pairings is (2n - 1)!! where n = playerCount / 2
                // See http://en.wikipedia.org/wiki/Perfect_matching and http://en.wikipedia.org/wiki/Double_factorial for reasoning
                // See http://mathworld.wolfram.com/DoubleFactorial.html for the definition of (2n - 1)!! used here
                var expectedUniquePairings = (int)(Factorial(playerCount) / (Math.Pow(2, playerCount / 2) * Factorial(playerCount / 2)));
                var actualUniquePairings   = uniquePairings.Count();
                Assert.Equal(expectedUniquePairings, actualUniquePairings);
            }
        }
        public void It_Just_Freaking_Works()
        {
            // Yay happy path!

            var statisticsProvider  = new SwissStatisticsProvider();
            var rankingEngine       = new SwissRankingEngine(statisticsProvider);
            var contextBuilder      = new SwissTournamanetContextBuilder(statisticsProvider, rankingEngine);
            var commandEventHandler = new CommandEventHandler <SwissTournamentContext>(contextBuilder);

            var context = new SwissTournamentContext(0, TournamentState.None, null, null, null);

            // Create the tournament
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.CreateTournament.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Timestamp", new DateTimeOffset(1982, 7, 22, 01, 14, 22, TimeSpan.FromHours(-7)).ToString() }
            })
                );

            Assert.Equal(TournamentState.TournamentCreated, context.State);
            Assert.NotEqual(0, context.TournamentSeed);

            // Add players
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.AddPlayer.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "A" }
            })
                );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.AddPlayer.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "B" }
            })
                );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.AddPlayer.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "C" }
            })
                );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.AddPlayer.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "D" }
            })
                );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.AddPlayer.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "E" }
            })
                );

            Assert.Equal(TournamentState.TournamentStarted, context.State);
            Assert.NotEmpty(context.Players);

            var roundOnePairings = rankingEngine.GetPairings(context);

            // Record match results for round 1
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "A" },
                { "Wins", "2" },
            })
                );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "B" },
                { "Wins", "1" },
            })
                );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "D" },
            })
                );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "E" },
                { "Draws", "1" },
            })
                );

            // End round 1
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.EndRound.ToString(),
                    properties: new Dictionary <string, string>
            {
            })
                );

            // Get standings as of round 1. If we include round 2, we get the bye points immediately.
            var roundOneStandings = rankingEngine.GetStandings(context, 1);
            var roundTwoPairings  = rankingEngine.GetPairings(context);

            // Record match results for round 2
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "A" },
                { "Draws", "1" },
            })
                );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "C" },
                { "Wins", "2" },
            })
                );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "D" },
                { "Wins", "1" },
            })
                );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "E" },
            })
                );

            // End round 2
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.EndRound.ToString(),
                    properties: new Dictionary <string, string>
            {
            })
                );

            var roundTwoStandings = rankingEngine.GetStandings(context, 2);

            // Drop a player
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RemovePlayer.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "E" },
                { "Wins", "1" },
            })
                );

            // Record match results for round 3
            var roundThreePairings = rankingEngine.GetPairings(context);

            // Record match results for round 3
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "A" },
                { "Wins", "1" },
            })
                );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "B" },
                { "Draws", "1" },
            })
                );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "C" },
            })
                );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary <string, string>
            {
                { "Player", "D" },
                { "Wins", "2" },
            })
                );

            // End round 3 and the tournament
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.EndRound.ToString(),
                    properties: new Dictionary <string, string>
            {
            })
                );

            var finalStandings = rankingEngine.GetStandings(context);
        }
                    public void One_Player_Should_Get_A_Bye(SwissRankingEngine sut, Player A, Player B, Player C)
                    {
                        // Seed 7357 = Round 1 pairing order { B, C, A }
                        var context = new SwissTournamentContext(
                            7357,
                            TournamentState.TournamentCreated,
                            new[] { A, B, C },
                            null,
                            null);

                        var actual = sut.GetPairings(context);

                        Assert.Equal(actual.Count(), 2);

                        var firstPairing = actual.ElementAt(0);
                        Assert.Equal(2, firstPairing.Players.Count());
                        Assert.Contains(B, firstPairing.Players);
                        Assert.Contains(C, firstPairing.Players);

                        var byePairing = actual.ElementAt(1);
                        Assert.Equal(1, byePairing.Players.Count());
                        Assert.Contains(A, byePairing.Players);
                    }
                    public void All_Players_Should_Be_Paired(SwissRankingEngine sut, Player A, Player B, Player C, Player D)
                    {
                        // Seed 7357 = Round 1 pairing order { B, C, A, D }
                        var context = new SwissTournamentContext(
                            7357,
                            TournamentState.TournamentCreated,
                            new[] { A, B, C, D },
                            null,
                            null);

                        var actual = sut.GetPairings(context);

                        Assert.Equal(actual.Count(), 2);

                        var firstPairing = actual.ElementAt(0);
                        Assert.Equal(2, firstPairing.Players.Count());
                        Assert.Contains(B, firstPairing.Players);
                        Assert.Contains(C, firstPairing.Players);

                        var secondPairing = actual.ElementAt(1);
                        Assert.Equal(2, secondPairing.Players.Count());
                        Assert.Contains(A, secondPairing.Players);
                        Assert.Contains(D, secondPairing.Players);
                    }
        public void It_Just_Freaking_Works()
        {
            // Yay happy path!

            var statisticsProvider = new SwissStatisticsProvider();
            var rankingEngine = new SwissRankingEngine(statisticsProvider);
            var contextBuilder = new SwissTournamanetContextBuilder(statisticsProvider, rankingEngine);
            var commandEventHandler = new CommandEventHandler<SwissTournamentContext>(contextBuilder);

            var context = new SwissTournamentContext(0, TournamentState.None, null, null, null);

            // Create the tournament
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.CreateTournament.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Timestamp", new DateTimeOffset(1982, 7, 22, 01, 14, 22, TimeSpan.FromHours(-7)).ToString() }
                    })
            );

            Assert.Equal(TournamentState.TournamentCreated, context.State);
            Assert.NotEqual(0, context.TournamentSeed);

            // Add players
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.AddPlayer.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "A" }
                    })
            );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.AddPlayer.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "B" }
                    })
            );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.AddPlayer.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "C" }
                    })
            );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.AddPlayer.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "D" }
                    })
            );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.AddPlayer.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "E" }
                    })
            );

            Assert.Equal(TournamentState.TournamentStarted, context.State);
            Assert.NotEmpty(context.Players);

            var roundOnePairings = rankingEngine.GetPairings(context);

            // Record match results for round 1
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "A" },
                        { "Wins", "2" },
                    })
            );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "B" },
                        { "Wins", "1" },
                    })
            );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "D" },
                    })
            );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "E" },
                        { "Draws", "1" },
                    })
            );

            // End round 1
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.EndRound.ToString(),
                    properties: new Dictionary<string, string>
                    {
                    })
            );

            // Get standings as of round 1. If we include round 2, we get the bye points immediately.
            var roundOneStandings = rankingEngine.GetStandings(context, 1);
            var roundTwoPairings = rankingEngine.GetPairings(context);

            // Record match results for round 2
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "A" },
                        { "Draws", "1" },
                    })
            );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "C" },
                        { "Wins", "2" },
                    })
            );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "D" },
                        { "Wins", "1" },
                    })
            );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "E" },
                    })
            );

            // End round 2
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.EndRound.ToString(),
                    properties: new Dictionary<string, string>
                    {
                    })
            );

            var roundTwoStandings = rankingEngine.GetStandings(context, 2);

            // Drop a player
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RemovePlayer.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "E" },
                        { "Wins", "1" },
                    })
            );

            // Record match results for round 3
            var roundThreePairings = rankingEngine.GetPairings(context);

            // Record match results for round 3
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "A" },
                        { "Wins", "1" },
                    })
            );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "B" },
                        { "Draws", "1" },
                    })
            );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "C" },
                    })
            );

            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.RecordMatchResults.ToString(),
                    properties: new Dictionary<string, string>
                    {
                        { "Player", "D" },
                        { "Wins", "2" },
                    })
            );

            // End round 3 and the tournament
            context = commandEventHandler.ProcessCommand(
                context: context,
                commandEvent: new CommandEvent(
                    sequence: 0,
                    aggregateId: "T1",
                    name: TournamentCommand.EndRound.ToString(),
                    properties: new Dictionary<string, string>
                    {
                    })
            );

            var finalStandings = rankingEngine.GetStandings(context);
        }