<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>lbrandy.com</title>
	
	<link>http://lbrandy.com/blog</link>
	<description>{ on programming and the internets, every monday }</description>
	<lastBuildDate>Thu, 06 Oct 2011 00:14:33 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/lbrandy" /><feedburner:info uri="lbrandy" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>RIP Steve Jobs</title>
		<link>http://feedproxy.google.com/~r/lbrandy/~3/XdYARd1x8-I/</link>
		<comments>http://lbrandy.com/blog/2011/10/rip-steve-jobs/#comments</comments>
		<pubDate>Thu, 06 Oct 2011 00:14:33 +0000</pubDate>
		<dc:creator>louis</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://lbrandy.com/blog/?p=1550</guid>
		<description />
			<content:encoded><![CDATA[<p> <img src='http://lbrandy.com/blog/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' /> </p>
<img src="http://feeds.feedburner.com/~r/lbrandy/~4/XdYARd1x8-I" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://lbrandy.com/blog/2011/10/rip-steve-jobs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://lbrandy.com/blog/2011/10/rip-steve-jobs/</feedburner:origLink></item>
		<item>
		<title>Google Voice, LOL</title>
		<link>http://feedproxy.google.com/~r/lbrandy/~3/Y0sCjiEHiWk/</link>
		<comments>http://lbrandy.com/blog/2011/07/google-voice-lol/#comments</comments>
		<pubDate>Fri, 08 Jul 2011 19:00:14 +0000</pubDate>
		<dc:creator>louis</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://lbrandy.com/blog/?p=1548</guid>
		<description><![CDATA[It would appear the recent &#8220;migration&#8221; emails that google apps has been sending me weren&#8217;t messing around. At some point my &#8220;apps&#8221; got migrated, and with it went my google voice number. None of my google voice accounts show the google-voice number anymore, the number no longer forwards to my phone, and there appears to [...]]]></description>
			<content:encoded><![CDATA[<p>It would appear the recent &#8220;migration&#8221; emails that google apps has been sending me weren&#8217;t messing around. At some point my &#8220;apps&#8221; got migrated, and with it went my google voice number. None of my google voice accounts show the google-voice number anymore, the number no longer forwards to my phone, and there appears to be no way to get it back. Google just turned my phone off. The best part is I still get emails for the account that apparently has the phone number (it thinks my account name is: louis%<a href="http://lbrandy.com/" target="_blank">lbrandy.com</a>@<a href="http://gtempaccount.com/" target="_blank">gtempaccount.com</a>) hooked up, but I can&#8217;t actually log into it, or use it, or transfer it. At least no way I can figure out.</p>
<p>So the very reason for google&#8217;s voice attractiveness (existence?), to save me the pain of having to update people with new phone numbers, appears to have backfired gloriously.</p>
<img src="http://feeds.feedburner.com/~r/lbrandy/~4/Y0sCjiEHiWk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://lbrandy.com/blog/2011/07/google-voice-lol/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://lbrandy.com/blog/2011/07/google-voice-lol/</feedburner:origLink></item>
		<item>
		<title>I’m buying facebook at $50b</title>
		<link>http://feedproxy.google.com/~r/lbrandy/~3/Xa_12A6Ocm8/</link>
		<comments>http://lbrandy.com/blog/2011/01/im-buying-facebook-at-50b/#comments</comments>
		<pubDate>Mon, 31 Jan 2011 02:08:02 +0000</pubDate>
		<dc:creator>louis</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://lbrandy.com/blog/?p=1539</guid>
		<description><![CDATA[I&#8217;ve been silent for a few weeks (months, gah) now, but with good reason. I&#8217;ve spent the better part of the last few weeks interviewing all over SF and silicon valley. I&#8217;ll just skip to the end: I&#8217;ve accepted a job at Facebook and my wife and I will be relocating to silicon valley in [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been silent for a few weeks (months, gah) now, but with good reason. I&#8217;ve spent the better part of the last few weeks interviewing all over SF and silicon valley. I&#8217;ll just skip to the end: I&#8217;ve accepted a job at Facebook and my wife and I will be relocating to silicon valley in the next few weeks.</p>
<p>So up first, my current job. As you may or may not know, I work at a startup face recognition company in Pittsburgh. Our decision to relocate was largely for personal reasons, and choosing to leave my job wasn&#8217;t easy. It&#8217;s an awesome place to work. If you are interested, we are still hiring.</p>
<h3>Interviewing</h3>
<p>The job market in California seems quite good for software engineers right now. If you are thinking about heading west, I would wager a guess that now is a great time to do it. The whole process was extremely quick (once I was in contact with a recruiter). I ended up with a handful of offers from several really awesome places (names ranging from the biggest, to awesome little places you&#8217;ve only maybe heard of).  Having spent quite a bit of time the last 5 years being the interviewer, it was quite enlightening to be on the other side of it, for once. To be sure, there&#8217;s another blog post hiding behind that last sentence.</p>
<p>Though the vast majority of my interviews ended up with offers, it wasn&#8217;t all good news. I actually got phone-screened by one startup company. I have tons of opinions about the quality of my interviewers throughout this process (and it&#8217;s not all good, by any means), but I can&#8217;t blame that phone screener. I bumblefucked my way through a fairly easy question (I was unprepared, more on that in a bit) and knew I was blowing it. My wife tried to make me feel better after the call but I told her, having done a ton phone screens myself, that was a definite &#8216;no&#8217;. In fact, if he wanted to interview me after that phone screen, I&#8217;d have questioned his whole interview philosophy to begin with! There&#8217;s probably another blog post hiding there, too.</p>
<p>In the end, the blown phone screen was a nice wake-up call that I needed to do some more review. I did, and the rest of my interviews went extremely well. Had that phone screen been later in the process, I&#8217;m certain it would have turned out much better for me, but c&#8217;est la vie.</p>
<h3>Facebook</h3>
<p>In the end, the decision to choose Facebook was fairly difficult (and somewhat personal) because I had such a fantastic group of offers. All of them had huge upsides, and the thought of saying &#8220;no&#8221; to any one elicited this &#8220;are you crazy?&#8221; feeling in me. The reasons Facebook stood out, for me, was because 1) they have tons of the types of problems I want to learn about, and solve, 2) they are one of the few places in the world where you can learn about things at that scale, and 3) over the next few years, Facebook is almost certainly going to be a fascinating place to be.</p>
<p>This post was mainly an update to anyone who frequently reads. In the next few posts, I&#8217;ll go back to the more general style, probably talking a bit more about the interview and the interviewing process. I feel like I could write a book, at this point.</p>
<img src="http://feeds.feedburner.com/~r/lbrandy/~4/Xa_12A6Ocm8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://lbrandy.com/blog/2011/01/im-buying-facebook-at-50b/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		<feedburner:origLink>http://lbrandy.com/blog/2011/01/im-buying-facebook-at-50b/</feedburner:origLink></item>
		<item>
		<title>Memory mapped IO for fun and profit</title>
		<link>http://feedproxy.google.com/~r/lbrandy/~3/SCSHBajcDOo/</link>
		<comments>http://lbrandy.com/blog/2010/11/memory-mapped-io-for-fun-and-profit/#comments</comments>
		<pubDate>Sun, 14 Nov 2010 23:57:31 +0000</pubDate>
		<dc:creator>louis</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://lbrandy.com/blog/?p=1509</guid>
		<description><![CDATA[What I am going to describe below is a fairly straightforward application of memory mapped IO to get huge benefits versus normal IO when loading static, but large, data structures. You can find the code for it here (note: linux only, though it would be an easy windows port). For our face recognition SDK, our [...]]]></description>
			<content:encoded><![CDATA[<p>What I am going to describe below is a fairly straightforward application of memory mapped IO to get huge benefits versus normal IO when loading static, but large, data structures. <a href="https://github.com/lbrandy/blog_examples/blob/master/mmap/zerocopy.c">You can find the code for it here (note: linux only, though it would be an easy windows port).</a> For our face recognition SDK, our model files are large binary files that are full of various bits of (odd-sized) data. We recently flattened everything out and made our initialization, effectively, zero-copy. This is a write-up of that process on a contrived example to make it a bit simpler. If you&#8217;ve ever used mmap to persist a large static data structure, you&#8217;ll probably find nothing of value here, but for the rest, read on.</p>
<p>For this example, consider a very large list of strings (and their lengths) that we need to persist to disk. Or, a very large array of these:</p>
<pre>typedef struct {
  char *data;
  int len;
} data_t;
</pre>
<p>To make the problem non-trivial, we will consider the case of different length strings (in my tests I was using a million strings around 150 bytes in length &#8212; plus or minus a few to keep them from all being the same size).</p>
<h3>Let&#8217;s get started</h3>
<p>Here is a naive read/write for this data structure:</p>
<pre>void naive_write(char* filename, data_t* data, int n)
{
  FILE *f;
  int i;
  f = fopen (filename, "wb" );
  fwrite(&amp;n, sizeof(n), 1, f);
  for (i=0;i&lt;n;i++)
  {
    fwrite(&amp;data[i].len, sizeof(data[i].len), 1, f);
    fwrite(data[i].data, sizeof(char), data[i].len, f);
  }
  fclose(f);
}

data_t* naive_read(char* filename)
{
  data_t* answer;
  FILE *f;
  int i, n;
  f = fopen (filename, "rb");
  fread(&amp;n, sizeof(n), 1, f);
  answer = malloc(n * sizeof(data_t));
  for (i=0;i&lt;n;i++)
  {
    fread(&amp;answer[i].len, sizeof(answer[i].len), 1, f);
    answer[i].data = malloc(sizeof(char) * answer[i].len);
    fread(answer[i].data, sizeof(char), answer[i].len, f);
  }
  return answer;
}
</pre>
<p>We first write the number of strings, and then for each string we write its length followed by the string data. When reading, we read the number of strings (and malloc the array of strings) and then for each string we read its length (to malloc the string data itself) and then read the string. This implementation does many mallocs() and it does many freads(). It is, consequently, fairly slow.</p>
<p>With this data format on disk, though, this would be difficult to (easily) optimize much more. So, let&#8217;s change the layout.</p>
<h4>A better file format</h4>
<pre>
void flat_write(char* filename, data_t* data, int n)
{
  FILE *f;
  int i;
  f = fopen (filename, "wb" );
  fwrite(&#038;n, sizeof(n), 1, f);
  for (i=0;i&lt;n;i++)
    fwrite(&#038;data[i].len, sizeof(data[i].len), 1, f);

  for (i=0;i&lt;n;i++)
    fwrite(data[i].data, sizeof(char), data[i].len, f);

  fclose(f);
}
</pre>
<p>We made a very minor change to how we write the data structure to disk. This time, we put all of the lengths up front, and then all of the strings after that. This is going to allow us two rather large simplifications: 1) we can malloc() once for all of the data strings, 2) we can read them all with a single fread(). This is a pretty big change because it effectively removes all malloc() pressure and puts everything into fread(). The read function does, though, become slightly more complicated:</p>
<pre>
data_t* onecopy_read(char* filename)
{
  data_t* answer;
  FILE *f;
  int i, n, total;

  f = fopen (filename, "rb");
  fread(&#038;n, sizeof(n), 1, f);
  answer = malloc(n * sizeof(data_t));

  total = 0;
  for (i=0;i&lt;n;i++)
  {
    fread(&#038;answer[i].len, sizeof(answer[i].len), 1, f);
    total += answer[i].len;
  }

  answer[0].data = malloc(sizeof(char)*total);
  fread(answer[0].data, sizeof(char), total, f);  /* one giant fread */

   /* set all the ptrs */
  total=0;
  for (i=0;i&lt;n;i++)
  {
    answer[i].data = answer[0].data + sizeof(char)*total;
    total += answer[i].len;
  }

  return answer;
}
</pre>
<p>This is a decent optimization but not ideal. fread, still, involves a copy (copying from disk into our program&#8217;s address space). The only way around this problem is to not use fread.</p>
<h4>Towards zero-copy</h4>
<p>Using the exact same file, we can open it using mmap() (windows users see: <a href="http://msdn.microsoft.com/en-us/library/aa366761%28VS.85%29.aspx">MappedViewOfFile</a>). This maps the file directly into our address space without having to do any copying whatsoever. We can then just setup all the data pointers to point to the memory mapped file, and we&#8217;ve initialized our data structure with zero copies.</p>
<pre>
data_t* zerocopy_read(char* filename)
{
  data_t* answer;
  int f;
  int i, n, total;
  char *data_ptr;
  int* hdr_ptr;
  struct stat sb;

  f = open (filename, O_RDONLY);
  fstat(f, &#038;sb);
  hdr_ptr = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, f, 0);

  n = *hdr_ptr++;
  answer = malloc(n*sizeof(data_t));

  for (i=0;i&lt;n;i++)
    answer[i].len = *hdr_ptr++;

  data_ptr = (char*) hdr_ptr;

  total=0;
  for (i=0;i&lt;n;i++)
  {
    answer[i].data = data_ptr + sizeof(char)*total;
    total += answer[i].len;
  }

  return answer;
}
</pre>
<h4>Timings</h4>
<p>So the moral of the story, then, is how much faster are the various versions. Here&#8217;s some timings:</p>
<pre>(for num_strings=1,000,000 and str_len= ~150 bytes)
naive_read time: 0.373289
1copy_read time: 0.202408
0copy_read time: 0.011233
</pre>
<p>It&#8217;s important to understand what we are actually measuring here. I ran this test on Linux (Windows isn&#8217;t much different) which has a file cache. The actual disk wasn&#8217;t ever really involved in this program or these timings. We are really testing the summed speed of our processing, malloc() (in the first case), fread(), and the kernel&#8217;s handling of cached files. (note: if you modified this program to only read the files, and then reboot your machine to clear the file-cache, the first run would be much slower as the file was read in from disk and cached).</p>
<h3>Benefits</h3>
<p>Having these large static data structures persist using memory mapped files has some rather significant advantages beyond just initializing extremely fast. We&#8217;ve shifted the entire burden of managing this memory onto the operating system. If multiple processes are being run, this memory will be shared amongst them. This is not true of the fread() solution. The OS&#8217;s file cache will effectively keep the file in memory and ready to go whenever it&#8217;s been recently run. If there are portions of your data you aren&#8217;t using (in face recognition imagine a scenario where you only do face detection), the rest will never get loaded (or will eventually be swapped out). And last, but not least, the flattening process puts all of the bits contiguous in memory (compare with the naive approach), allowing for the OS to handle the pages of data much more efficiently.</p>
<img src="http://feeds.feedburner.com/~r/lbrandy/~4/SCSHBajcDOo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://lbrandy.com/blog/2010/11/memory-mapped-io-for-fun-and-profit/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://lbrandy.com/blog/2010/11/memory-mapped-io-for-fun-and-profit/</feedburner:origLink></item>
		<item>
		<title>Twitter and other things of this nature</title>
		<link>http://feedproxy.google.com/~r/lbrandy/~3/BdzkA8p78ZQ/</link>
		<comments>http://lbrandy.com/blog/2010/11/twitter-and-other-things-of-this-nature/#comments</comments>
		<pubDate>Sun, 07 Nov 2010 21:58:54 +0000</pubDate>
		<dc:creator>louis</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://lbrandy.com/blog/?p=1503</guid>
		<description><![CDATA[Back when no one knew what twitter was, I described it to a friend as a cross between micro-blogging and instant messenger, he declared that it must be &#8220;the worst place on the internet&#8221;. We were both wrong. At any rate, I&#8217;ve finally decided to join the twitter (activated my long inactive account). You can [...]]]></description>
			<content:encoded><![CDATA[<p>Back when no one knew what twitter was, I described it to a friend as a cross between micro-blogging and instant messenger, he declared that it must be &#8220;the worst place on the internet&#8221;. We were both wrong. At any rate, I&#8217;ve finally decided to join the twitter (activated my long inactive account). You can follow me @lbrandy. Check out my <a href="http://twitter.com/lbrandy/status/1363850423181312#">first epic tweet</a>. If you follow me, I promise I will say funny things at least once per week.</p>
<p>I need people to follow as well, so if you read this site, you should send me your twitter name so I can follow you. You can do this in comments, in email, or, if you are feeling brave, on twitter. That last one will presume I can figure out how to &#8220;receive&#8221; such messages.</p>
<p>I did tons of work on my sites this weekend, so I&#8217;m not really writing a serious post. I upgraded the server (so if you see any badness, please let me know). I also switched on google ads, not because I think you guys will click on any ads, but because I wanted to play around with adsense and learn a bit about it.</p>
<p>Back to normal posting next week.</p>
<img src="http://feeds.feedburner.com/~r/lbrandy/~4/BdzkA8p78ZQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://lbrandy.com/blog/2010/11/twitter-and-other-things-of-this-nature/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://lbrandy.com/blog/2010/11/twitter-and-other-things-of-this-nature/</feedburner:origLink></item>
		<item>
		<title>Using genetic algorithms to find Starcraft 2 build orders</title>
		<link>http://feedproxy.google.com/~r/lbrandy/~3/tSKHAq2ot_0/</link>
		<comments>http://lbrandy.com/blog/2010/11/using-genetic-algorithms-to-find-starcraft-2-build-orders/#comments</comments>
		<pubDate>Mon, 01 Nov 2010 12:23:48 +0000</pubDate>
		<dc:creator>louis</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://lbrandy.com/blog/?p=1473</guid>
		<description><![CDATA[UPDATE: Just to make this explicitly clear: I did not write this program. The fourth sentence makes this fairly clear, but some comments indicate that some people are a bit lazy . I also added a video of the rush to the bottom. I&#8217;ve been on a bit of starcraft kick recently (see my replay [...]]]></description>
			<content:encoded><![CDATA[<p><em>UPDATE: Just to make this explicitly clear: I did not write this program. The fourth sentence makes this fairly clear, but some comments indicate that some people are a bit lazy <img src='http://lbrandy.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . I also added a video of the rush to the bottom.</em></p>
<p>I&#8217;ve been on a bit of starcraft kick recently (see my replay aggregator: <a href="http://replayspider.com/">replayspider</a>). I also work in computer vision, and general AI has always been an interest (see this post on <a href="http://lbrandy.com/blog/2009/04/genetic-algorithms-evolving-human-faces/">&#8220;evolving&#8221; faces</a>). Sometimes strange interests collide. Over on a forum called <a href="http://www.teamliquid.net/">teamliquid</a>, a user by the name of Lomilar <a href="http://www.teamliquid.net/forum/viewmessage.php?topic_id=160231">posted a fairly long thread</a> about a program he had written that optimized build orders for the zerg race in starcraft. He eventually cleaned up his code and <a href="http://code.google.com/p/evolutionchamber/">posted the code to googlecode</a>. The program is called EvolutionChamber (a clever name, as it&#8217;s the name of one of the buildings in the game), and it uses genetic algorithms to find build orders.</p>
<p>This I had to see.</p>
<h3>A quick Starcraft 2 primer: setting up the rules</h3>
<p>Since this is primarily a programming blog, I&#8217;m going to assume you know a fair bit about video games. Feel free to skip this section if  you know anything about starcraft 2, in particular. Essentially, SC2 is a real-time strategy game that starts you with a simple base, some workers, and some resources, and asks you to put those workers to work collecting resources that you can use to build things (including other workers), eventually building up an army and killing your opponent. A build order refers to the exact opening steps you take early in the game that best supports the strategy you are trying to conduct. These early games are all about balancing spending money on your economic foundation (making more workers), making units (rush!), or building new buildings or getting new upgrades (tech!).</p>
<p>Build orders generally only cover the very early game because once you&#8217;ve scouted the enemy, you have to begin to react to what he&#8217;s doing and modifying it as you go. In other words, and a perfect aphorism, no battle plan survives first contact with the enemy. In this way, build orders in real-time strategy are very much akin to openings in chess. They set up the soul of the entire game about to be played, and some players prefer to force certain types of games, depending on what kind of opening they choose to do.</p>
<p>One of the reasons build-order optimization is so important is that you can discover openings that &#8220;hard-counter&#8221; other openings. If I can get an army of N size into your base when you do opening X, you will always lose.</p>
<p>Enough with the abstract, here&#8217;s what you need to know:</p>
<ol>
<li>The program in question optimizes Zerg build orders (which is one race in starcraft), this is a rather significant choice because the mechanics of the zerg race are arguably the most difficult to manage (esp. for build order optimization).</li>
<li>Of most interest are &#8220;rush&#8221; build orders. This means &#8220;how quickly can I get N of this type of unit?&#8221;.</li>
<li>There are two primary resources that workers collect in starcraft: gas and minerals.</li>
<li>Zerg also have a third de factor resource: larva. Larva are used to create ALL zerg units, including workers.  So long as you have less than three,  they regenerate at a fixed rate (note: this means any time spent at three larva delays all future larva production &#8212; very bad).</li>
<li>Most units require some building to be constructed in order to be &#8220;unlocked&#8221; (and many of these buildings require others as prerequisites &#8211; this is the so-called tech tree)</li>
<li>Creating a building causes you to lose the worker who creates it (so the longer you can wait, the more resources that worker can collect before building the building)</li>
</ol>
<p>These &#8220;rules&#8221; provide for an extremely complicated search space to find optimal build orders. Essentially, you want to make the exact number of workers you need as quickly as possible (and then no more). Losing a worker when you make a building, and delaying all future larva when at three larva make the dynamics extremely complicated.</p>
<h3>How EvolutionChamber works</h3>
<p>At its core, the program is a <a href="http://en.wikipedia.org/wiki/Genetic_algorithm">genetic algorithm</a>. For those of you who don&#8217;t know, a genetic algorithm is a type of optimization algorithm that tries to find optimal solutions using a method analogous to biologic evolution (to be specific: descent with modification &amp; natural selection). Put simply, you take a &#8220;population&#8221; of initial build orders, evaluate them for fitness, and modify the population according to each element&#8217;s fitness. In other words, have the most successful reproduce.</p>
<p>The program&#8217;s input is simply the desired game state. In practice, this means &#8220;make N units&#8221; to determine some rush build order (but it also allows for other types of builds, like make N workers with some defensive structures and a small army). Here are some of the highlights:</p>
<ol>
<li>It&#8217;s written in Java using <a href="http://jgap.sourceforge.net/">JGAP</a>.</li>
<li>A &#8216;chromosome&#8217;, in this case, is an array of &#8216;actions&#8217; that can be done in game. (e.g, 1) Build a drone. 2) Build a drone. 3) Build a spawning pool. 4) Build an overlord. And so on.)</li>
<li>Invalid actions (ie, trying to build a unit you cannot build because you do not have the tech necessary) are ignored (this allows for &#8220;junk dna&#8221;).</li>
<li>An action that can&#8217;t be done YET (not enough minerals!) causes the simulation to wait until it can be done.</li>
<li>It uses some fairly standard mutation types (deletion, insertion, and one strange one called &#8220;overlording&#8221; &#8212; heh)</li>
<li>It uses the &#8220;many villages&#8221; approach where there are several separate populations evolving independently.</li>
<li>Populations that are deemed to be stagnant are annihilated and replaced by a variant of the most successful.</li>
<li>The fitness function is really a measure of distance from the &#8220;desired&#8221; state and the current state (this is measured by the difference in resources required to get there), taking into account the time required (less time is always better).</li>
</ol>
<h3>The 7-roach rush</h3>
<p>For the starcraft nerds among you, here&#8217;s one of the very first builds constructed by the program:</p>
<pre>10 extractor-trick to 11
11 overlord
11 spawning pool
15 extractor
16 queen (stop drones here)
18 overlord
18 roach warren
17 overlord (yes, two)
spawn-larva on queen when she pops
roach x7</pre>
<p>This is a fairly fascinating build order in a number of respects.</p>
<p>First, from a starcraft perspective: it is incredibly strong. To be clear, I am certain virtually anyone who practiced this build and went onto the ladder and used it in every game would very easily rise to diamond level (currently the highest league). The seven roaches at that time in the game will destroy all but the most well-executed counter-builds. It caused an <a href="http://us.battle.net/sc2/en/forum/topic/902030865">almost immediete stir on the starcraft forums</a>, and had one player proclaiming that an all-in variant (&#8220;all-in&#8221; coming from poker and to mean that you win or lose right now, in this case it means attacking with all your workers as well as your army) was <a href="http://us.battle.net/sc2/en/forum/topic/902031213">completely unstoppable in a certain matchup</a>.</p>
<p>Second, as far as I know, this build was &#8220;discovered&#8221; by the program (or at least, it&#8217;s never been well known). There is a similar build that&#8217;s been well known called the 5-roach-rush (the 5-roach-rush comes later, and is a bit more economical). When comparing the two, the 5RR has certain situational advantages, but the 7RR build, above, has two staggeringly obvious advantages: 1) you get two more roaches, 2) you get them almost 45 seconds sooner. I&#8217;m not 100% certain this build was &#8220;discovered&#8221; by the program, but I do know it&#8217;s not been extremely popular or considered standard play so my guess is that it&#8217;s not been studied in too much detail.</p>
<p>The most interesting part of this build, however, is how counter-intuitive it is. <strong>It violates several well-known (and well-adhered-to) heuristics used by Starcraft players when creating builds. </strong>Some of this may be lost on you non-starcraft players, but I&#8217;ll do my best to explain.</p>
<p><strong>Extractor trick. </strong></p>
<p><strong></strong>The extractor trick is using a drone to build an extractor (remember this removes the drone), then build a replacement drone, then cancel the extractor, giving you the original drone back &#8212; this allows you to build one more worker than your supply would allow. The extractor trick, as used above, has been tested and seen to be economically inferior to a more standard play of buying an overlord on 9 supply. The extractor trick + very early spawning pool do some economic damage and induce a small larva delay, so they are almost never seen. In this case, however, the extractor trick + early pool end up speeding up the entire tech path (this is the primary reason why the 7RR produces roaches much sooner than the 5RR).</p>
<p><strong>Double Overlord at 18/17.</strong></p>
<p>First, a quick discussion about supply. Each unit you create costs supply. So long as you have supply, you can make units. Most RTS games have a similar concept. Overlords, for the zerg, are the unit that provides this supply. So for zerg, you have to spend 100 minerals to unlock additional supply. In general, it  is considered &#8220;optimal&#8221; for you to have just enough supply to not be supply-blocked. It&#8217;s generally considered wasteful to buy supply when you don&#8217;t need it (since you could spend that money, instead, on units or workers now, and buy the supply later, when you need it).</p>
<p>In almost all cases, it would be extremely wasteful to purchase supply twice in a row. I&#8217;m not sure any starcraft player would look at such a build and consider that a good idea. In this case though, it ends up  working out so perfectly that you&#8217;d actually have to try it any other way to understand how and why. Because your desired goal is 7 roaches, you will need to construct 2 more overlords at some point, but doing them both so early is certainly surprising. During that particular period of the build (around 18 supply), you end up waiting for your roach warren to finish so you can begin creating roaches.  This causes you to max out on larva, stopping regeneration. By moving the second overlord so far up into the build, this larva ends up being &#8220;free&#8221; &#8212; you&#8217;d lose it anyway because the regeneration would stop. So the only penalty to making the second overlord early is minerals, and during this portion of the build, you are not mineral-bound. The net result is that moving that overlord so far up into the build costs nothing and frees up larva regeneration to produce quicker roaches.</p>
<p>This is the type of non-obvious optimization that genetic algorithms excel at.</p>
<p>As for the 7-roach-rush, I&#8217;m certain if you are playing starcraft2, you&#8217;ll see this build quite a bit. As for whatever hidden and game-breaking builds remain undiscovered, that remains to be seen.</p>
<p><strong>UPDATE:</strong></p>
<p>Here&#8217;s a video of someone doing the &#8216;all-in&#8217; variant of this build against a real player:</p>
<p><object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/KH1ucvJomlY?fs=1&amp;hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/KH1ucvJomlY?fs=1&amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object></p>
<p>Thanks to <a href="http://www.rockpapershotgun.com/2010/11/02/genetic-algorithms-find-build-order-from-hell/">rockpapershotgun.com</a> for the writeup and pointing me to this video.</p>
<img src="http://feeds.feedburner.com/~r/lbrandy/~4/tSKHAq2ot_0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://lbrandy.com/blog/2010/11/using-genetic-algorithms-to-find-starcraft-2-build-orders/feed/</wfw:commentRss>
		<slash:comments>186</slash:comments>
		<feedburner:origLink>http://lbrandy.com/blog/2010/11/using-genetic-algorithms-to-find-starcraft-2-build-orders/</feedburner:origLink></item>
		<item>
		<title>Things that I think I think</title>
		<link>http://feedproxy.google.com/~r/lbrandy/~3/uxS2gWhFtv4/</link>
		<comments>http://lbrandy.com/blog/2010/10/things-that-i-think-i-think/#comments</comments>
		<pubDate>Mon, 18 Oct 2010 13:09:29 +0000</pubDate>
		<dc:creator>louis</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://lbrandy.com/blog/?p=1465</guid>
		<description><![CDATA[1. Not everything I think I think can be a &#8220;full&#8221; blog post. 2. Negative Information Theory It&#8217;s a little appreciated fact that being frequently wrong is (adjusted for a prioris) as difficult as being frequently right. For work, I once wrote a pixel classifier that, given an eye region of an image, would identify [...]]]></description>
			<content:encoded><![CDATA[<h4>1. Not everything I think I think can be a &#8220;full&#8221; blog post.</h4>
<h4>2. Negative Information Theory</h4>
<p>It&#8217;s a little appreciated fact that being frequently wrong is (adjusted for a prioris) as difficult as being frequently right. For work, I once wrote a pixel classifier that, given an eye region of an image, would identify red-eye pixels. My classifier worked exactly as I intended it (ie, it was debugged) and yet it was wrong about 80% of the time. It was simultaneously a terrible and terribly effective algorithm.</p>
<h4>3. A Trick for Sales Projections</h4>
<p>For internal use, halve it. For external use, double it. Twice.</p>
<h4>4. The web is such a friggin hack</h4>
<p>Imagine if I told you we were going to build a system to run our company. We&#8217;d use a domain specific language for the data-store, a second language for the back-end code which is run on a server written in a third language, a fourth xml-based approach to explain the documents, a fifth DSL to explain the styling of said document, a sixth language to write the front-end code, and a seventh &#8220;language&#8221; that is actually a library that makes the sixth language usable for our purposes. Would anyone think I was crazy?</p>
<p>Not that the web can easily be &#8220;fixed&#8221; but the existence of things like &#8220;reset.css&#8221; or tables vs css are not just excellent examples of the huge pile of sand people have built castles on, but more precisely, are a testament to a slaughtered paradigm.</p>
<h4>5. The &#8220;one-page resume is a myth&#8221; myth</h4>
<p>I&#8217;ve looked at so many resumes. I try not to be biased but I&#8217;ll be honest. Long resumes are not as good as focused resumes. There isn&#8217;t some rule where over a certain length goes into the trash, it&#8217;s just that everyone gets about the same amount of time, and if yours is long, I&#8217;m more likely to miss something good.</p>
<h4>6. All my best work is done in the shower&#8230;</h4>
<p>Programming work, that is. This is why I never feel guilty if I leave 15 minutes early. I traded 15 terribly unproductive minutes for 15 terribly productive ones.</p>
<img src="http://feeds.feedburner.com/~r/lbrandy/~4/uxS2gWhFtv4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://lbrandy.com/blog/2010/10/things-that-i-think-i-think/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://lbrandy.com/blog/2010/10/things-that-i-think-i-think/</feedburner:origLink></item>
	</channel>
</rss><!-- Dynamic page generated in 0.347 seconds. --><!-- Cached page generated by WP-Super-Cache on 2012-01-06 11:00:55 -->

