public DoubleElimBracket(BracketModel _model)
            : base(_model)
        {
            this.NumberOfLowerRounds = 0;
            if (_model.Matches.Count > 0)
            {
                if (CalculateTotalLowerBracketMatches(Players.Count) > 0)
                {
                    int numOfGrandFinal = _model.Matches.Count;

                    // Create Matches from MatchModels.
                    // This is extending SEB's method, so all upper bracket Matches are already done.
                    foreach (MatchModel mm in _model.Matches.OrderBy(m => m.MatchNumber))
                    {
                        if (Matches.ContainsKey(mm.MatchNumber))
                        {
                            // Case 1: match is upper bracket:
                            continue;
                        }
                        if (mm.MatchNumber == numOfGrandFinal)
                        {
                            // Case 2: match is grand final:
                            this.grandFinal = new Match(mm);
                        }
                        else
                        {
                            // Case 3: match is lower bracket:
                            Match match = new Match(mm);
                            LowerMatches.Add(match.MatchNumber, match);
                            this.NumberOfLowerRounds = Math.Max(NumberOfLowerRounds, match.RoundIndex);
                        }
                    }
                }
                this.NumberOfMatches = Matches.Count + LowerMatches.Count;
                if (null != grandFinal)
                {
                    ++NumberOfMatches;
                }
            }

            RecalculateRankings();
            if (grandFinal?.IsFinished ?? false)
            {
                this.IsFinished = true;
            }

            if (this.IsFinalized && false == Validate())
            {
                throw new BracketValidationException
                          ("Bracket is Finalized but not Valid!");
            }
        }
        /// <summary>
        /// Finds an eliminated player's rank for a double-elim bracket.
        /// </summary>
        /// <param name="_matchNumber">Number of finished Match</param>
        /// <returns>Player's rank</returns>
        protected override int CalculateRank(int _matchNumber)
        {
            int rank = 2;             // 2 = GrandFinal loser

            if (LowerMatches?.ContainsKey(_matchNumber) ?? false)
            {
                // Standard case: lower bracket match.
                Match match = GetInternalMatch(_matchNumber);
                rank = NumberOfMatches - GetLowerRound(match.RoundIndex).Last().MatchNumber + 2;
            }
            else if (Matches?.ContainsKey(_matchNumber) ?? false)
            {
                // Special case: upper bracket "play-in" round.
                // These special rounds eliminate their losers
                // instead of sending them to the lower bracket.
                rank = Convert.ToInt32(Math.Pow(2, NumberOfRounds - 1) + 1);
            }

            return(rank);
        }
Beispiel #3
0
        /// <summary>
        /// Resets the Bracket.
        /// Affects Matches, Rankings, and bracket status.
        /// </summary>
        /// <remarks>
        /// This is overriden in Group Stages and Swiss, for added functionality.
        /// </remarks>
        protected virtual void ResetBracketData()
        {
            if (null == Matches)
            {
                Matches = new Dictionary <int, Match>();
            }
            if (null == LowerMatches)
            {
                LowerMatches = new Dictionary <int, Match>();
            }
            if (null == Rankings)
            {
                Rankings = new List <IPlayerScore>();
            }

            IsFinished = false;
            Matches.Clear();
            LowerMatches.Clear();
            grandFinal      = null;
            NumberOfRounds  = NumberOfLowerRounds = 0;
            NumberOfMatches = 0;
            Rankings.Clear();
        }
