Beispiel #1
0
        public int GetTotalRounds(Tournament tournament)
        {
            if(tournament == null)
                throw new ArgumentNullException("tournament");

            return RoundManager.GetMaxRoundsForTournament(tournament);
        }
Beispiel #2
0
        public int? GetCurrentRoundNumber(Tournament tournament)
        {
            if(tournament == null)
                throw new ArgumentNullException("tournament");

            var roundStatus = Enumerable
                .Range(1, GetTotalRounds(tournament))
                .Select(roundNumber => new
                {
                    Number = roundNumber,
                    State = RoundManager.GetRoundState(tournament, roundNumber)
                });

            if(!roundStatus.Any())
                return null;

            var currentRound = roundStatus
                .SkipWhile(o => o.State >= RoundState.Final)
                .FirstOrDefault();

            if(currentRound == null || currentRound.State == RoundState.Invalid)
                return null;

            return currentRound.Number;
        }
        public StandingsResponse Create(Tournament tournament, int? roundNumber)
        {
            if(tournament == null)
                throw new ArgumentNullException("tournament");

            var playerStandings = tournament
                .Players
                .Select(player => new
                {
                    name = player.Name,
                    matchPoints = StatsProvider.GetMatchPoints(tournament, player, roundNumber),
                    matchWinPercentage = StatsProvider.GetMatchWinPercentage(tournament, player, roundNumber),
                    opponentsMatchWinPercentage = StatsProvider.GetOpponentsMatchWinPercentage(tournament, player, roundNumber),
                    gamePoints = StatsProvider.GetGamePoints(tournament, player, roundNumber),
                    gameWinPercentage = StatsProvider.GetGameWinPercentage(tournament, player, roundNumber),
                    opponentsGameWinPercentage = StatsProvider.GetOpponentsGameWinPercentage(tournament, player, roundNumber),
                })
                .OrderByDescending(o => o.matchPoints)
                .ThenByDescending(o => o.opponentsMatchWinPercentage)
                .ThenByDescending(o => o.gameWinPercentage)
                .ThenByDescending(o => o.opponentsGameWinPercentage)
                .Select((o, rank) => new PlayerStandingResponse(
                    rank: rank + 1,
                    playerName: o.name,
                    matchPoints: o.matchPoints,
                    matchWinPercentage: o.matchWinPercentage,
                    opponentsMatchWinPercentage: o.opponentsMatchWinPercentage,
                    gamePoints: o.gamePoints,
                    gameWinPercentage: o.gameWinPercentage,
                    opponentsGameWinPercentage: o.opponentsGameWinPercentage
                ))
                .ToArray();

            return new StandingsResponse(playerStandings);
        }
Beispiel #4
0
        public decimal GetOpponentsGameWinPercentage(Tournament tournament, Player player, int? roundNumber = null)
        {
            var opponents = tournament.GetPlayerOpponents(player, roundNumber);

            return opponents
                .Select(opponent => GetGameWinPercentage(tournament, opponent, roundNumber))
                .DefaultIfEmpty(0)
                .Average();
        }
Beispiel #5
0
 public int GetGamePoints(Tournament tournament, Player player, int? roundNumber = null)
 {
     return tournament
         .GetPlayerGames(player, roundNumber)
         .Select(game => new PlayerStats(
             wins: game.Winner == player ? 1 : 0,
             losses: game.Winner != player && game.Winner != null ? 1 : 0,
             draws: game.Winner == null ? 1 : 0
         ))
         .Aggregate(0, (sum, stats) => sum + (stats.Wins * GameWin + stats.Draws * GameDraw + stats.Losses * GameLoss));
 }
Beispiel #6
0
        public decimal GetMatchWinPercentage(Tournament tournament, Player player, int? roundNumber = null)
        {
            var achieved = GetMatchPoints(tournament, player, roundNumber);

            var maximum = tournament
                .GetPlayerMatches(player, roundNumber)
                .Count() * MatchWin;

            if(maximum == 0)
                return 0.33m;

            var rawPercentage = achieved / (decimal)maximum;

            // Match win percentage is capped at 0.33 on the low end
            return Math.Max(0.33m, rawPercentage);
        }
