c# How to generate a tournament bracket HTML table

前端 未结 2 1495
谎友^
谎友^ 2021-02-03 14:19

so i have been stuck with this issue for 3 weeks now and i couldn\'t for the life of me figure it out. what im trying to do is to get this kind of output/presentation using tabl

相关标签:
2条回答
  • 2021-02-03 14:21

    We can implement this using System.Web.UI.WebControls.Table by dynamically adding table rows and table cells.

    Class File

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    /// <summary>
    /// Summary description for Matches
    /// </summary>
    
        public class Rounds
        {
            public int RoundNumber{get;set;}
            public List<Match> Matches{get;set;}
    
            public Rounds(int number, List<Match> matches)
            {
                this.RoundNumber = number;
                this.Matches = matches;
            }
        }
    
        public class Match
        {
            public int MatchId{get;set;}
            public Team Team1{get;set;}
            public Team Team2 {get; set;}
            public Team WinningTeam { get; set; }
    
            public Match(int id, Team t1, Team t2) :this (id, t1, t2, null)
            {
    
            }
    
            public Match(int id, Team t1, Team t2, Team t3)
            {
                this.MatchId = id;
                this.Team1 = t1;
                this.Team2 = t2;
                this.WinningTeam = t3;
            }
        }
    
        public class Team
        {
            public int TeamId {get;set;}
            public string TeamName {get;set;}
    
            public Team(int id, string name)
            {
                this.TeamId = id;
                this.TeamName = name;
            }
        }
    

    .aspx.cs file - We need to add a div control in .aspx file.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    
    public partial class Test : System.Web.UI.Page
    {
        List<Rounds> rounds = new List<Rounds>();
        protected void Page_Load(object sender, EventArgs e)
        {
            ShowRoundMatchesUsingTable();
        }
    
        private List<Rounds> GetRoundMatchesDetails()
        {
            List<Team> teamList = new List<Team>();
            teamList.Add(new Team(1, "Arcenal"));
            teamList.Add(new Team(2, "Barsa"));
            teamList.Add(new Team(3, "Manchester"));
            teamList.Add(new Team(4, "Black Burn"));
            teamList.Add(new Team(5, "Ferrari"));
            teamList.Add(new Team(6, "Adidas"));
            teamList.Add(new Team(7, "Reebock"));
            teamList.Add(new Team(8, "Nike"));
    
            List<Match> matchList1 = new List<Match>();
            matchList1.Add(new Match(1, teamList.Find(lst => lst.TeamName == "Arcenal"), teamList.Find(lst => lst.TeamName == "Barsa")));
            matchList1.Add(new Match(1, teamList.Find(lst => lst.TeamName == "Manchester"), teamList.Find(lst => lst.TeamName == "Black Burn")));
            matchList1.Add(new Match(1, teamList.Find(lst => lst.TeamName == "Ferrari"), teamList.Find(lst => lst.TeamName == "Adidas")));
            matchList1.Add(new Match(1, teamList.Find(lst => lst.TeamName == "Reebock"), teamList.Find(lst => lst.TeamName == "Nike")));
    
            List<Match> matchList2 = new List<Match>();
            matchList2.Add(new Match(2, teamList.Find(lst => lst.TeamName == "Arcenal"), teamList.Find(lst => lst.TeamName == "Manchester")));
            matchList2.Add(new Match(2, teamList.Find(lst => lst.TeamName == "Adidas"), teamList.Find(lst => lst.TeamName == "Nike")));
    
            List<Rounds> rounds = new List<Rounds>();
    
            rounds.Add(new Rounds(1, matchList1));
            rounds.Add(new Rounds(2, matchList2));
    
            return rounds;
        }
    
        private void ShowRoundMatchesUsingTable()
        {
            IEnumerable<Rounds> roundsList = GetRoundMatchesDetails();
    
            if (roundsList == null || roundsList.Count() == 0) return;
    
            Table tbl = new Table();
    
            TableRow trHeaderRow = new TableRow();
            TableRow trDetailRow = new TableRow();
            TableCell tcDetails = new TableCell();
    
            foreach (Rounds round in roundsList)
            {
                TableHeaderCell th = new TableHeaderCell();
                th.Text = "Round : " + round.RoundNumber ;
                trHeaderRow.Cells.Add(th);
    
                if (round.Matches != null && round.Matches.Count > 0)
                {
                    tcDetails = new TableCell();
                    trDetailRow.Cells.Add(tcDetails);
                }
    
                foreach (Match m in round.Matches)
                {
                    Table dtlTable = new Table();
                    tcDetails.Controls.Add(dtlTable);
    
                    TableRow tr1 = new TableRow();
                    TableCell tc = new TableCell();
                    tc.Text = m.Team1.TeamName;
                    tr1.Cells.Add(tc);
                    dtlTable.Rows.Add(tr1);
    
                    tr1 = new TableRow();
                    tc = new TableCell();
                    tc.Text = "Vs";
                    tr1.Cells.Add(tc);
                    dtlTable.Rows.Add(tr1);
    
                    tr1 = new TableRow();
                    tc = new TableCell();
                    tc.Text = m.Team2.TeamName;
                    tr1.Cells.Add(tc);
                    dtlTable.Rows.Add(tr1);
                }
            }
    
            tbl.Rows.Add(trHeaderRow);
            tbl.Rows.Add(trDetailRow);
            div.Controls.Add(tbl);
        }
    }
    
    0 讨论(0)
  • 2021-02-03 14:31

    Here's my attempt. I have tested the code for 2, 3, and 4 Round Tournaments. The outputs for a 2-Round and 3-Round tournament are shown here:

    enter image description here


    enter image description here


    I have used the same model that you provided to define a Match. I have added a Tournament class to generate test data.

    Match.cs - The class containing the models

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace tournament
    {
        public class Match
        {
            public int id { get; set; }
            public int teamid1 { get; set; }
            public int teamid2 { get; set; }
            public int roundnumber { get; set; }
            public int winner { get; set; }
    
            public Match(int id, int teamid1, int teamid2, int roundnumber, int winner)
            {
                this.id = id;
                this.teamid1 = teamid1;
                this.teamid2 = teamid2;
                this.roundnumber = roundnumber;
                this.winner = winner;
            }
        }
    
        public class Tournament
        {
            public SortedList<int, SortedList<int, Match>> TournamentRoundMatches { get; private set; }
            public Match ThirdPlaceMatch { get; private set; }
    
            public Tournament(int rounds)
            {
                this.TournamentRoundMatches = new SortedList<int, SortedList<int, Match>>();
                this.GenerateTournamentResults(rounds);
                if (rounds > 1)
                {
                    this.GenerateThirdPlaceResult(rounds);
                }
            }
    
            public void AddMatch(Match m)
            {
                if (this.TournamentRoundMatches.ContainsKey(m.roundnumber))
                {
                    if (!this.TournamentRoundMatches[m.roundnumber].ContainsKey(m.id))
                    {
                        this.TournamentRoundMatches[m.roundnumber].Add(m.id, m);
                    }
                }
                else
                {
                    this.TournamentRoundMatches.Add(m.roundnumber, new SortedList<int, Match>());
                    this.TournamentRoundMatches[m.roundnumber].Add(m.id, m);
                }
            }
    
            private void GenerateTournamentResults(int rounds)
            {
                Random WinnerRandomizer = new Random();
    
                for (int round = 1, match_id = 1; round <= rounds; round++)
                {
                    int matches_in_round = 1 << (rounds - round);
                    for (int round_match = 1; round_match <= matches_in_round; round_match++, match_id++)
                    {
                        int team1_id;
                        int team2_id;
                        int winner;
                        if (round == 1)
                        {
                            team1_id = (match_id * 2) - 1;
                            team2_id = (match_id * 2);
                        }
                        else
                        {
                            int match1 = (match_id - (matches_in_round * 2) + (round_match - 1));
                            int match2 = match1 + 1;
                            team1_id = this.TournamentRoundMatches[round - 1][match1].winner;
                            team2_id = this.TournamentRoundMatches[round - 1][match2].winner;
                        }
                        winner = (WinnerRandomizer.Next(1, 3) == 1) ? team1_id : team2_id;
                        this.AddMatch(new Match(match_id, team1_id, team2_id, round, winner));
                    }
                }
            }
    
            private void GenerateThirdPlaceResult(int rounds)
            {
                Random WinnerRandomizer = new Random();
                int semifinal_matchid1 = this.TournamentRoundMatches[rounds - 1].Keys.ElementAt(0);
                int semifinal_matchid2 = this.TournamentRoundMatches[rounds - 1].Keys.ElementAt(1);
                Match semifinal_1 = this.TournamentRoundMatches[rounds - 1][semifinal_matchid1];
                Match semifinal_2 = this.TournamentRoundMatches[rounds - 1][semifinal_matchid2];
                int semifinal_loser1 = (semifinal_1.winner == semifinal_1.teamid1) ? semifinal_1.teamid2 : semifinal_1.teamid1;
                int semifinal_loser2 = (semifinal_2.winner == semifinal_2.teamid1) ? semifinal_2.teamid2 : semifinal_2.teamid1;
                int third_place_winner = (WinnerRandomizer.Next(1, 3) == 1) ? semifinal_loser1 : semifinal_loser2;
                this.ThirdPlaceMatch = new Match((1 << rounds) + 1, semifinal_loser1, semifinal_loser2, 1, third_place_winner);
            }
        }
    }
    

    I have generated the raw HTML dynamically using the static method GenerateHTMLResultsTable. This was done using only <table> without any need for <div> blocks.

    Program.cs - The static Program class that initializes test data and generates the HTML

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;
    
    namespace tournament
    {
        class Program
        {
            static string GenerateHTMLResultsTable(Tournament tournament)
            {
                int match_white_span;
                int match_span;
                int position_in_match_span;
                int column_stagger_offset;
                int effective_row;
                int col_match_num;
                int cumulative_matches;
                int effective_match_id;
                int rounds = tournament.TournamentRoundMatches.Count;
                int teams = 1 << rounds;
                int max_rows = teams << 1;
                StringBuilder HTMLTable = new StringBuilder();
    
                HTMLTable.AppendLine("<style type=\"text/css\">");
                HTMLTable.AppendLine("    .thd {background: rgb(220,220,220); font: bold 10pt Arial; text-align: center;}");
                HTMLTable.AppendLine("    .team {color: white; background: rgb(100,100,100); font: bold 10pt Arial; border-right: solid 2px black;}");
                HTMLTable.AppendLine("    .winner {color: white; background: rgb(60,60,60); font: bold 10pt Arial;}");
                HTMLTable.AppendLine("    .vs {font: bold 7pt Arial; border-right: solid 2px black;}");
                HTMLTable.AppendLine("    td, th {padding: 3px 15px; border-right: dotted 2px rgb(200,200,200); text-align: right;}");
                HTMLTable.AppendLine("    h1 {font: bold 14pt Arial; margin-top: 24pt;}");
                HTMLTable.AppendLine("</style>");
    
                HTMLTable.AppendLine("<h1>Tournament Results</h1>");
                HTMLTable.AppendLine("<table border=\"0\" cellspacing=\"0\">");
                for (int row = 0; row <= max_rows; row++)
                {
                    cumulative_matches = 0;
                    HTMLTable.AppendLine("    <tr>");
                    for (int col = 1; col <= rounds + 1; col++)
                    {
                        match_span = 1 << (col + 1);
                        match_white_span = (1 << col) - 1;
                        column_stagger_offset = match_white_span >> 1;
                        if (row == 0)
                        {
                            if (col <= rounds)
                            {
                                HTMLTable.AppendLine("        <th class=\"thd\">Round " + col + "</th>");
                            }
                            else
                            {
                                HTMLTable.AppendLine("        <th class=\"thd\">Winner</th>");
                            }
                        }
                        else if (row == 1)
                        {
                            HTMLTable.AppendLine("        <td class=\"white_span\" rowspan=\"" + (match_white_span - column_stagger_offset) + "\">&nbsp;</td>");
                        }
                        else
                        {
                            effective_row = row + column_stagger_offset;
                            if (col <= rounds)
                            {
                                position_in_match_span = effective_row % match_span;
                                position_in_match_span = (position_in_match_span == 0) ? match_span : position_in_match_span;
                                col_match_num = (effective_row / match_span) + ((position_in_match_span < match_span) ? 1 : 0);
                                effective_match_id = cumulative_matches + col_match_num;
                                if ((position_in_match_span == 1) && (effective_row % match_span == position_in_match_span))
                                {
                                    HTMLTable.AppendLine("        <td class=\"white_span\" rowspan=\"" + match_white_span + "\">&nbsp;</td>");
                                }
                                else if ((position_in_match_span == (match_span >> 1)) && (effective_row % match_span == position_in_match_span))
                                {
                                    HTMLTable.AppendLine("        <td class=\"team\">Team " + tournament.TournamentRoundMatches[col][effective_match_id].teamid1 + "</td>");
                                }
                                else if ((position_in_match_span == ((match_span >> 1) + 1)) && (effective_row % match_span == position_in_match_span))
                                {
                                    HTMLTable.AppendLine("        <td class=\"vs\" rowspan=\"" + match_white_span + "\">VS</td>");
                                }
                                else if ((position_in_match_span == match_span) && (effective_row % match_span == 0))
                                {
                                    HTMLTable.AppendLine("        <td class=\"team\">Team " + tournament.TournamentRoundMatches[col][effective_match_id].teamid2 + "</td>");
                                }
                            }
                            else
                            {
                                if (row == column_stagger_offset + 2)
                                {
                                    HTMLTable.AppendLine("        <td class=\"winner\">Team " + tournament.TournamentRoundMatches[rounds][cumulative_matches].winner + "</td>");
                                }
                                else if (row == column_stagger_offset + 3)
                                {
                                    HTMLTable.AppendLine("        <td class=\"white_span\" rowspan=\"" + (match_white_span - column_stagger_offset) + "\">&nbsp;</td>");
                                }
                            }
                        }
                        if (col <= rounds)
                        {
                            cumulative_matches += tournament.TournamentRoundMatches[col].Count;
                        }
                    }
                    HTMLTable.AppendLine("    </tr>");
                }
                HTMLTable.AppendLine("</table>");
    
                HTMLTable.AppendLine("<h1>Third Place Results</h1>");
                HTMLTable.AppendLine("<table border=\"0\" cellspacing=\"0\">");
                HTMLTable.AppendLine("    <tr>");
                HTMLTable.AppendLine("        <th class=\"thd\">Round 1</th>");
                HTMLTable.AppendLine("        <th class=\"thd\">Third Place</th>");
                HTMLTable.AppendLine("    </tr>");
                HTMLTable.AppendLine("    <tr>");
                HTMLTable.AppendLine("        <td class=\"white_span\">&nbsp;</td>");
                HTMLTable.AppendLine("        <td class=\"white_span\" rowspan=\"2\">&nbsp;</td>");
                HTMLTable.AppendLine("    </tr>");
                HTMLTable.AppendLine("    <tr>");
                HTMLTable.AppendLine("        <td class=\"team\">Team " + tournament.ThirdPlaceMatch.teamid1 + "</td>");
                HTMLTable.AppendLine("    </tr>");
                HTMLTable.AppendLine("    <tr>");
                HTMLTable.AppendLine("        <td class=\"vs\">VS</td>");
                HTMLTable.AppendLine("        <td class=\"winner\">Team " + tournament.ThirdPlaceMatch.winner + "</td>");
                HTMLTable.AppendLine("    </tr>");
                HTMLTable.AppendLine("    <tr>");
                HTMLTable.AppendLine("        <td class=\"team\">Team " + tournament.ThirdPlaceMatch.teamid2 + "</td>");
                HTMLTable.AppendLine("        <td class=\"white_span\">&nbsp;</td>");
                HTMLTable.AppendLine("    </tr>");
                HTMLTable.AppendLine("</table>");
                return HTMLTable.ToString();
            }
    
            static void Main(string[] args)
            {
                Tournament Test3RoundTournament = new Tournament(3);
                Tournament Test2RoundTournament = new Tournament(2);
                File.WriteAllText(@"C:\Tournament\results.html", GenerateHTMLResultsTable(Test2RoundTournament));
                File.WriteAllText(@"C:\Tournament\results.html", GenerateHTMLResultsTable(Test3RoundTournament));
                Console.ReadLine();
            }
        }
    }
    

    UPDATE

    Explanation of Parameters Used to Generate the HTML Table

    enter image description here

    As you can see, the column_stagger_offset is the amount by which each column is shifted up to align them the way they are supposed to. The effective_row is essentially where the particular table cell would have been had there been no vertical shift. Knowing the effective_row and the position_in_match_span helps determine what needs to be shown in that particuar cell (blank, team1, team2, or vs.).

    As you've noticed, I am iterating over the columns one row at a time. That seems most natural considering that HTML tables are also constructed that way, i.e. create row, add cells ... create row, add cels ... and so on.

    0 讨论(0)
提交回复
热议问题