public void Should_Be_Awarded_No_Match_Points(SwissStatisticsProvider sut, IEnumerable<Round> rounds, Player A, Player B)
                {
                    var actual = sut
                        .GetMatchPoints(rounds, A);

                    Assert.Equal(0.0m, actual);
                }
Пример #2
0
        public IEnumerable <Round> BuildRounds(SwissTournamentContext context, int?throughRoundNumber = null)
        {
            // Round 1 pairing is completely random.

            // Subsequent rounds have players grouped by match points and randomly
            // ordered within that group.

            // If there's an uneven number of players, the player with the lowest
            // match points who has not yet had a bye is given a bye.

            // Then, going from the highest match points to the lowest, the topmost
            // player is paired with the second topmost player. If they've had a match
            // before, the third topmost player is paired, continuing on until a
            // player is found that has not had a match with the topmost. Both players
            // are then removed from the pool of  available players and the process
            // repeats.

            // We accomplish random pairing in round 1 by all players having zero
            // match points and using the above algorithm.

            // Byes are assigned first, then pairings. I haven't been able to devise
            // a scenario where two players end up getting paired twice because a player
            // was given a bye, but this system does introduce the potential for it.

            var roundNumber = throughRoundNumber ?? context.ActiveRound ?? 1;
            var roundSeed   = context.TournamentSeed ^ roundNumber;
            var roundRng    = new Random(roundSeed);

            var previousRounds = roundNumber == 1
                                ? Enumerable.Empty <Round>()
                                : BuildRounds(context, roundNumber - 1);

            var pairingPool = context
                              .Players
                              .Where(player => player.RoundDropped == null || player.RoundDropped > roundNumber)
                              .Select(player => new
            {
                Player      = player,
                MatchPoints = StatisticsProvider.GetMatchPoints(previousRounds, player),
                Randomizer  = roundRng.Next(),
            })
                              .OrderByDescending(o => o.MatchPoints)
                              .ThenBy(o => o.Randomizer)
                              .Select(o => o.Player)
                              .ToArray()                // Flatten to ensure the RNG is only called once per player
                              .AsEnumerable();

            var roundMatchResults = context
                                    .MatchResults
                                    .Where(mr => mr.RoundNumber == roundNumber)
                                    .ToArray();

            var previousByes = previousRounds
                               .SelectMany(round => round.Matches)
                               .Where(match => match.Players.Count() == 1)
                               .SelectMany(match => match.Players)
                               .ToArray();

            var previousPairings = previousRounds
                                   .SelectMany(round => round.Matches)
                                   .Select(match => match
                                           .Players
                                           .OrderBy(player => player.Identifier))
                                   .ToArray();

            // Find the first match set that doesn't include a rematch
            var matchSets        = GenerateMatchSets(pairingPool, previousByes, roundMatchResults);
            var selectedMatchSet = matchSets
                                   .Where(matchSet => matchSet
                                          .Select(match => match
                                                  .Players
                                                  .OrderBy(player => player.Identifier)
                                                  )
                                          .All(players => !previousPairings
                                               .Any(previous => previous
                                                    .SequenceEqual(players)
                                                    )
                                               )
                                          )
                                   .FirstOrDefault()
                                   ?? matchSets.First();

            return(previousRounds
                   .Concat(new Round(
                               roundNumber,
                               selectedMatchSet
                               .OrderBy(match => match.Number)
                               .ToArray()
                               ))
                   .ToArray());
        }