Being a Dork, Perl, and Basketball Stats

For the past year or so, I've been dorking around with some of my friends with the idea of a basketball statistic that attempts to measure what a player brings a team. You know, take his points, his assists, rebounds, blocks, etc., and throw them all into one big number. It's been a fun diversion, and an excuse to think about math and some web programming again.
The idea is based on the work done at sonicscentral.com towards something that's been called Points Created (an attempt to parallel Bill James' baseball stat "Runs Created"). It's not perfect, but I've had some dorky fun, and, quite frankly, the ratings have come out moderately ok.
Recently, when I realized I could dynamically update this from the web rather than doing it via Excel, I set out to create a Perl script that would enable me to run it, have it grab the latest stats from the invaluable dougstats.com, and then generate the stats for everybody in the NBA.

A few hours later, I had something working.

#!/usr/bin/perl
use LWP::Simple;
use CGI;
my $query= new CGI;
print $query->header;

The basics: the hash-bang, and includes for LWP (to get the data over the web) and CGI (so I can pass in parameters).

my %Data;
my @row;
my @PlayerStats;
my $PlayerName;
my $PlayerStatsString;
my $url = "http://www.dougstats.com/05-06RD.txt";

my $PointsCreated = 0;
my $PCperG = 0;
my $PCper48 = 0;

Here we set up all of the variables. A hash to contain the player data. Arrays for handling a row of data and a row of player statistics. Scalars for the player name, the string of text representing the data, the URL to get the data, and then some internal values for calculating statistics that aren't in the downloaded data.

my $sort = $query->param('sort');

my $stats = get($url);
die "Couldn't get data" unless defined $stats;

@row = split(/n/, $stats);

shift @row;

Here we get the data. We grab the sort parameter (so I can determine which value to sort on -- more on that later). We go out and get the data (or die, if we can't get it). We split the data on new lines into rows of data in the array—each array element is a full row of text data. Finally, we shift off the top row, since it's the category text and we don't want that in our stats.

foreach (@row) {
($PlayerName, $PlayerStatsString) = split(/s+/, $_, 2);
@PlayerStats = split(/s+/, $PlayerStatsString);

my $DefRebs = $PlayerStats[11] - $PlayerStats[10];

$PointsCreated = $PlayerStats[18] + (0.75 * $PlayerStats[12])
+ (1.03 * ((0.75 * $PlayerStats[10]) + (0.25 * $DefRebs)
+ $PlayerStats[13] + (0.5 * $PlayerStats[15]) - $PlayerStats[14]
- (0.71 * ($PlayerStats[5] - $PlayerStats[4]))));
$PCperG = $PointsCreated / $PlayerStats[2];
$PCper48 = ($PointsCreated / $PlayerStats[3]) * 48;

$Data{$PlayerName} = [@PlayerStats, $DefRebs, $PointsCreated, $PCperG, $PCper48];
}

delete $Data{"Player"};

Ok - here's where some of the magic happens. I iterate through each row of data, and split the row into components: the player name and then the combined player stats. Then I split the player stats into individual stat buckets. I build some of the intermediate stats that aren't in the dataset—defensive rebounds, and then the Points Created and Points Created per Game and per 48 minutes.
Toss everything into a big hash, with the hash key set as the player name, and just make sure there's not an element that is the row of column headers (the delete line). I could probably toss this last line ...

I won't get into the details of the Points Created formula right now, but there's some (limited) intelligence behind those coefficients. Basically, it's an attempt to quantify how many possessions a player creates or loses, turn that into points, and then add in the points the player actually scored to come up with a final total. I've been working on a more refined version with some other folks that better integrates assists and the fact that not all hoops are created equal.
Quite frankly, that's about it. The rest of the script is just output, dumping the data in a simple table to the screen, and throwing in some links to allow some basic sorting. If you check out the Points Created display (or the possibly improved adjusted Points Created), you can see the results of the work.

The basics: both metrics say that LeBron James has created the most overall points this season. The adjusted method has Allen Iverson edging out James for PC/G, whereas the original has James edging out Iverson. The adjusted method likes point guards a lot more than the original method. Part of me thinks it likes them too much, but what do I know.

In summary: I'm a dork, but not a big enough dork to do this stuff as anything more than a part-time hobby.

Hamachi and uTorrent

Just as a public service to myself, in case I forget how to get uTorrent's DHT to work when I'm using Hamachi:

By default, uTorrent binds the uPnP address to the first network adapter it finds, which happens to be the Hamachi virtual adapter. So, DHT won't work through the firewall (and uTorrent performance is generally worse).

In uTorrent, Options->Advanced, set the net.bind_ip and net.outgoing_ip to the IP address that uTorrent should put in the firewall/router via uPnP (i.e. the IP address of the computer). Restart uTorrent, and everything's happy.

Why Nintendo is Awesome

The Nintendo Revolution was already on my "must buy" list the moment it is released. New Zelda game, probably new Mario Kart, and probably new Mario Party with the cool-ass Nintendo controller. Plus, it's backwards compatible with the Gamecube, so I can keep playing all my games and finally finish Wind Waker and Eternal Darkness.
Then they announced that it would be able to download and play Nintendo games like old school Super Mario 3 and Tecmo Bowl.

Then they announced that it would be able to download and play Super NES games like Super Mario Kart and Ken Griffey Baseball.

Then they announced that it would be able to download and play Nintendo 64 games like Mario 64, Mario Kart 64, and Goldeneye.

So, at launch, you'd spend $200 and get a system that could play Revoluion games, Gamecube games, N64 games, SNES games, and NES games. That rocks, especially if the old games are like $1.99 or something.

Yesterday, at GDC, Nintendo announced that the Revolution would also play Sega Genesis games and TurboGrafix 16 games. Basically, almost any game I played as a kid up through college is going to be available to me. That, my friends, rocks. Who needs Grand Theft Auto or ridiculous life-like graphics when I get get a bunch of people together to play 3 different versions of Mario Kart?

Last Ditch Effort?

Yesterday, my former employer put out a press release that they're offering ClayTools systems at a steep discount for attendees of the Game Developers Conference. They've also partnered with a game development studio who will incorporate ClayTools into their development pipeline.

On the one hand, I've reached the point where I'm legitimately glad to hear that the company is making strides to still hit the game developer market. When I was last at the company (about 2.5 years ago), it was the plan then, but there was a hesitance to take a stab at it because the prices would need to come down significantly, and doing that would be the other product lines at risk. Unfortunately, I think that sometimes, you need to take those risks if you want to succeed. The delay in developing both a low-cost haptic device and a low-cost and solid API allowed competitors like Immersion and Novint to catch up in their offerings or increase their market share (Immersion becoming big by suing Sony and Microsoft, Novint introducing a low-cost haptic device called the Falcon that has been extremely well-received).

It's sort of painful to watch. I've still got some good friends at the company (though, sadly, it's much smaller in size, and a good number of talented, intelligent people have left). The company still seems to be muddling through the same problems it faced in 2000-2003, and these problems stem directly from the desire to create a new market segment, while not touching the existing one at all. I think that's a foolish and short-sighted proposition -- the market for high-end haptic devices is a niche, and it's been a profitable and consistent niche for the company. The mass-market is the low-end, low-cost device; by not committing to that goal, it feels to me like things are just on a slow burn towards obsolescence.

Compounding this problem is the fact that a high-tech, high-idea company like SensAble really needs to spend a tiny bit of money and pay a college kid to redo the website. I was involved in the last redesign (and the upkeep of the site), and that was in 2002. Four years later, the site is a mish-mash of bad ideas, poor web writing, and a complete misunderstanding of how to use the web to help your business. It's the site of a company that looks like it's either a) out of business, or b) about to be out of business. A "What's New" article from August 2005? Mismatched fonts and graphics? Just painful, not to mention damn near impossible to tell when there is actually any new news to report.