Beispiel #4
0
        /// <summary>
        /// Gets one Match, from its Match Number.
        /// If no matching Match is found, an exception is thrown.
        /// </summary>
        /// <param name="_matchNumber">Number of Match to find</param>
        /// <returns>relevant Match</returns>
        /// <remarks>
        /// This method is overriden in Group Stages.
        /// </remarks>
        protected virtual Match GetInternalMatch(int _matchNumber)
        {
            if (_matchNumber < 1)
            {
                throw new InvalidIndexException
                          ("Match number cannot be less than 1!");
            }

            if (grandFinal?.MatchNumber == _matchNumber)
            {
                return(grandFinal);
            }
            if (Matches?.ContainsKey(_matchNumber) ?? false)
            {
                return(Matches[_matchNumber]);
            }
            if (LowerMatches?.ContainsKey(_matchNumber) ?? false)
            {
                return(LowerMatches[_matchNumber]);
            }

            throw new MatchNotFoundException
                      ("Match not found; match number may be invalid.");
        }
        /// <summary>
        /// Uses the playerlist to generate the bracket structure & Matches.
        /// This creates & populates all the Match objects, and ties them together.
        /// If any Matches already exist, they will be deleted first.
        /// If there are <4 players, nothing will be made.
        /// This method first calls the base (Single Elim) version,
        /// which creates the upper bracket.
        /// </summary>
        /// <param name="_gamesPerMatch">Max games for every Match</param>
        public override void CreateBracket(int _gamesPerMatch = 1)
        {
            if (Players.Count < 4)
            {
                return;
            }

            // The base/single-elim method creates the upper bracket:
            base.CreateBracket(_gamesPerMatch);

            /*
             * Similar to the SEB version, we're going to create a list of rounds.
             * Each of these is a lower-bracket round.
             * Again, we order the rounds back-to-front.
             */
            List <List <Match> > roundList = new List <List <Match> >();
            int totalMatches = CalculateTotalLowerBracketMatches(Players.Count);
            int numMatches   = 0;
            int r            = 0;

            // Create the Matches
            while (numMatches < totalMatches)
            {
                // Go through the while loop once per round.
                // Add a new round and populate it:
                roundList.Add(new List <Match>());
                for (int i = 0;
                     i < Math.Pow(2, r / 2) && numMatches < totalMatches;
                     ++i, ++numMatches)
                {
                    // Go through the for loop once per match.
                    // Add a Match to the current round list:
                    Match m = new Match();
                    m.SetMaxGames(_gamesPerMatch);
                    roundList[r].Add(m);
                }
                ++r;
            }

            // Assign Match Numbers (start counting after upper bracket matches)
            int matchNum = 1 + Matches.Count;

            for (r = roundList.Count - 1; r >= 0; --r)
            {
                foreach (Match match in roundList[r])
                {
                    match.SetMatchNumber(matchNum++);
                }
            }

            /*
             * Tie Matches together.
             * This gets a little complicated. Three main cases:
             * 1) First round:
             *    - Both players per match come from the UB.
             * 2) Previous round has 2x the matches:
             *    - Both players per match come from the prev LB round.
             * 3) Previous round has == the matches:
             *    - One player from UB, one from the prev LB round.
             * In addition, we need to "re-seed" the lower bracket,
             * so that if Jon beats Bob in UB, then loses his next match,
             * they don't get an immediate rematch.
             */

            // FlipSeeds is the current method of re-seeding.
            // Every round that gets players from the UB
            // will toggle this bool to flip the order those players come down.
            // This isn't perfect, but it works well for any bracket with <32 players.
            bool flipSeeds = true;

            for (r = roundList.Count - 2; r >= 0; --r)
            {
                bool rIndexIsEven = (0 == r % 2) ? true : false;
                if (rIndexIsEven && roundList[r + 1].Count == roundList[r].Count)
                {
                    // Round is "normal," but one Player is coming from Upper Bracket.
                    for (int m = 0; m < roundList[r].Count; ++m)
                    {
                        // Calculate the UB round # that's sending its loser here:
                        List <IMatch> upperRound = GetRound(NumberOfRounds - (r / 2));
                        int           currNum    = roundList[r][m].MatchNumber;

                        // Assign prev/next matchup indexes:
                        if (flipSeeds)
                        {
                            roundList[r][m].AddPreviousMatchNumber(upperRound[upperRound.Count - 1 - m].MatchNumber);
                            Matches[upperRound[upperRound.Count - 1 - m].MatchNumber].SetNextLoserMatchNumber(currNum);
                        }
                        else
                        {
                            roundList[r][m].AddPreviousMatchNumber(upperRound[m].MatchNumber);
                            Matches[upperRound[m].MatchNumber].SetNextLoserMatchNumber(currNum);
                        }
                        // ************* THIS ISN'T QUITE RIGHT (RE-SEED ORDER)

                        roundList[r][m].AddPreviousMatchNumber(roundList[r + 1][m].MatchNumber);
                        roundList[r + 1][m].SetNextMatchNumber(currNum);
                    }

                    // Flip the re-seeding bool:
                    flipSeeds = !flipSeeds;
                }
                else if (!rIndexIsEven && roundList[r + 1].Count == (roundList[r].Count * 2))
                {
                    // Round is "normal," and both Players come from the lower bracket.
                    for (int m = 0; m < roundList[r].Count; ++m)
                    {
                        // Assign prev/next matchup indexes
                        int currNum = roundList[r][m].MatchNumber;

                        // [r][3] <-> [r+1][6]
                        roundList[r][m].AddPreviousMatchNumber(roundList[r + 1][m * 2].MatchNumber);
                        roundList[r + 1][m * 2].SetNextMatchNumber(currNum);

                        // [r][4] <-> [r+1][7]
                        roundList[r][m].AddPreviousMatchNumber(roundList[r + 1][m * 2 + 1].MatchNumber);
                        roundList[r + 1][m * 2 + 1].SetNextMatchNumber(currNum);
                    }
                }
                else
                {
                    // Round is abnormal. Case is not possible
                    // (unless we later decide to include it)
                }

                //flipSeeds = !flipSeeds;
            }

            // As stated above, the "first" LB round is its own special case.
            // Here, we manually set its previous match numbers:
            r = roundList.Count - 1;
            if (r >= 0)
            {
                // We have enough teams to have created a Lower Bracket.
                // Manually update the first Lower round,
                // and create a Grand Final match.

                for (int m = 0; m < roundList[r].Count; ++m)
                {
                    // Calculate which UB round sends its losing players here:
                    List <IMatch> upperRound = GetRound(NumberOfRounds - (r / 2 + 1));
                    int           currNum    = roundList[r][m].MatchNumber;

                    // Assign prev/next matchup indexes for FIRST round.
                    // (both teams come from Upper Bracket)
                    roundList[r][m].AddPreviousMatchNumber(upperRound[m * 2].MatchNumber);
                    Matches[upperRound[m * 2].MatchNumber].SetNextLoserMatchNumber(currNum);

                    roundList[r][m].AddPreviousMatchNumber(upperRound[m * 2 + 1].MatchNumber);
                    Matches[upperRound[m * 2 + 1].MatchNumber].SetNextLoserMatchNumber(currNum);
                }

                // Create a Grand Final:
                grandFinal = new Match();
                grandFinal.SetMatchNumber(matchNum);
                grandFinal.SetMaxGames(_gamesPerMatch);
                grandFinal.SetRoundIndex(1);
                grandFinal.SetMatchIndex(1);
                grandFinal.AddPreviousMatchNumber(Matches.Count);
                grandFinal.AddPreviousMatchNumber(roundList[0][0].MatchNumber);

                // Connect UB final and LB final to Grand Final:
                roundList[0][0].SetNextMatchNumber(grandFinal.MatchNumber);
                Matches[Matches.Count].SetNextMatchNumber(grandFinal.MatchNumber);

                /*
                 * Now we've generated our Bracket.
                 * Time to move the Matches from the double array
                 * into an easily accessible Dictionary for storage.
                 * While we do this, we'll update their data: Round and Match indexes.
                 */
                NumberOfLowerRounds = roundList.Count;
                for (r = 0; r < roundList.Count; ++r)
                {
                    for (int m = 0; m < roundList[r].Count; ++m)
                    {
                        roundList[r][m].SetRoundIndex(roundList.Count - r);
                        roundList[r][m].SetMatchIndex(m + 1);
                        LowerMatches.Add(roundList[r][m].MatchNumber, roundList[r][m]);
                    }
                }
                NumberOfMatches += (LowerMatches.Count + 1);
            }
        }