Beispiel #7
0
 public int GetMatchPoints(Tournament tournament, Player player, int? roundNumber = null)
 {
     return tournament
         .GetPlayerMatches(player, roundNumber)
         .Select(match => new PlayerStats(
             wins: match.Games.Where(game => game.Winner == player).Count(),
             losses: match.Games.Where(game => game.Winner != player && game.Winner != null).Count(),
             draws: match.Games.Where(game => game.Winner == player).Count()
         ))
         .Select(stats => stats.Wins > stats.Losses
             ? MatchWin
             : stats.Wins == stats.Losses
                 ? MatchDraw
                 : MatchLoss)
         .Sum();
 }
        public TournamentResponse Create(Tournament tournament)
        {
            if(tournament == null)
                throw new ArgumentNullException("tournament");

            var tournamentState = TournamentManager.GetTournamentState(tournament);

            return new TournamentResponse(
                key: tournament.Key,
                name: tournament.Name,
                started: tournamentState >= TournamentState.Started,
                finished: tournamentState >= TournamentState.Finished,
                totalRounds: TournamentManager.GetTotalRounds(tournament),
                activeRoundNumber: tournament.ActiveRoundNumber
            );
        }
Beispiel #9
0
        public TournamentState GetTournamentState(Tournament tournament)
        {
            if(tournament == null)
                throw new ArgumentNullException("tournament");

            var firstRoundState = RoundManager.GetRoundState(tournament, 1);

            if(firstRoundState < RoundState.Committed)
                return TournamentState.None;

            var lastRound = RoundManager.GetMaxRoundsForTournament(tournament);
            var lastRoundState = RoundManager.GetRoundState(tournament, lastRound);

            if(lastRoundState < RoundState.Completed)
                return TournamentState.Started;

            return TournamentState.Finished;
        }
        public RoundResponse Create(Tournament tournament, Round round)
        {
            if(round == null)
                throw new ArgumentNullException("round");

            var roundState = RoundManager.GetRoundState(tournament, round.Number);

            return new RoundResponse(
                number: round.Number,
                started: roundState >= RoundState.Committed,
                completed: roundState >= RoundState.Completed,
                final: roundState >= RoundState.Final,
                matches: round
                    .Matches
                    .OrderBy(match => match.Number)
                    .Select(match => MatchResponseProvider.Create(match))
                    .ToArray()
            );
        }
Beispiel #11
0
        public ICollection<Match> CreateMatches(Tournament tournament, int roundNumber)
        {
            var seed = tournament.Seed ^ roundNumber;
            var rng = new Random(seed);

            var tournamentRounds = tournament
                .Rounds
                .SelectMany(round => round.Matches)
                .ToArray();

            // Group players by match points.
            // Randomize within each group.
            var playerPool = tournament
                .Players
                .Where(player => !player.Dropped)
                .Select(player => new
                    {
                        Player = player,
                        Randomizer = rng.Next(),
                        MatchPoints = StatsProvider.GetMatchPoints(tournament, player),
                        HadBye = tournamentRounds
                            .Where(match => match.Players.Contains(player))
                            .Where(match => match.Players.Count == 1)
                            .Any(),
                        Opponents = tournamentRounds
                            .Where(match => match.Players.Contains(player))
                            .SelectMany(match => match.Players.Except(new[] { player }))
                            .ToArray()
                    })
                .OrderBy(o => o.MatchPoints)
                .ThenBy(o => o.Randomizer)
                .ToArray()
                .AsEnumerable();

            var matches = Enumerable.Empty<Match>();

            // If there is an odd number of players, from the bottom up, take the first player
            // that hasn't had a bye and remove them from the pool.
            if(playerPool.Count() % 2 == 1)
            {
                var byePlayer = playerPool
                    .Where(o => !o.HadBye)
                    .Take(1);

                matches = matches
                    .Concat(byePlayer
                        .Select(o => new Match
                        {
                            Number = 1 + (playerPool.Count() / 2),
                            // A bye gives two game wins
                            Games = new[]
                                {
                                    new Game
                                    {
                                        Winner = o.Player,
                                    },
                                    new Game
                                    {
                                        Winner = o.Player,
                                    },
                                },
                            Players = new[]
                                {
                                    o.Player,
                                },
                        })
                        .ToArray()
                    );

                playerPool = playerPool.Except(byePlayer);
            }

            // Start at the top and take the next player in order. If they've never had a match,
            // place both in a pairing and remove them from the pool. If they have had a match,
            // take the next lowest player. Repeat until an unplayed opponent is found.
            var matchNumber = 1;
            while(playerPool.Any())
            {
                var left = playerPool
                    .First();

                var right = playerPool
                    .Skip(1)
                    .SkipWhile(o => left
                        .Opponents
                        .Contains(o.Player)
                    )
                    .FirstOrDefault()
                    ?? playerPool
                        .Skip(1)
                        .First();

                matches = matches
                    .Concat(new[]
                        {
                            new Match
                            {
                                Number = matchNumber,
                                Games = new List<Game>(),
                                Players = new[]
                                    {
                                        left.Player,
                                        right.Player,
                                    }
                                    .OrderBy(player => player.Name)
                                    .ToArray(),
                            }
                        }
                    );

                playerPool = playerPool
                    .Except(new[] { left, right });

                matchNumber++;
            }

            return matches.ToArray();
        }
