Quick C# source code for generating NCAA picks

I wrote up some quick C# code for generating weighted random results with for this year's NCAA bracket. I wrote this up in a Code Behind; it writes all the HTML, so no design work needed. My basic thought is that I always hear about people joining pools, then being eliminated early on because of some upset. So if it's all left up to chance, why not fill out your bracket with the random upsets built in.

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

First is the extremely basic structures: Team and Round. A team just contains a rank and name. A round is just a list of teams. Each of these can be expanded on - some may want to use a team's record or other stats in the algorithm. I just used rank and that's all that's in the config. Likewise, each Round can be given a name and possibly dates.

    public class Team
    {
        public string Name;
        public int Rank;

        public Team() { }

        public Team(string name, int rank)
        {
            Name = name;
            Rank = rank;
        }
    }

    public class Round
    {
        public List<Team> Teams = new List<Team>();
    }

Now, the basic flow of the program:

  • Load the first round from configuration data
  • Build the entire bracket using a weighted random algorithm
  • Display the bracket
        protected void Page_Load(object sender, EventArgs e)
        {
            Round firstRound = GetFirstRound();
            List<Round> rounds = BuildBracket(firstRound);
            DisplayBracket(rounds);
        }

Here's the quick script for getting the data from a configuration file. I pulled the data from the 2009 NCAA page on wikipedia, then minimally formatted the data. I've attached the config as a text file. Basically, if the rows are in the proper format (rank first, then name of the team), it'll add a Team object. Note: If you want to do something completely different to build your list of teams, just replace this one step; then you can still use the structure for the rest of the code.

        private string m_ConfigPath = @"C:\\ncaa config.txt";
        private string m_TeamPattern = @"^(?<rank>\d+) +(?<team>.+)";

        private Round GetFirstRound()
        {
            Round round = new Round();
            Regex reg = new Regex(m_TeamPattern);

            using (StreamReader reader = new StreamReader(m_ConfigPath))
            {
                while (reader.Peek() >= 0)
                {
                    Match m = reg.Match(reader.ReadLine());

                    if (m == null || m.Length  == 0)
                        continue;

                    Team team = new Team();
                    team.Rank = Int32.Parse(m.Groups["rank"].Value);
                    team.Name = m.Groups["team"].Value.Trim();
                    round.Teams.Add(team);
                }
            }

            return round;
        }

Now that we've got the first round, we can build the rest of the bracket. There's a lot of assumptions here: That there's a 2n number of teams, and that the order of the bracket doesn't get shuffled at all. Given that I, the developer, am in full control of the config and execution of the script, I haven't added any checks. If you'd like to add them, be my guest :)

To pick a winner, it calls a method PickWinner described in the next section.

        private List<Round> BuildBracket(Round firstRound)
        {
            List<Round> rounds = new List<Round>();
            Round round = firstRound;
            rounds.Add(round);

            int prevCount = 0;

            while (round.Teams.Count > 1 && round.Teams.Count != prevCount)
            {
                prevCount = round.Teams.Count;
                round = GetNextRound(round);
                rounds.Add(round);
            }

            return rounds;
        }

        private Round GetNextRound(Round round)
        {
            Round nextRound = new Round();
            Team prevTeam= null;

            for (int i = 0; i < round.Teams.Count; i++)
            {
                Team team = round.Teams[i];

                if ((i % 2) == 0)
                {
                    prevTeam = team;
                    continue;
                }

                Team winner = PickWinner(team, prevTeam);
                nextRound.Teams.Add(winner);
            }

            return nextRound;
        }

Here's the script for choosing one team over another. You can do whatever you want here. My algorithm is basically:

  • Team 1 has a rank of 100
  • Team 2 has a rank of 5
  • Add those up: 105
  • Generate a random number 0 <= x < 105.
  • x favors Team 1. If x < 100, it has selected Team 1. If it happens to be that 100 <= x < 105, then it's with Team 2
  • On these stats, a rank of 100 is actually a worse team, so the "heavier" weight of Team 1 means they're more likely to lose. So if x < 100, then chalk Team 2 with the win.
        private Random m_Random = new Random();

        private Team PickWinner(Team team1, Team team2)
        {
            int total = team1.Rank + team2.Rank;
            int rand= m_Random.Next(total);

            return (rand < team1.Rank) ? team2 : team1;
        }