For the record, I do want the company to succeed. I have no stock in the company, no vested interest in their success. But, it was my first job out of college, and I'm still close to a lot of people who have passed through the company. They gave me a chance to strech my legs a bit as a product manager, which has lead to my current role as an engineering project manager at a web hosting company, which I'm just utterly digging. It was also my first experience with a company at a crossroads, having an identity crisis. Management getting pushed out. Layoffs of long-time employees, followed with hiring people who've worked with the new management before. Spending about two years becoming a company that marketed technology without actually creating it. It was painful, frustrating, and made me want to run as far away from technology as I could.
In short, I really disliked a lot of what the company had become, and how it had gotten there. Almost 3 years later, the company is doing what many of us had suggested (low-cost device with low-cost/free API, aimed at integration with other applications), and that's mildly refreshing. I just hope it's not too late.

I think this is post #1 in my job history series. I've been thinking a lot about work, and how I've gotten to where I've gotten, and yesterday's press release kinda was the tipping point. Post #2 will be about how I did run away from technology for about a year and a half, and how I don't think I could have ended up at a worse place in my entire life. I mean, like, legitimately, the skeeviest, nastiest, weirdest, most fucked up place I've ever been.

I'm Avoiding My Front Door

Someone just rang my door bell, and I have no desire to donate money to anyone's damn cause. Seriously, I hate that it is legal to harrass people into donating to your cause. My cell phone has gone off like twice a day from the same batch of Colorado numbers trying to get me to send more money to the Human Rights Campaign (who I will not link to because they've been a pain in my ass).

So, here I sit, waiting out the person at the door who could be here to give me a giant million dollar check, but is more likely hoping I'll become a member of their organization for the low, low price of only $20 a month. And then I'll get a sticker and a newsletter.

Whee.

It's the year 2006. I've got multiple computers. If I want to give you money, I'll do it over the interweb. Please leave me alone.

Like Drinking Liquid Feces

On the way home from playing some basketball, I stopped at a local grocery store. I wanted to grab a Gatorade or water, but they didn't seem to have any cold. I headed to the cold drink session and saw a selection of Odwalla beverages. You know, the really pulpy/smoothie-type drinks loaded with vitamins and sold at a ridiculous markup. Still, I kinda like the Vitamin C one, but it wasn't what I was in the mood for.

