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> /// 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); } }