Maybe I should have posted this here, but I just wrote up my first thirty days on ottoneu at niv.is. Check it out.
Category Archives: ottoneu
Raising Money
Sorry about the recent lack of posts. Things always get quiet towards the end of the year, and I have been traveling and coding and coding and traveling non-stop the last few weeks. Hopefully Geoff and I will get things going again as baseball season nears.
A few days ago, my friend Zach and I had a discussion over email about raising funding for internet startups. The conversation was spurred by this article from the Economist, discussing a possible pending tech bubble. Zach argued that the bubble was clear to him when someone, somewhere decided that the reported $6 billion bid on Groupon by Google was not enough money and called the deal off. My counter point or more accurately put continuation of the conversation is below, slightly edited.
[T]o me the ‘bubble’ aspect is that a lot of pointless things (as the economist points out) are getting funded. Like, pointless things. gimmicky neat tech demos are getting funded. And to me that is the main point of irrationality.
[It] sounds like irrationality is closing in on two fronts though. one on the “spray and pray” front where angels will fund idiotic ideas, and another on the high end where companies are being overvalued. the former seems more pervasive, and the latter seems bubble-y.
Part of this has to do with comfort. Stupid-to-less-than-stupid-to-reasonable-but-unproven ideas should all be bootstrapped. Instead, we have these idiots who are so afraid of eating ramen that they spend all their time and effort on figuring out how to raise money instead of how to build a true business. The raising money is the goal, because then you can make 80k a year doing your own stupid thing and it’s living the dream.
I interviewed with that Sunfire place again last night. the woman I spoke with asked me if i wanted to raise money. I said preferably no, but i could see reasons to. And i said the only reason I would was if I was not at enough users to pay for itself but enough users that I saw a future in the business. And she made a reality-based statement that depressed me – it’d be really hard to raise money if I don’t have the numbers I was hoping for – its easy if you get huge amounts of growth, of course. But I was like – wait, if i have enough users, why would I ever raise money?
And part of that is the thing too. If you don’t have ANY proof, you’re in a better spot than if you have numbers that aren’t stellar. And in a way, that is the investor needing comfort too – either the comfort of the irrational kool-aid or the comfort of stellar numbers. Less than awesome numbers on a launched product that might require (gasp) work was looked at by the woman I was talking to, accurately, as a liability.
So, this is why I don’t want to raise money. mostly because investors are risk-averse assholes. And also because I like ramen.
Despite being one of the risk-averse assholes I called out in that email, Zach liked the response, and the more I think about it, the more I like it as well. I understand the need to raise money even if your startup proves itself spectacularly – growth can always use more capital, etc, etc. But there is a lot of weirdness in the money-raising world, and until I find some people who see things a bit more in line with how I see things, I don’t see myself actively pursuing funding.
The Solution? More Tables
I don’t know very much about database design. I tried taking a course once in college but the professor scared me on the first class and I ended up dropping it, which I guess now that I write it out I regret. What little I do know I have taught myself from both production examples from my previous jobs and just general experience. That being said, there is one rule that I’m slowly learning that I feel the need to record. Whenever there is some sort of complication with storing or retrieving data in a relational database, the solution is always more tables.
Yesterday I was struggling with a problem. If two teams agree to a trade involving a given set of players, all other trade proposals including those players should no longer be active. For example, if Albert Pujols is involved in a trade that has been accepted, he shouldn’t be still out there in other trade proposals – we don’t want two or more teams concurrently accepting trades with Albert Pujols!
On its face, this seems like a basic enough problem, but the twist is my existing database schema. It looks like this:
[cc lang=”mysql”](
ID
int(11) NOT NULL auto_increment,
ProposalDate
date NOT NULL,
ProposingTeam
tinyint(4) NOT NULL,
TargetTeam
tinyint(4) NOT NULL,
ProposingTeamPlayers
varchar(30) NOT NULL,
TargetTeamPlayers
varchar(30) NOT NULL,
ProposingTeamLoan
int(11) NOT NULL,
TargetTeamLoan
int(11) NOT NULL,
Accepted
tinyint(4) NOT NULL default ‘0’,
Rejected
tinyint(4) NOT NULL default ‘0’,
PRIMARY KEY (ID
)
)[/cc]The problem here is that I’m storing “ProposingTeamPlayers” and “TargetTeamPlayers” as strings. So for each trade, I implode the array of players involved into a comma-delimited list. This is all well and good until you realize you want to search for all trade proposals involving a given player. The process then becomes:
- Find all trade proposals involving either team
- Get all players involved in said trade proposals
- Check if any of the players are involved in the just accepted trade
- If yes, then mark that trade proposal as rejected (which, implicitly, it is)
The solution to getting rid of this awful, slow code? Add a new table:[cc lang=”mysql”]TradeProposalID
int(11) NOT NULL,
PlayerID
int(11) NOT NULL,
TeamID
int(11) NOT NULL,
PRIMARY KEY (TradeProposalID
,PlayerID
)[/cc]Then this query gives us what we want:[cc lang=”php”]$sql = “SELECT ID FROM TradeProposalsIndexTable JOIN TradeProposalsPlayersTable ON ID=TradeProposalID WHERE Accepted=’0′ AND Rejected=’0′ AND PlayerID=$playerID”;[/cc]Mark all those trades as rejected, and all done.
Without adding new tables, I had a mess trying to store multi-player trades in my database, and I had a hacky solution that clearly was not well thought out. Add a table, remove those ProposingTeamPlayers and TargetTeamPlayers columns, and a multi-line complete mess of a solution turns into 2 elegant lines of SQL.
So we’re left with a simple rule to follow at all times: if you are getting confused by how to get or store data with your relational database, add some more tables.
First Pitch
We are now 4 days back from First Pitch: Arizona. I can’t speak for Geoff, but for me, the conference was a rousing success. If you had told me all the positive things that happened in Phoenix were going to happen before going, I would have gladly paid twice. An overview:
- Maybe not the father of fantasy baseball, but at least a pretty influential uncle, Ron Shandler is the real deal when talking about one of the game’s great minds. He presented a new approach to fantasy baseball at the conference, one which is similar to my game in a number of ways. We met (along with Geoff!) for a hour after he presented his game, and he came away with a very favorable impression of what I’ve been working on. He even mentioned it as legit to the entire conference. If that isn’t a strong endorsement, I don’t know what is.
- Joel Henard at Baseball Prospectus Radio offered to interview me for his show. Repeat: I was offered an interview. About a fantasy baseball game. From the guy who has interviewed real, actual GMs and baseball players.
- Everyone, without exception, who got a taste of my full pitch was asking me things varied from “sounds awesome” to “when can I sign up?”
- Joe Sheehan recognized me coming up an elevator and asked if I’d be around later at the Rising Stars baseball game or for poker later on. Swoon.
The amazing thing about having people, real live people validate what you’re spending hours a day working on – what you’ve left a safe, comfortable job, to work on – is that it makes you forget everything else. It just makes you want to f’n code the hell out of it. So, I’ll say it again: let’s go.
The Implications of Inning Limits
Recently I ran across a problem with my deployed version of ottoneu: there was no programmatic limit for games played or innings pitched for any given fantasy team.
Let me back up.
Fantasy baseball games, in general, have limits around the number of games a team can ‘play’ at any given position. For example, if there are 162 games in a season, it doesn’t make much sense to allow an owner to manipulate his lineup enough to squeeze 170 games out of a given position. This might not be possible for all owners, and it doesn’t fit in with the idea of mapping fantasy baseball to real baseball. If the Yankees can’t go out on their off days and get extra games in with their backups, well, then, neither should your fantasy team that you’ve cleverly named after them.
Most games will run with limits along the lines of 162 games per lineup slot, which includes things like 162 games per OF spot (in our 5OF lineup, we have a 810 game limit at OF) as well as 162 games per special position (middle infielder, corner infielder, utility). Innings pitched can be a little bit more varied, but we went with the tried and true multiply 162*9 and add a few extra to get to a nice round number, so our limit is 1500IP.
So back to where I started – ottoneu did not have programmatic enforcement in place for when teams went over these limits. While this isn’t a gigantic problem with 12 owners, once you’re talking a game with leagues on the order of 100, then yes, we have a problem. I decided to tackle the problem last week, and immediately an interesting question arises: when does the inning limit kick in? Can you run right up to 1499.2 IP at the end of a day, and start 5 guys the next day and then the limit kicks in at the end? Should there be a rule put in place that says when you are within some arbitrary distance of the 1500 IP limit at the end of a day, you can’t play any more pitchers? Or the final option – add up stats until you hit 1500 IP, no matter if it is in the middle of a start or an appearance by a reliever or anything?
I kicked this question around with Geoff for a little bit, and we decided that first off, it made no sense to build any kind of lower buffer. A 1500 IP limit doesn’t mean “1495-1500”. We thought about calculating the inning count at the end of every day, but this solution again made a inning limit closer to “1500-1525” instead of the hard 1500IP cap that we wanted. So that leaves the last option.
The idea of these limits, like I said, is not only to put everyone on equal footing, but also to line up the game against real baseball. This last option, in which the live scoring script literally stops taking your statistics once you hit 1500IP, is the least connected to real baseball. If I’m at 1497.0 IP, and Jon Lester going on the last day of the season (I know, it would never happen), there’s a very real possibility that only half of his start will count towards my fantasy statistics. Cutting off starts in the middle divorces the idea that these player’s days line up perfectly with your team’s day.
We decided to go with this option, but where does that leave ottoneu? The first line of ottoneu’s constitution is as follows:
The intention of this league is to mimic the job of an actual general manager as closely as possible.
Clearly managing who plays and who doesn’t on a day to day level isn’t on a GM’s plate, but if we our goal is to reflect reality, this solution is actively hurting us. I don’t have any good answers here – one can go down the path of explaining the other ways ottoneu isn’t like real baseball, but I am not sure if that is a compelling defense of this solution. Clearly the alternatives we discussed were not better than what we decided on, but maybe there was something else I didn’t consider? I open it to the floor.
The best part of designing a game is that you get to run into interesting questions about the system you are building all the time. Of course, that is also the worst part.