Quick C# source code for generating NCAA picks
Submitted by Blake on Mon, 03/16/2009 - 18:06I 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!
What does your current twitter feed say about you?
Submitted by Blake on Fri, 03/13/2009 - 15:15Or how to detect if someone is worth following in 3 seconds flat.
We've all been there. You find yourself on someone's twitter page. Someone you're not currently following. You've spent too much time on twitter that day already, so you leave it up to your gut to figure out. In a matter of seconds, you'll determine if you want to follow this person or not. You glance at their tweets. Maybe you don't even read more than 100 characters total. Maybe you don't even focus your eyes on the text. But in the end, you'll either hit that Follow button or you'll navigate away from the page.
Sound familiar? Perhaps you take a little bit more care when choosing who to follow. However....
You have to assume that whoever's looking at your twitter feed is in this situation. It's a fast-paced community. The adage of not judging a book by its cover gets thrown to the wayside all too often. Here are some tips that can be a point in either column.
1. All @ Replies. Yes, connecting with people on twitter is what it's all about. However if the entirety of your contribution is messages to someone else, it's not going to be an asset to my twitter feed, especially if they're each mid-twitter conversation snippets - too many lols (or variants thereof) as whole tweets is not a good thing. Short responses are good, but having them comprise all your tweets is just too much.
Exception: If the person is magnet for followers and has accrued thousands, then it's understandable that they could have several personal messages in a row. It's actually commendable that they take the time to go through their @ Replies and answering them. In this case, I usually take a few extra seconds to navigate to their second and third pages in order to get a good cross-section of what they are contributing on their own
2. NO @ Replies. This is broadcasting that your twitter presence is a one way street. No matter what someone may respond to you, you're not participating in the communal conversation.
Exception: N00bs. They just started out and/or are following only a few people. Can't fault them for that.
3. All less than 50 characters. Continual partial attention services like twitter and status updates get a bad rap for having too much inconsequential data. I don't need to know how you wish you had gotten more sleep, or hear about it in both the night and morning, or hear the same gripe on repeat every night. Yes, brevity is wit, and if everyone were like hodgman this wouldn't be an issue. A cursory glance at the tweets will reveal this. Complaining about the weather? Breakfast? DMV lines? Time to move on.
4. Bots. Very easy to spot. Are all entries the first 100 characters of a headline followed by a tinyurl link. Determining if I want a bot in my feed is all about one thing: frequency. Are they writing tweets every minute? I've hit UNFOLLOW on a good share of these feeds after they completely dominated my feed.
Frequency is the first test. If I know it be too large of a presence in my feed, I don't want it in there regardless of how much I may like the source. If it's a source I care about that has that many updates, I'll most likely navigate to the site on my own to get it all at once. After frequency, it's just a question of how much you respect the source. I typically don't add bots that I just stumble on; I have to know the source first.
Strong exception: A feed that is partially a bot and partially a person. This tells me that Yes, the source has an automatic twitter service, but it also has someone manning the controls. This means that there is an opportunity for dialog. And what's more, this person is most likely a fullly dedicated resource to handle the source's day-to-day internet presence, if not specifically social media or even just twitter. Take-away: they're ready to listen.
5. Re-tweets / description + link. A big plus. You're taking what you find interesting and spreading it to a new group of users - about the best display of the twitter engine chugging. I'll gladly eschew the "No @ replies" rule if the user is committed to spreading informative links. Great example: elijahmanor.
6. Informative/relevant tweets in this small sample set? That said, don't necessarily write someone off if their most recent tweets cause a lightbulb to go off in your head or fireworks in your heart. You're not judging if each and every tweet of theirs will change your life but if their contribution will add to the value of your personal feed. You're like a college basketball scout: it's not "are they of value right now, given this small sample set?", but "Based on the cues I'm gathering, do they have the potentional to add value?"
Now take a minute to look at your twitter feed. What does it say about you?