Beispiel #12
0
        // Rounds have five states: projected, committed, and completed, finalized, and invalid
        // - A round is finalized when it's been completed and a result entered for the next round
        // - A round is completed when all matches have the minimum number of results
        // - A round is committed when the first result is submitted. A result can't be submitted until the previous round is completed.
        // - A round is projected when the previous round is completed by no results have been submitted.
        // - A round is invalid if it's more than one greater than the last completed round number
        // When a round becomes committed, it's written to the database and can't be changed. All matches must have a result submitted to move forward.
        public RoundState GetRoundState(Tournament tournament, int roundNumber)
        {
            if(roundNumber < 0)
                return RoundState.Invalid;

            if(roundNumber == 0)
                return RoundState.Completed;

            if(tournament.Players.Count < 2)
                return RoundState.Invalid;

            var previousRoundState = GetRoundState(tournament, roundNumber - 1);

            if(previousRoundState < RoundState.Completed)
                return RoundState.Invalid;

            var requestedRound = tournament
                .Rounds
                .Where(round => round.Number == roundNumber)
                .FirstOrDefault();

            if(requestedRound == null)
                return RoundState.Projected;

            // A round is completed when all matches have at least two wins or are called because of time.
            // Since we don't track called games, we just check for at least one completed game in each match.
            var requestedRoundIsCompleted = requestedRound
                .Matches
                .All(match => match
                    .Games
                    .Count() >= 1
                );

            var nextRoundHasResults = tournament
                .Rounds
                .Where(round => round.Number == roundNumber + 1)
                .Where(round => round.Matches.Any())
                .Any();

            if(requestedRoundIsCompleted)
                if(nextRoundHasResults)
                    return RoundState.Final;
                else
                    return RoundState.Completed;

            return RoundState.Committed;
        }
Beispiel #13
0
        public Round GetRound(Tournament tournament, int roundNumber)
        {
            if(tournament == null)
                throw new ArgumentNullException("tournament");

            if(roundNumber > GetMaxRoundsForTournament(tournament))
                return null;

            var roundState = GetRoundState(tournament, roundNumber);

            if(roundState == RoundState.Invalid)
                return null;

            if(roundState == RoundState.Projected)
                return new Round
                {
                    Number = roundNumber,
                    Matches = CreateMatches(tournament, roundNumber),
                };
            else
                return tournament
                    .Rounds
                    .Where(r => r.Number == roundNumber)
                    .FirstOrDefault();
        }
Beispiel #14
0
        public int GetMaxRoundsForTournament(Tournament tournament)
        {
            var playerCount = tournament.Players.Count();
            var roundCount = Math.Ceiling(Math.Log(playerCount, 2));

            // No less than one round
            return (int)Math.Max(1, roundCount);
        }
 public ActiveRoundResponse Create(Tournament tournament, int? currentRoundNumber)
 {
     return new ActiveRoundResponse(tournament.Key, currentRoundNumber);
 }