Instead I made the mistake of reaching for the monstrosity known as the Super Protein Chocolate. How can anything chocolate milk related be bad? How can you mess up chocolate milk? SERIOUSLY. HOW CAN YOU FUCK UP CHOCOLATE MILK?

The good people at Odwalla can answer that question. They made a drink that I can only imagine is like drinking ones own feces, having never drunk my own feces (or anyone else's, for that matter). If I'd tasted someone's diarreah, I think it would taste like this. Never in my life have I tasted something so awful, so disgusting that I tossed it after one sip. That's how long the Super Protein Chocolate lasted. One sip.

I'm pretty easy going when it comes to products. Make me a bad burger, I'll probably just eat it and grumble silently. Bring me the wrong food at a restaurant and I'll probably feel bad about asking you to fix it, and then I'll leave a big tip because I feel like I've made your job worse.

I will not go silently into the night about the Odwalla Shit In a Bottle. I will not be silenced. I want to warn anyone who might stumble across this post to save their taste buds and avoid this drink like the plague. I want my $3 back and maybe an extra buck for the emotional and physical distress.

Sorry Odwalla, but this is, literally, shit.

Odwalla Super Protein Chocolate

WordPress 2.01

I finally bit the bullet and upgraded to WordPress 2.01. I had been hesitating since I hadn't wanted to reconfigure my plugins/themes/hacks, but figured I needed to do it eventually. My web host recently launched a nifty "you've got WP 1.5 installed, click this link and we'll upgrade everything for you."

I clicked, it ran, and it just worked. That's really nifty and a really good hosting feature. It's also something that now I'm noodling around in my brain figuring out how to implement something like that for some of the features that the company I work for offers.

WordPress 2.01 has a much nicer interface, with a really great text editing box. It's also got cool AJAX-y fields on the right that let you expand/collapse UI bits you care/don't care about. And now I can add category fields on the fly, which is fricking awesome. The post preview also gets rendered in your theme, so you know pretty much exactly how it'll look.

If you see anything that doesn't work the way it should, drop me a note here.

Opera Web Browser for the DS

A little over a month ago, I reviewed my then-new Nintendo DS, and I'm still using it a ton. Probably the most I've used a game system since Animal Crossing was out for the Gamecube.

Well, one of the things I mentioned in that article was the desire for Nintendo to release a web browser. Nintendo's gone and done one better -- they've worked with Opera to have get Opera running on the Nintendo DS. Here's the press release.

Very very cool.

Random Stuff -- The Super Bowl, Ads, and a Cool Blog

  • The Super Bowl = Super Blow. Poor officiating, ridiculously poor coaching on both sides, and two teams who decided to play about as generic a game as they could. Bravo.
  • The Super Bowl Ads sucked. Except for 2. The two Sprint ads. The first one, with the guy throwing his phone at the other guy as a "crime deterrent" was only mildly funny until he hit him a second time. Then it became really funny. It became a classic when the Sprint splash screen after that actually listed "crime deterrent" as a feature. The second Sprint ad was funny the moment they played "Baby Come Back", and became hysterical when they broke out the Benny Hill-reference. They both can be viewed at Sprinttvads.com.
  • My friend Alex pointed out to me that the founder of the first company I worked for (and one of the smartest people I've ever met), has a blog detailing the construction of his gigantic timberframe house. That's a serious project.

Albums bought recently:
Jenny Lewis and The Watson Twins -- Rabbit Fur Coat
Roger Clyne and The Peacemakers -- Live at Billy Bob's Texas
Clap Your Hands Say Yeah -- Clap Your Hands Say Yeah

A Trip to the Mall

Two interesting things I figured out at the mall yesterday:

1) I'm not on crack. All the kids I've seen sort of gliding around are actually gliding around. They've got little shoes with like a rollerblade wheel in the sole. It's the strangest thing ever. Boys and girls, gliding around, looking like they're sort of floating along a few cm above the ground. And yes, I wish I had them when I was a kid.

2) The whole Macy's/Filene's buyout is kind of annoying. I got a Filene's gift card for Christmas, and they were having some huge sale. I figured I should use it now, since I never shop there and would more likely forget I even had it. There was nothing in Filene's, so I headed down to Macy's, since Filene's had a sign up that Macy's will be accepting Filene's gift cards. "Nifty" I said to myself, and walked down to that end of the mall. I found myself a sweatshirt and some comfy track pants and decided to checkout with my haul.

Alas, I hadn't read the fine print. They won't accept them until Feb 1. So, I could put my stuff back, miss the sale, and try to find stuff at some point in the future. Or, I could pay now and then go back on Wednesday and have them hit the gift card and put the funds back on my credit card. I chose that route, but it made me wonder why they couldn't just take the card now. You know, help the customer out and all.

I guess it doesn't matter in the long run, because they know I need to use the gift card and they already have the money from it. Anyway, I think I'm just cranky I didn't get some roller-shoes.