Finally, the display. I'm displaying it as one table (see below). I added some "fuzzy math" to get each round's displays and rowspans worked out. Again, this probably goes out the window if the number of teams isn't 2n.

        private void DisplayBracket(List<Round> rounds)
        {
            StringBuilder sb = new StringBuilder();            
            sb.Append("<table border=1>" + System.Environment.NewLine);

            int rowCount = rounds[0].Teams.Count; //rows in first round
            List<int> colSpans = new List<int>();

            //Build col spans and header row
            sb.Append("<tr>" + System.Environment.NewLine);

            for (int i = 0; i < rounds.Count; i++)
            {
                int teamCount = rounds[i].Teams.Count;
                int colSpan = rowCount / teamCount;
                colSpans.Add(colSpan);

                sb.Append(String.Format("<td><b>Round {0}</b></td>", ((int)(i + 1)).ToString()));
            }

            sb.Append("</tr>" + System.Environment.NewLine);

            for (int rowIndex=0; rowIndex < rowCount; rowIndex++)
            {
                sb.Append("<tr>" + System.Environment.NewLine);

                for (int i = 0; i < rounds.Count; i++)
                {
                    List<Team> teams= rounds[i].Teams;
                    int roundVal = teams.Count * rowIndex;

                    if ((roundVal % rowCount) == 0)
                    {
                        int roundIndex= roundVal / rowCount;
                        sb.Append(String.Format("<td valign=\"middle\" rowspan={0}>{1} ({2})</td>{3}", 
                            colSpans[i].ToString(),
                            teams[roundIndex].Name,
                            teams[roundIndex].Rank.ToString(),
                            System.Environment.NewLine));
                    }
                }

                sb.Append("</tr>" + System.Environment.NewLine);
            }

            sb.Append("</table>");
            Response.Write(sb.ToString());
        }

And then here's the finished result (one of many results).

Round 1 Round 2 Round 3 Round 4 Round 5 Round 6 Round 7
Louisville (1) Louisville (1) Louisville (1) Utah (5) Michigan State (2) Michigan State (2) Duke (2)
play-in winner (16)
Ohio State (8) Siena (9)
Siena (9)
Utah (5) Utah (5) Utah (5)
Arizona (12)
Wake Forest (4) Wake Forest (4)
Cleveland State (13)
West Virginia (6) West Virginia (6) Kansas (3) Michigan State (2)
Dayton (11)
Kansas (3) Kansas (3)
North Dakota State (14)
Boston College (7) USC (10) Michigan State (2)
USC (10)
Michigan State (2) Michigan State (2)
Robert Morris (15)
Connecticut (1) Connecticut (1) Connecticut (1) Connecticut (1) Missouri (3)
Chattanooga (16)
BYU (8) Texas A&M (9)
Texas A&M (9)
Purdue (5) Northern Iowa (12) Mississippi State (13)
Northern Iowa (12)
Washington (4) Mississippi State (13)
Mississippi State (13)
Marquette (6) Marquette (6) Missouri (3) Missouri (3)
Utah State (11)
Missouri (3) Missouri (3)
Cornell (14)
California (7) California (7) Memphis (2)
Maryland (10)
Memphis (2) Memphis (2)
Cal State Northridge (15)
Pittsburgh (1) Pittsburgh (1) Pittsburgh (1) Pittsburgh (1) Duke (2) Duke (2)
East Tennessee St. (16)
Oklahoma State (8) Tennessee (9)
Tennessee (9)
Florida St. (5) Wisconsin (12) Wisconsin (12)
Wisconsin (12)
Xavier (4) Portland St. (13)
Portland St. (13)
UCLA (6) VCU (11) VCU (11) Duke (2)
VCU (11)
Villanova (3) American (14)
American (14)
Texas (7) Texas (7) Duke (2)
Minnesota (10)
Duke (2) Duke (2)
Binghamton (15)
North Carolina (1) North Carolina (1) LSU (8) LSU (8) LSU (8)
Radford (16)
LSU (8) LSU (8)
Butler (9)
Illinois (5) Illinois (5) Gonzaga (4)
Western Kentucky (12)
Gonzaga (4) Gonzaga (4)
Akron (13)
Arizona State (6) Arizona State (6) Arizona State (6) Clemson (7)
Temple (11)
Syracuse (3) Syracuse (3)
Stephen F. Austin (14)
Clemson (7) Clemson (7) Clemson (7)
Michigan (10)
Oklahoma (2) Oklahoma (2)
Morgan State (15)

Quick and dirty. Feel free to use it. Let me know if you have any questions. And please let me know if you use these results (or results of your own) in a betting pool!

AttachmentSize
ncaa config.txt1.19 KB

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Copy the characters (respecting upper/lower case) from the image.