<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>close enough</title>
  <id>http://vincentwoo.com</id>
  <updated>2011-01-20T00:00:00Z</updated>
  <author>
    <name></name>
  </author>
  <entry>
    <title>a window</title>
    <link href="http://vincentwoo.com/2011/11/09/a-window/" rel="alternate"/>
    <id>http://vincentwoo.com/2011/11/09/a-window/</id>
    <published>2011-11-09T00:00:00Z</published>
    <updated>2011-11-09T00:00:00Z</updated>
    <author>
      <name></name>
    </author>
    <summary type="html">&lt;p&gt;This is what my place looks like, sometimes&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;This is what my place looks like, sometimes&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>NodeJS is easy, just ask her out</title>
    <link href="http://vincentwoo.com/2011/06/10/nodejs-is-easy-just-ask-her-out/" rel="alternate"/>
    <id>http://vincentwoo.com/2011/06/10/nodejs-is-easy-just-ask-her-out/</id>
    <published>2011-06-10T00:00:00Z</published>
    <updated>2011-06-10T00:00:00Z</updated>
    <author>
      <name></name>
    </author>
    <summary type="html">&lt;p&gt;NodeJS is the hot new girl on the block. You&amp;rsquo;ve flirted a few times at meetups,
and you think you could probably get a date with her if you asked. However, you
still have doubts. She&amp;rsquo;s smart, but sometimes it feels like her Inception-esque
nested callback conversation is out of your league. Maybe you&amp;rsquo;d be better off
getting back together with Rails. You find yourself missing her concise,
imperative style during tedious stretches of smalltalk on dates with other
programming languages. She was nice, and she loved you&amp;hellip;&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;NodeJS is the hot new girl on the block. You&amp;rsquo;ve flirted a few times at meetups,
and you think you could probably get a date with her if you asked. However, you
still have doubts. She&amp;rsquo;s smart, but sometimes it feels like her Inception-esque
nested callback conversation is out of your league. Maybe you&amp;rsquo;d be better off
getting back together with Rails. You find yourself missing her concise,
imperative style during tedious stretches of smalltalk on dates with other
programming languages. She was nice, and she loved you.&lt;/p&gt;

&lt;p&gt;You tell yourself, &amp;ldquo;No, she was nice, but limited.&amp;rdquo; You remind yourself that you
put yourself back in the web development market for a reason. But you worry that
your years of blissful content with Rails have dulled your ability to satisfy
new frameworks.&lt;/p&gt;

&lt;p&gt;Well, fear not. She&amp;rsquo;s easy. I don&amp;rsquo;t even mean that in a pejorative sense, she&amp;rsquo;s
easy in a way that will make you feel good about yourself. What&amp;rsquo;s that? Too much
information, but you want to hear how we got together anyway? Well, alright.&lt;/p&gt;

&lt;h3&gt;Hooking Up&lt;/h3&gt;

&lt;p&gt;I started &lt;a href="http://multiplayerset.com"&gt;MultiplayerSet&lt;/a&gt; with the intention of making a realtime game that
my non-savvy friends could play with nothing more than a browser. I thought
about how I might do this. Canonical options for realtime behavior in a browser
were limited. You could do something with &lt;a href="http://juggernaut.rubyforge.org/"&gt;Juggernaut&lt;/a&gt; and Rails, but, ugh.
Flash on every page? But what about the server side? How do you architect that?
A controller action for every game action? But actions have context, game state,
oh no am I going to have to make a model just to represent the state of every
game oh god session management I already have a day job.&lt;/p&gt;

&lt;p&gt;I figured I might as well ask NodeJS out. She seemed worldy, yet content. Her
mantra of &amp;ldquo;Javascript on the server, Javascript on the client&amp;rdquo; just &lt;em&gt;jived&lt;/em&gt; with
me. She had simple answers to all my questions. Just build the server game
states in memory, just hack together jQuery on the client side for interaction.
Let &lt;a href="http://socket.io/"&gt;Socket.IO&lt;/a&gt; take care of the heavy lifting. All these things are great,
but the reason they are all great gravitate around one reason: &lt;em&gt;NodeJS is
fundamentally unstructured&lt;/em&gt;. Everything you might ever want to do in the context
of a web service is present at every layer of abstraction in NodeJS &lt;em&gt;because
there is basically no abstraction&lt;/em&gt;. Rails is nice because she is fundamentally
structured by convention. Doing anything that Rails isn&amp;rsquo;t already comfortable
with doing is hard.&lt;/p&gt;

&lt;h3&gt;Doing it Right (the Wrong Way)&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://blog.ankurgoyal.com/post/6433642218/node-js-is-backwards"&gt;Some people&lt;/a&gt; are just &lt;a href="http://yehudakatz.com/2011/06/14/what-the-hell-is-happening-to-rails/"&gt;not going to get it&lt;/a&gt;. They&amp;rsquo;ll point at how
disorganized Node is, and ask why you didn&amp;rsquo;t go for the more classy Erlang or at
least stay with the more successful Rails. What they&amp;rsquo;re missing is that Node
doesn&amp;rsquo;t care that you don&amp;rsquo;t know what you doing. &lt;em&gt;Node is always ready to
party&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;There&amp;rsquo;s no configuration, no convention. Sure, you can have those things if you
want them, but Node doesn&amp;rsquo;t care. She&amp;rsquo;ll let you go from zero to highly
performant (if somewhat kludgy) web service in a tiny amount of code. Writing a
game? Sure, you could set up a structured &lt;a href="http://nowjs.com/"&gt;RPC framework&lt;/a&gt;, or you could just
say &lt;em&gt;fuck it&lt;/em&gt;, drop in some websocket support and hit the ground running. Giant
switch statement in a master method that runs on your server? Rubyists cringe,
but if you&amp;rsquo;re smart you understand that all that fancy routing is just so you
can pretend you won&amp;rsquo;t have to do some regexes later. &lt;em&gt;Just do it&lt;/em&gt;. Node gets
that you are a &lt;a href="http://programming-motherfucker.com/"&gt;programming motherfucker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Picking Node means understanding that the value of a framework is the difference
between the time savings of its useful abstractions and the tedium of its
&lt;a href="http://steve-yegge.blogspot.com/2010/07/wikileaks-to-leak-5000-open-source-java.html"&gt;useless ones&lt;/a&gt;. Picking Node means understanding that for the intersection
of certain classes of problems and the majority of web frameworks that value is
actually negative. Node doesn&amp;rsquo;t have any baggage and she expects that you leave
yours behind, too.&lt;/p&gt;

&lt;h3&gt;Common Misgivings&lt;/h3&gt;

&lt;p&gt;Alright, I can see you have some reservations about the relationship working out
long term. You raise excellent points about commonJS pain, callback hell, and
having to reinvent various wheels. You point to Rails and her higly streamlined
experience when coding a certain class of project. I say yes, you have a point.
Doing a lot of things you might do in a couple lines in Rails can be aggravating
in Node. She&amp;rsquo;s not very refined, and you have to explain things to her in a very
specific way, and this can be frustrating.&lt;/p&gt;

&lt;p&gt;But you know the truth, right? You can see the writing on the wall. The web is
getting more complicated. The hard things that need doing at scale aren&amp;rsquo;t just
serving web pages and responding to REST requests anymore. The hard problems are
now dealing with a huge amount of varied data, different levels of cache
liveness, and varying levels of acceptable asynchronicity. If you subscribe to
convention over configuration, that means having a canonical answer to
everything. That means the default system needs to be able to do anything. That
means that the people who make Rails are going to have to work pretty hard.&lt;/p&gt;

&lt;p&gt;There&amp;rsquo;s another way, though. &lt;em&gt;Fuck&lt;/em&gt; the framework, go for the &lt;a href="http://en.wikipedia.org/wiki/JavaScript"&gt;lingua
franca&lt;/a&gt; and ensure that it works, fast. Ensure that if you need a specific
tool you can &lt;a href="http://npmjs.org/"&gt;get it&lt;/a&gt; in a hurry. Develop on a platform you know can
comfortably handle everything. Make sure nothing&amp;rsquo;s hidden behind the magic veil
of abstraction and that you get to pick what gets done fast and what gets done
slow.&lt;/p&gt;

&lt;p&gt;Sure, you won&amp;rsquo;t have fancy syntactic sugar. Sure, Node&amp;rsquo;s no ballroom dancer. But
she works everywhere, and she doesn&amp;rsquo;t care. Just ask her out.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Arguing for Immortality</title>
    <link href="http://vincentwoo.com/2011/04/30/arguing-for-immortality/" rel="alternate"/>
    <id>http://vincentwoo.com/2011/04/30/arguing-for-immortality/</id>
    <published>2011-04-30T00:00:00Z</published>
    <updated>2011-04-30T00:00:00Z</updated>
    <author>
      <name></name>
    </author>
    <summary type="html">&lt;p&gt;I want to live forever. I&amp;rsquo;ve always thought that not dying was a pretty
obvious thing to want. To my surprise, I&amp;rsquo;ve found that a lot of people
whom I usually agree with on most topics strongly disagree with me on
this one. Rather than write yet another piece extolling the virtues of
a far-future post-scarcity post-singularity world, I thought I&amp;rsquo;d just
document some of the objections to immortality I get and my
counterarguments&amp;hellip;&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;I want to live forever. I&amp;rsquo;ve always thought that not dying was a pretty
obvious thing to want. To my surprise, I&amp;rsquo;ve found that a lot of people
whom I usually agree with on most topics strongly disagree with me on
this one. Rather than write yet another piece extolling the virtues of
a far-future post-scarcity post-singularity world, I thought I&amp;rsquo;d just
document some of the objections to immortality I get and my
counterarguments.&lt;/p&gt;

&lt;p&gt;Note that for the purposes of giving my conversational partners
opportunities to disagree, I typically posit a form of immortality where
you, and you alone are presented with the option of eternal youth with
no suicide option. You constantly regenerate to perfect health at the
prime of your life. There are a lot of potential ways we might go about
not dying, but people tend to find objections to this particular flavor
more readily than the others. Please assume this working definition for
the below.&lt;/p&gt;

&lt;h3&gt;Watching your loved ones die&lt;/h3&gt;

&lt;p&gt;I get this one the most. The argument goes like&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;I can&amp;rsquo;t stand the thought of having to watch everyone I love die. Can
you imagine living forever with everyone you used to know gone? How
could you deal with the pain of building lifelong relationships and then
seeing them disappear, forever?&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;My response to this is essentially, &amp;ldquo;&lt;em&gt;Newsflash&lt;/em&gt;, you already are going
to have to deal with that.&amp;rdquo; If you are above average in caution,
intelligence, or just plain old fitness, odds are you are going to have
to watch a fair share of your friends die and still continue living for
a significant period of time afterwards. Statistics don&amp;rsquo;t lie. Accidents
and cancer, while tragic, are inevitable.&lt;/p&gt;

&lt;p&gt;As for having to carry the burden forever, we can again look to reality
to answer this objection. &amp;ldquo;Time heals all wounds,&amp;rdquo; is an adage for a
reason. If you can deal with a friend being dead for twenty years (as
people already do), it seems pretty likely you&amp;rsquo;ll be able to deal with
it for an indefinite future. As you live forever, you&amp;rsquo;ll make new
friends and get over the loss of old ones. It seems unlikely that it
will be year 2769 of missing Fred that finally pushes you over the edge,
forever. What will probably actually happen is you just get really good
at coping.&lt;/p&gt;

&lt;h3&gt;I&amp;rsquo;ll get bored and sick of life&lt;/h3&gt;

&lt;p&gt;This one is simple enough. It goes&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;I feel awful enough when I&amp;rsquo;m bored as it is. If I live forever, won&amp;rsquo;t
I eventually just get bored after having done literally everything? I
can&amp;rsquo;t even imagine my life past [X] years.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;The way I deal with this is essentially Moore&amp;rsquo;s Law. Each day, literally
a year of video is uploaded to Youtube. Every year, more books than you
could possibly read in a regular lifetime are written. As society
increases exponentially in complexity, generation of interesting content
explodes. It will be impossible for you to do everything when society
invents new experiences faster than you can experience them. Sit back
and enjoy the ride, you&amp;rsquo;re immortal.&lt;/p&gt;

&lt;p&gt;As for the people who can&amp;rsquo;t even imagine their long term prospects right
now, I say that this makes them perfectly suited for immortality. If
you lack a long term vision and focus on the short term, you&amp;rsquo;ll never
be dragged down by your long past. The best mindset for happiness over a
long time period is &lt;em&gt;always&lt;/em&gt; to enjoy the present.&lt;/p&gt;

&lt;h3&gt;Death&amp;rsquo;s inevitability gives life meaning&lt;/h3&gt;

&lt;p&gt;Now we start waxing philosophic with stuff like,&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;There is no good without evil. Having death exist gives my life
meaning just like scarcity lends gold-backed currencies their value. I
need to know that there is an end because it gives me a sense of urgency
to my life. Without it I would just stagnate.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;To me this is like saying &amp;ldquo;shitting gives eating meaning.&amp;rdquo; Does it?
Evolution would beg to disagree if it were anything but a blind
selection process. When was the last time you sat down for a great meal
and said to yourself, &amp;ldquo;Thank god I&amp;rsquo;m going to die one day or this meal
would be devoid of meaning&amp;rdquo;? What you do with your life is what lends it
value or meaning, not the eventuality of death.&lt;/p&gt;

&lt;p&gt;Besides, if you believe in death being necessary to lend your life
value, why go with the arbitrary time limit of your natural lifespan?
No one who has fielded this argument against me has agreed to just kill
themselves when they feel like they&amp;rsquo;ve basically achieved what they&amp;rsquo;ve
wanted to in life. The fact of the matter is that everyone chooses life
over death on a day-to-day basis, and actions speak louder than words.&lt;/p&gt;

&lt;h3&gt;What about the afterlife?&lt;/h3&gt;

&lt;p&gt;This one can be touchy, for obvious reasons. Let&amp;rsquo;s go with the
reasonably neutral objection of,&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Suppose there is an afterlife. By taking immortality, I would be
missing out on a greater truth and the greatest party in or outside of
the universe.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Sure, you might be. But most religions (weighted by subscribers) don&amp;rsquo;t
fault you for not dying. If your purpose in living is to live according
to your religion, immortality doesn&amp;rsquo;t get in the way of that.
Furthermore, at the end of eternity, which is a construct that should be
available to gods, it seems reasonable that you will receive your just
reward in any case. Religions also commonly posit Judgment Day scenarios
in which all living humans will be placed into various afterlives. It
seems reasonable here to assume that if a god so wishes it, your
immortality will be revoked.&lt;/p&gt;

&lt;h3&gt;I&amp;rsquo;ll be too different from everyone else&lt;/h3&gt;

&lt;p&gt;There are a couple variations on this theme. In its simplest form,&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;If I live forever and no one else does, I&amp;rsquo;ll be endlessly meeting new
people with the foreknowledge that I will outlive them. I&amp;rsquo;ll be so
fundamentally different from the average mortal person that I won&amp;rsquo;t be
able to form meaningful relationships. Nobody will understand me!&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;This one is more of a deal breaker on a case-by-case basis. If a person
really feels that they will be well and truly alienated by being
immortal, well, it&amp;rsquo;s hard to tell them that they won&amp;rsquo;t be. It&amp;rsquo;s kind of
self-fulfilling. That said, there a few things you can say.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;People actually will understand you&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;  If there&amp;rsquo;s anything humans are good at, it&amp;rsquo;s getting used to stuff.
In short order you and your future peers will be cracking wise about
your absurd inability to die over whatever the future equivalent of beer
is. Maybe mechano-beer. I&amp;rsquo;m pretty sure that after an eternity of
explaining yourself to mortals, you&amp;rsquo;ll be extremely proficient at
helping other people understand who you are.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Science will eventually catch up&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;  Lifespan lengthening technologies only need to make it to the point
where they can increase a person&amp;rsquo;s lifespan one year for every year of
research before the masses become practically immortal. Wait long enough
and you and your magical wish-granted immortality will be in good
company.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Being different is an asset&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;  You can honestly just play your immortality for international
celebrity. Make a bid for president. Be rich. Honestly, being too much
like other people basically sucks. Being exceptional will make you
popular, not the opposite.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Disaster scenarios&lt;/h3&gt;

&lt;p&gt;This one is kind of morbid. I thought about simply forbidding it by
adding a clause to the immortality definition, but it&amp;rsquo;s interesting
enough to keep in. In a few words,&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;What if everyone but me gets wiped out? I don&amp;rsquo;t think I could live as
the only human being alive. Also, what if the government kidnaps me and
uses me as a lab rat? What if I piss off some people enough to get me
tortured forever? There might be times where I might actually need to
kill myself, even if I would normally find living forever appealing.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Coming up with a bulletproof answer to this one is hard. It&amp;rsquo;s very
tempting to say that no actual implementation of immortality will
actually prevent you from dying in literally every case or rob you of
the option of killing yourself. But really, that would be cheating.&lt;/p&gt;

&lt;p&gt;A real counterargument has to take the form of, &amp;ldquo;You already run similar
risks today, as a mortal.&amp;rdquo; You can be used as a medical experiment or
tortured this very day, and they can keep you alive for a very long
time. As an immortal, it&amp;rsquo;s likely that you will eventually outlive your
captors.&lt;/p&gt;

&lt;p&gt;Also consider that it&amp;rsquo;s far more likely that any government or entity
with power will want your cooperation instead of your incarceration. You
could be the world&amp;rsquo;s greatest astronaut or the guy who goes into
nuclear reactor cores melting down.&lt;/p&gt;

&lt;p&gt;However, the extinction of the human race scenario actually does throw a
wrench into this argument. To this, all I can really say is that the
miniscule risk you run of a mass extinction is just a risk you&amp;rsquo;ll have
to swallow in order to reap the vast rewards of immortality. Maybe
there&amp;rsquo;s other life out there that you can find. Maybe in your spare time
you&amp;rsquo;ll engineer a way to rebuild the world.&lt;/p&gt;

&lt;h3&gt;Wrap-up&lt;/h3&gt;

&lt;p&gt;This is a fun topic for me, and I hope I&amp;rsquo;ve been interesting without
being &lt;em&gt;too&lt;/em&gt; offensive to people on either side of the coin. This topic
is important in the sense that once you accept that immortality is a
pretty good idea, you open the door for a lot of other interesting
arguments to be made about rationality, ethics, and just general
decision making. If you feel I&amp;rsquo;ve made some sort of egregious error or
just want to weigh in for any reason, please feel free to comment below.&lt;/p&gt;

&lt;p&gt;EDIT: The &lt;a href="http://news.ycombinator.com/item?id=2511929"&gt;Hacker News&lt;/a&gt;
discussion of this page is fairly interesting. Give it a try, too!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Facebook Puzzle: sophie</title>
    <link href="http://vincentwoo.com/2011/03/05/facebook-puzzle-sophie/" rel="alternate"/>
    <id>http://vincentwoo.com/2011/03/05/facebook-puzzle-sophie/</id>
    <published>2011-03-05T00:00:00Z</published>
    <updated>2011-03-05T00:00:00Z</updated>
    <author>
      <name></name>
    </author>
    <summary type="html">&lt;p&gt;This week: Facebook&amp;rsquo;s &lt;a href="http://www.facebook.com/careers/puzzles.php?puzzle_id=11"&gt;sophie
puzzle&lt;/a&gt;. This
one is &amp;ldquo;buffet&amp;rdquo; difficulty, which translates roughly to &amp;ldquo;the underlying
problem is NP-complete,&amp;rdquo; which explains why I have such a hard time
choosing food at sushi buffets. In any case, the problem is to find your
cat in your apartment, where you know where the cat is likely to be, as
well as the transit times between the various locations in your home.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll document here the various bad solutions I came up with on my way to
a decent one, and as a bonus: an optimized-ish version in C++!&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;This week: Facebook&amp;rsquo;s &lt;a href="http://www.facebook.com/careers/puzzles.php?puzzle_id=11"&gt;sophie
puzzle&lt;/a&gt;. This
one is &amp;ldquo;buffet&amp;rdquo; difficulty, which translates roughly to &amp;ldquo;the underlying
problem is NP-complete,&amp;rdquo; which explains why I have such a hard time
choosing food at sushi buffets. In any case, the problem is to find your
cat in your apartment, where you know where the cat is likely to be, as
well as the transit times between the various locations in your home.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll document here the various bad solutions I came up with on my way to
a decent one, and as a bonus: an optimized-ish version in C++!&lt;/p&gt;

&lt;h3&gt;About the math&lt;/h3&gt;

&lt;p&gt;The problem asks you to minimize the expected time to find sophie. What
does this mean? Take a look at the example input (comments mine).&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;4
#node name    #probability sophie is there
front_door    .2
in_cabinet    .3
under_bed     .4
behind_blinds .1
5
#node x    #node y       #time between x and y
front_door under_bed     5
under_bed  behind_blinds 9
front_door behind_blinds 5
front_door in_cabinet    2
in_cabinet behind_blinds 6 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This says there are four nodes and five edges between those nodes, and
that 40% of the time, sophie is going to be under the bed. If sophie was
under the bed 100% of the time, the optimal path to minimize the
expected time to find her would be just the path that takes you to the
bed in the shortest amount of time. But since some nodes are unlikely to
hide sophie, you can afford to take your sweet time getting to them.&lt;/p&gt;

&lt;p&gt;For this sample input, the optimal path is front_door, in_cabinet,
under_bed, behind_blind. Note that to go from in_cabinet to under_bed,
you should pass through the front_door node. The expectation for this
path is 6.00 seconds, as explained from this snippet from &lt;a href="http://www.davideisenstat.com/fbpfaq/#sophie"&gt;David
Eisenstat&amp;rsquo;s site&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Pr(front_door) * 0
+ Pr(in_cabinet) * Distance(front_door, in_cabinet)
+ Pr(under_bed) * (Distance(front_door, in_cabinet)
                   + Distance(in_cabinet, under_bed))
+ Pr(behind_blinds) * (Distance(front_door, in_cabinet)
                       + Distance(in_cabinet, under_bed)
                       + Distance(under_bed, behind_blinds))
    = .2 * 0 + .3 * 2 + .4 * (2 + 7) + .1 * (2 + 7 + 9) = 6.00
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Building the graph&lt;/h3&gt;

&lt;p&gt;The input only includes edges between particular nodes. In order to know
that, say, the distance between the cabinet and the bed is 7 (through
the front door), you have to build up the shortest distances between
every node in the graph. This is known as the &amp;ldquo;all pairs shortest path&amp;rdquo;
problem. There exists a quite famous dynamic programming algorithm to
solve this, the &lt;a href="http://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm"&gt;Floyd Warshall
algorithm&lt;/a&gt;. Trivially implemented in Ruby:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# note that $weights[x][y] is initialized to either
# Float::MAX if there is no edge between x and y, or
# to whatever the length of the edge is if there is.
def floyd_warshall
    for k in 0..$num-1
    for i in 0..$num-1
    for j in 0..$num-1
        if $weights[i][k] + $weights[k][j] &amp;lt; $weights[i][j]
            $weights[i][j] = $weights[i][k] + $weights[k][j]
            $next[i][j] = k
        end
    end
    end
    end
end

# links menoizes the list of nodes you need to traverse
# between nodes i and j
def links(i, j)
    k = $next[i][j]
    return k if k.class == Set
    $next[j][i] = $next[i][j] = (k.nil? or i == j) ?
        Set.new([]) :
        (links(i, k) + Set.new([k]) + links(k, j))
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This gives a good starting point for actually trying to start solving
the problem.&lt;/p&gt;

&lt;h3&gt;BFS solution&lt;/h3&gt;

&lt;p&gt;In my hubris, I figured a breadth first search where you expand on the
path with the lowest current expected time would work. Here&amp;rsquo;s what it
looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def solve
    queue = MinHeap.new
    queue.push 0.0, [[0], Set.new((1..$num-1).select {|n| $probs[n] &amp;gt; 0}), 0.0, 0.0]
    while not queue.empty?
        node, remain, time, expected = queue.pop
        if remain.empty?
            p node
            return expected 
        end
        # only iterate remaining nodes that you don't need to
        # go through other remaining ndoes to reach
        remain.select {|n|
            (links(node.last, n) &amp;amp; remain).empty?
        }.each do |n|
            new_time = time + $weights[node.last][n]
            new_expected = expected + $probs[n] * new_time
            queue.push new_expected, [node + [n], remain - [n], new_time, new_expected]
        end
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In my defense I hadn&amp;rsquo;t yet realized that the sophie problem is a variant
of the traveling salesman problem and that a BFS search would take
forever on large graphs. This doesn&amp;rsquo;t work because you incrementally
build all the bad paths on your way to finding the first path to
complete. Complexity: proportional to the number of paths, or O(n!).&lt;/p&gt;

&lt;h3&gt;DP Solution&lt;/h3&gt;

&lt;p&gt;Hitting upon the realization that the problem is a variant of the
&lt;a href="http://en.wikipedia.org/wiki/Traveling_salesman_problem"&gt;traveling salesman
problem&lt;/a&gt; I
decided to try the canonical dynamic programming solution to TSP.&lt;/p&gt;

&lt;p&gt;The DP solution requires that you build a structure like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C[subset][j]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;where subset is some subset of all nodes, and j is a node in that
subset. The value of this entry should be the minimum expected time to
proceed from node 0 to node j and through all the nodes in the subset.
The problem then reduces to finding the minimum of:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;C[subset - [j]][i] # for all i in subset
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There are some problems here, but first, some code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def solve
    relevant = (1..$num-1).select {|n| $probs[n] &amp;gt; 0}
    hash = {}
    hash[[0]] = {0 =&amp;gt; [0, 0]}
    for size in 1..relevant.size
        relevant.combination(size).each do |subset|
            subset.insert 0, 0
            hash[subset] = {0 =&amp;gt; [Float::MAX, Float::MAX]}
            for j in subset
                next if j == 0
                reduced = subset - [j]
                hash[subset][j] = reduced.collect {|i|
                    e, t = hash[reduced][i]
                    t += $weights[i][j]
                    [e + t * $probs[j], t]
                }.min {|a,b| a.first &amp;lt;=&amp;gt; b.first}
            end
        end
    end
    hash[[0] + relevant].values.collect {|x| x.first}.min
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This works, but isn&amp;rsquo;t fast. A 17 node graph took me about 10 minutes to
finish. The problem is that since subsets aren&amp;rsquo;t ordered, there is no
convenient way to represent them as array indices and you must therefore
hash entire subsets. Since there are 2&lt;sup&gt;n&lt;/sup&gt; subsets, and you must
compute the path that ends in each node in each subset, which itself
requires examining all other previous paths of the subset minus one of
its elements (breathe), the complexity here is
O(2&lt;sup&gt;n&lt;/sup&gt;n&lt;sup&gt;2&lt;/sup&gt;).&lt;/p&gt;

&lt;h3&gt;Recursive Backtracking (Pruning) Solution&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Backtracking"&gt;Backtracking&lt;/a&gt; can be thought
of as essentially a DFS search with fast failure. For example, say you
have found a complete path that you know will give you an expected time
of 30 seconds. Now you are attempting to build another path, and halfway
through you know your partial expected time is already 31 seconds. You
can abandon building this path, saving yourself the hassle of expanding
all of that partial path&amp;rsquo;s children.&lt;/p&gt;

&lt;p&gt;Skipping entire subtrees is known as pruning, and you can achieve some
pretty massive savings depending on how well you implement it. My
solution was to very conservatively estimate the remaining expectation
of a partial path. For instance, if you are halfway through a path with
a current expected time of 10 seconds, a path length of 20 seconds and
you have covered 60% of the places where sophie can be, then even in the
perfect case where the next node was 0 seconds away and had a 40%
probability of hiding sophie, you would still incur an additional 20 *
.4 = .8 seconds of expected time. If you have already found a minimum
path length of say, 10.5, you can prune this subtree where you could not
have before.&lt;/p&gt;

&lt;p&gt;And without further ado, here&amp;rsquo;s the code.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$min = Float::MAX
def solve(node, remain, unseen, expect = 0, time = 0)
    return if expect + unseen * time &amp;gt;= $min
    return ($min = expect) if remain.empty?
    remain.each do |n|
        next_time  = time + $weights[node][n]
        solve n,
              remain - [n],
              unseen - $probs[n],
              expect + next_time * $probs[n],
              next_time
    end
    $min
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This works fairly well. We can do that 17 node graph in 30 seconds now.
I don&amp;rsquo;t have a good estimate of the complexity improvement here, since
you can generate corner cases that can prevent any pruning from
happening.&lt;/p&gt;

&lt;h3&gt;Optimizations!&lt;/h3&gt;

&lt;p&gt;Ruby is slow. At least, Cygwin&amp;rsquo;s default Ruby 1.8.7 interpreter is slow.
I decided to reimplement the whole deal in C++ and see what kind of
speedups I could achieve. Here is my initial implementation in C++:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;double solve(int node, set&amp;lt;int&amp;gt; &amp;amp;remain, double unseen,
        double expect = 0.0, double time = 0.0) {
    static double min = numeric_limits&amp;lt;double&amp;gt;::max();
    if (expect + unseen * time &amp;gt;= min)
        return -1;
    if (remain.size() == 0) {
        min = expect;
        return -1;
    }
    for (set&amp;lt;int&amp;gt;::iterator n = remain.begin(); n != remain.end(); n++) {
        int next = *n;
        double next_time = time + weights[node][next];
        set&amp;lt;int&amp;gt; next_remain = remain;
        next_remain.erase(next);
        solve(next, next_remain, unseen - probs[next],
            expect + next_time * probs[next], next_time);
    }
    return min;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;However, the speedup here was only a factor of two or so. Where are the
bottlenecks? Turns out, a big one in both Ruby and C++ is the constant
recreation of the remainder set at&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;set&amp;lt;int&amp;gt; next_remain = remain;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It&amp;rsquo;s much faster to just do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;set&amp;lt;int&amp;gt; next_remain = remain;
for (set&amp;lt;int&amp;gt;::iterator n = remain.begin(); n != remain.end(); n++) {
    int next = *n;
    double next_time = time + weights[node][next];
    next_remain.erase(next);
    solve(next, next_remain, unseen - probs[next],
        expect + next_time * probs[next], next_time);
    next_remain.insert(next);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This way, you only do one set copy per recursion, and then just pass
around to all of your children. &amp;ldquo;But Vincent,&amp;rdquo; you say, &amp;ldquo;why even
bother recreating the set at each recursion? Can&amp;rsquo;t you just pass alone
one set of remaining nodes and add and remove from it?&amp;rdquo; Sure, but its
very annoying to not invalidate iterators to a set that is constantly
shrinking and growing through iteration and recursion. Here&amp;rsquo;s what I
came up with:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;double solve(int node, vector&amp;lt;node_entry&amp;gt; &amp;amp;remain, double unseen,
        double expect = 0.0, double time = 0.0) {
    static double min = numeric_limits&amp;lt;double&amp;gt;::max();
    if (expect + unseen * time &amp;gt;= min)
        return -1;

    bool empty = true;
    for (vector&amp;lt;node_entry&amp;gt;::iterator n = remain.begin(); n != remain.end(); n++) {
        if (!n-&amp;gt;active)
            continue;
        empty = false;
        int next = n-&amp;gt;index;
        double next_time = time + weights[node][next];
        n-&amp;gt;active = false;
        solve(next, remain, unseen - probs[next],
            expect + next_time * probs[next], next_time);
        n-&amp;gt;active = true;
    }
    if (empty) min = expect;
    return min;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This way we just store whether an element is active or not in the data
model itself, instead of representing that information with presence in
a set. This is nice because adding/removing from a set, while O(log(n))
fast, ain&amp;rsquo;t no O(1). This gets us (for our 17 node graph):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ make &amp;amp;&amp;amp; time ./sophie in_sophie5.txt
make: `sophie' is up to date.
38.20

real    0m0.220s
user    0m0.156s
sys     0m0.030s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Hooray!&lt;/p&gt;

&lt;h3&gt;Final Thoughts&lt;/h3&gt;

&lt;p&gt;I didn&amp;rsquo;t talk about any of the edge cases, but you need to check for if
it&amp;rsquo;s actually possible to be sure to find sophie. In particular, if
there are nodes that aren&amp;rsquo;t reachable from the first node that sophie
has a chance of being in then you need to fail.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Dear submitter,&lt;/p&gt;

&lt;p&gt;Thank you for your submission of a puzzle solution
to Facebook! After running your solution to sophie (received on March 5,
2011, 8:14 pm), I have determined it to be correct. Your solution ran
for 25.118 ms on its longest test case.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Also, full source and some plagiarized test cases are all on
&lt;a href="https://github.com/vincentwoo/rubycode/tree/master/sophie"&gt;github&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Facebook Puzzle: peaktraffic</title>
    <link href="http://vincentwoo.com/2011/02/28/facebook-puzzle-peaktraffic/" rel="alternate"/>
    <id>http://vincentwoo.com/2011/02/28/facebook-puzzle-peaktraffic/</id>
    <published>2011-02-28T00:00:00Z</published>
    <updated>2011-02-28T00:00:00Z</updated>
    <author>
      <name></name>
    </author>
    <summary type="html">&lt;p&gt;Last week I was working on Facebook&amp;rsquo;s &lt;a href="http://www.facebook.com/careers/puzzles.php?puzzle_id=8"&gt;peak traffic puzzle&lt;/a&gt;,
which was a pretty entertaining and informative exercise. The idea is
basically to parse a log file and generate an undirected graph that
represents mutual friendships between Facebook users. Then you are to
find every group of friends where each friend in the group is friends
with every other person in the group&amp;hellip;&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Last week I was working on Facebook&amp;rsquo;s &lt;a href="http://www.facebook.com/careers/puzzles.php?puzzle_id=8"&gt;peak traffic puzzle&lt;/a&gt;,
which was a pretty entertaining and informative exercise. The idea is
basically to parse a log file and generate an undirected graph that
represents mutual friendships between Facebook users. Then you are to
find every group of friends where each friend in the group is friends
with every other person in the group.&lt;/p&gt;

&lt;p&gt;I wrote a naive implementation at first that looked something like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# $seen is a hash to menoize previously seen sets
# $sparse is a hash of usernames to a list of neighboring usernames
# $set is the list of output clusters

$seen = {}
def subgraph(set, adj)
    hash = (set + adj).sort
    return if $seen[hash]
    $sets.push set.sort.join(", ") if adj.empty? and set.size &amp;gt; 2
    adj.each {|node| subgraph(set + [node], $sparse[node] &amp;amp; adj)}
    $seen[hash] = true
end

$sparse.keys.each do |vertex|
    subgraph([vertex], $sparse[vertex])
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This appeared to work pretty well on my tests, but continued to fail on
Facebook puzzle submission for some reason I couldn&amp;rsquo;t track down at the
time. In my frustration, I went to the internet. Turns out, this problem
is actually known as &lt;a href="http://en.wikipedia.org/wiki/Clique_problem#Listing_all_maximal%0A_cliques"&gt;list maximal
cliques&lt;/a&gt;, a category of clique problem. The prior link has more
information about cliques, which are subgraphs that have the friend
property desired by this problem. Additionally, there is a known &amp;ldquo;pretty
good&amp;rdquo; strategy for solving this problem, the &lt;a href="http://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm"&gt;Bron Kerbosch
algorithm&lt;/a&gt;
.&lt;/p&gt;

&lt;p&gt;I went all out and applied a couple optimizations, namely (warning:
abstract pdfs)
&lt;a href="ftp://ftp-sop.inria.fr/geometrica/fcazals/papers/ncliques.pdf"&gt;pivoting&lt;/a&gt; and
&lt;a href="http://drops.dagstuhl.de/opus/volltexte/2011/2935/pdf/10441.EppsteinDavid.Paper.2935.pdf"&gt;degeneracy ordering&lt;/a&gt;,
which are two techniques used to cut down on the number of recursive calls.
At this point my code looked something like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def generate_degeneracy_ordering
    d = []  #degree buckets
    dw = {} #degree for each vertex
    $sparse.each_pair do |vertex, neighbors|
        deg = neighbors.size
        d[deg] ||= []
        d[deg].push vertex
        dw[vertex] = deg
    end
    d.each_index {|i| d[i] ||= []}
    $sparse.size.times do
        vertex = d.find {|x| !x.empty?}.pop
        $degen.push vertex
        for neighbor in $sparse[vertex]
            if d[dw[neighbor]].delete neighbor
                dw[neighbor] -= 1
                d[dw[neighbor]].push neighbor
            end
        end
    end
end

def bron_kerbosch(set, points, exclude, pivot_neighbors=nil)
    if points.empty?
        $sets.push set.sort.join(', ') if set.size &amp;gt; 2 and exclude.empty?
        return
    end

    pivot_neighbors ||= (exclude.empty? or $sparse[points.last].size &amp;gt; $sparse[exclude.last].size) ?
        $sparse[points.last] : $sparse[exclude.last]

    points.each_with_index do |vertex, i|
        next if pivot_neighbors.include? vertex
        points[i] = nil
        bron_kerbosch(set + [vertex],
                      points &amp;amp; $sparse[vertex],
                      exclude &amp;amp; $sparse[vertex])
        exclude.push vertex
    end
end

exit unless ARGV.size == 1
ingest(ARGV[0])

generate_degeneracy_ordering
before = []
after = $degen[1..$degen.size-1]
$degen.each do |vertex|
    intersect = after &amp;amp; $sparse[vertex]
    bron_kerbosch([vertex],
                  intersect,
                  before &amp;amp; $sparse[vertex],
                  $sparse[intersect.last]) #last elements in $degen have highest degrees
    before.push vertex
    after.shift
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which let me parse a 120MB input file in 1 minute flat. However, for
some reason this was slower than my naive solution, which could do it,
ironically, in 48s. It did pass the Facebook puzzle checker, though, so
at least I had that. If anyone has any insight into why my naive
solution appears to outperform bron_kerbosch I would greatly appreciate
it, as it does not seem correct. My guess is slow implementations in
Ruby of Array &amp;ldquo;set-like&amp;rdquo; operators (&amp;amp;, +).&lt;/p&gt;

&lt;h3&gt;final thoughts&lt;/h3&gt;

&lt;p&gt;I spent 2-3 days stuck on bugs that amounted to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;parsing input off by one whitespace&lt;/li&gt;
&lt;li&gt;output missing a final newline&lt;/li&gt;
&lt;li&gt;mysterious bug that turned out to be RUBY 1.8.6 NOT HAVING MAX_BY AND
ME TESTING IN 1.8.7&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Facebook absolutely does not provide you any clues as to what went wrong
besides there being a syntax error in your build. In this case it falls
on you to both duplicate their runtime environment and generate workable
tests. But finally seeing the confirmation email is definitely worth it.
Here are the results after finally getting my naive solution running.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Dear submitter,&lt;/p&gt;

&lt;p&gt;Thank you for your submission of a puzzle solution to Facebook! After running your solution to peaktraffic (received on February 28, 2011, 4:07 pm), I have determined it to be correct. Your solution ran for 1162.186 ms on its longest test case.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Here are my two &lt;a href="https://github.com/vincentwoo/rubycode/blob/master/peaktraffic"&gt;solutions&lt;/a&gt;
to this problem. The codepath that executes the Bron Kerbosch algorithm is not active but
fairly obvious. They both work.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Cheap Toto Pagination</title>
    <link href="http://vincentwoo.com/2011/02/23/cheap-toto-pagination/" rel="alternate"/>
    <id>http://vincentwoo.com/2011/02/23/cheap-toto-pagination/</id>
    <published>2011-02-23T00:00:00Z</published>
    <updated>2011-02-23T00:00:00Z</updated>
    <author>
      <name></name>
    </author>
    <summary type="html">&lt;p&gt;&lt;a href="http://cloudhead.io/toto"&gt;Toto&lt;/a&gt; is a great tiny blogging platform for
Ruby/Rack. However, it doesn&amp;rsquo;t expose much in the way of a MVC structure
and it can be just annoying enough when you want to add some feature
that isn&amp;rsquo;t there. In this case, I wanted to add some simple older/newer
pagination to the front page. To my chagrin, I couldn&amp;rsquo;t find a way to
pass variables to a Toto page without using the GET variable syntax
(i.e. ?page=1) and I still wanted to hold onto the rails RESTful
paradigm of /page/1, so I monkey patched the Toto::Site dispatcher, like
so:&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;&lt;a href="http://cloudhead.io/toto"&gt;Toto&lt;/a&gt; is a great tiny blogging platform for
Ruby/Rack. However, it doesn&amp;rsquo;t expose much in the way of a MVC structure
and it can be just annoying enough when you want to add some feature
that isn&amp;rsquo;t there. In this case, I wanted to add some simple older/newer
pagination to the front page. To my chagrin, I couldn&amp;rsquo;t find a way to
pass variables to a Toto page without using the GET variable syntax
(i.e. ?page=1) and I still wanted to hold onto the rails RESTful
paradigm of /page/1, so I monkey patched the Toto::Site dispatcher, like
so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# in config.ru
class Toto::Site
    alias_method :old_go, :go

    def go route, env = {}, type = :html
        if not route.first =~ /\d{4}/ and route.size == 2 and route.last =~ /(\d+)/
            @config[:id] = route.last.to_i
            route.pop
        end
        ret = old_go route, env, type
        @config.delete :id
        ret
    end
end
...
# and in the config initializer block:
set :root, "page"                                           # page to load on /
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can see that we intercept routes that look like name/1234 and pass
the numeric portion of the route in @config[:id], and then clear
@config[:id] (because @config is persistent). This is pretty hacky and
only really acceptable in the context of Heroku caching everything.&lt;/p&gt;

&lt;p&gt;and in templates/pages/page.rhtml&amp;hellip;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%
    page = @config[:id]
    per_page = @config[:articles_per_page]
    page = 1 if (page.nil? or (page-1) * per_page &amp;gt; @articles.length) or page &amp;lt; 1
    page_results = @articles[(page-1) * per_page .. page * per_page - 1]
    prev_page = page &amp;gt; 1 ? page - 1 : nil
    next_page = @articles.length &amp;gt; page * per_page ? page + 1 : nil
%&amp;gt;
...
&amp;lt;p id="footer"&amp;gt;
&amp;lt;% if prev_page %&amp;gt;
    &amp;lt;a href="/page/&amp;lt;%=prev_page%&amp;gt;"&amp;gt;&amp;amp;laquo; newer&amp;lt;/a&amp;gt;
    &amp;lt;% if next_page  %&amp;gt;|&amp;lt;% end %&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;% if next_page %&amp;gt;
    &amp;lt;a href="/page/&amp;lt;%=next_page%&amp;gt;"&amp;gt;older &amp;amp;raquo;&amp;lt;/a&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Also, syntax highlighting brought to you by &lt;a href="http://softwaremaniacs.org/soft/highlight/en/"&gt;highlight.js&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Interactive Google Doodle</title>
    <link href="http://vincentwoo.com/2011/02/14/interactive-google-doodle/" rel="alternate"/>
    <id>http://vincentwoo.com/2011/02/14/interactive-google-doodle/</id>
    <published>2011-02-14T00:00:00Z</published>
    <updated>2011-02-14T00:00:00Z</updated>
    <author>
      <name></name>
    </author>
    <summary type="html">&lt;div class="centered"&gt;
    &lt;a href="/google.html"&gt;
        &lt;img alt="Interactive Google Doodle"
        src="http://farm6.static.flickr.com/5291/5446054061_7eab02d984_z.jpg" /&gt;
    &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Google&amp;rsquo;s &lt;a href="http://www.pcmag.com/article2/0,2817,2368790,00.asp"&gt;take&lt;/a&gt; on
an interactive ball logo was interesting enough to me that I felt that I
needed to write &lt;a href="/google.html"&gt;something at least as fun&lt;/a&gt;. My take is
backed by HTML5, so you will need a suitably compliant browser to view
it&amp;hellip;&lt;/p&gt;
</summary>
    <content type="html">&lt;div class="centered"&gt;
    &lt;a href="/google.html"&gt;
        &lt;img alt="Interactive Google Doodle"
        src="http://farm6.static.flickr.com/5291/5446054061_7eab02d984_z.jpg" /&gt;
    &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Google&amp;rsquo;s &lt;a href="http://www.pcmag.com/article2/0,2817,2368790,00.asp"&gt;take&lt;/a&gt; on
an interactive ball logo was interesting enough to me that I felt that I
needed to write &lt;a href="/google.html"&gt;something at least as fun&lt;/a&gt;. My take is
backed by HTML5, so you will need a suitably compliant browser to view
it.&lt;/p&gt;

&lt;h3&gt;Features&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Mass based ball collisions&lt;/li&gt;
&lt;li&gt;Mouse input to scatter balls&lt;/li&gt;
&lt;li&gt;Balls &amp;ldquo;anchored&amp;rdquo; to positions with dampened springs&lt;/li&gt;
&lt;li&gt;Typing creates additional interactable balls&lt;/li&gt;
&lt;li&gt;Searching cues exit animation&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Technical information&lt;/h3&gt;

&lt;p&gt;The code for this demo (as well this entire blog) is on github. Here is
&lt;a href="https://github.com/vincentwoo/blog/blob/master/public/js/balls.js"&gt;the primary javascript engine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In google&amp;rsquo;s original doodle, the balls would react with changes in size
and velocity to the presence of the mouse. They would not, however,
collide with anything. I thought this was a shame, and set out to make a
HTML5 canvas app that could handle collisions between a large number of
objects.&lt;/p&gt;

&lt;p&gt;A naive initial implementation worked well enough. It looked like this
(pseudocode is rubyish):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;for i in 0 to circles.length - 2
    for j in i + 1 to circles.length - 1
        handleCollision(circles[i], circles[j]);
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;which served me well enough on Canary on my admittedly beefy desktop. It
wasn&amp;rsquo;t until I tried to run the same on Firefox 3.x on my work laptop that
I realized I probably wasn&amp;rsquo;t performant on a wide range of configurations.&lt;/p&gt;

&lt;p&gt;The thing to do, then, was to set up a 2D grid covering the entire canvas.
Each cell is about the diameter of a circle, and knows about any circles
within itself, as well as up to four of its neighbors. Then, collision code
becomes something like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;for circle in circles
    for potentialCollider in grid.getPotentialColliders(circle.pos)
        handleCollision(circle, potentialCollider);
    end
end

def grid.getPotentialColliders(pos) 
    cell = getCell(pos)
    ret = []
    for neighbor in cell.neighbors
        ret = ret.concat(neighbor.objects)
    end
    ret
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;which takes the runtime complexity down from n&lt;sup&gt;2&lt;/sup&gt; to about n. Neat!&lt;/p&gt;

&lt;p&gt;Probably the hardest part was making circles appear for the query text.
Understanding whether the user added characters, deleted, replaced, pasted,
etc. is kind of difficult. I honestly hacked together a solution that works
in 95% of use cases, but the best solution would to be to implement a full
fledged dynamic programming algorithm to find the &lt;a href="http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Dynamic/Edit/"&gt;minimum edit distance&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And finally, a big thanks to &lt;a href="http://rawkes.com/blog/2010/09/07/recreating-googles-bouncing-balls-logo-in-html5-canvas"&gt;Rob Hawke&amp;rsquo;s recreation of the google ball logo&lt;/a&gt;
for both reference and initial source data points.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>food photography is easy</title>
    <link href="http://vincentwoo.com/2011/01/30/food-photography-is-easy/" rel="alternate"/>
    <id>http://vincentwoo.com/2011/01/30/food-photography-is-easy/</id>
    <published>2011-01-30T00:00:00Z</published>
    <updated>2011-01-30T00:00:00Z</updated>
    <author>
      <name></name>
    </author>
    <summary type="html">&lt;p&gt;God, and people get paid to do this stuff? Just get a big aperture lens
and blow up the contrast boom pulitzer prize in food journalism (I&amp;rsquo;m not
sure that&amp;rsquo;s real).&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;God, and people get paid to do this stuff? Just get a big aperture lens
and blow up the contrast boom pulitzer prize in food journalism (I&amp;rsquo;m not
sure that&amp;rsquo;s real).&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>The Gustav: A BC2 Hack</title>
    <link href="http://vincentwoo.com/2011/01/24/gustav/" rel="alternate"/>
    <id>http://vincentwoo.com/2011/01/24/gustav/</id>
    <published>2011-01-24T00:00:00Z</published>
    <updated>2011-01-24T00:00:00Z</updated>
    <author>
      <name></name>
    </author>
    <summary type="html">&lt;div class="centered"&gt;
    &lt;iframe title="YouTube video player" 
            class="youtube-player"
            width="640"
            height="390" 
            src="http://www.youtube.com/embed/AXaMAa7XBKw?rel=0"&gt;
    &lt;/iframe&gt;
&lt;/div&gt;


&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The Gustav is a closed source C++ hack for &lt;a href="http://battlefieldbadcompany2.com"&gt;Battlefield: Bad Company
2&lt;/a&gt;. It incorporates realtime physics
simulation to determine potential targets, as well as unique D3D hooking
to provide visual cues for the user in the game world itself&amp;hellip;&lt;/p&gt;
</summary>
    <content type="html">&lt;div class="centered"&gt;
    &lt;iframe title="YouTube video player" 
            class="youtube-player"
            width="640"
            height="390" 
            src="http://www.youtube.com/embed/AXaMAa7XBKw?rel=0"&gt;
    &lt;/iframe&gt;
&lt;/div&gt;


&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The Gustav is a closed source C++ hack for &lt;a href="http://battlefieldbadcompany2.com"&gt;Battlefield: Bad Company
2&lt;/a&gt;. It incorporates realtime physics
simulation to determine potential targets, as well as unique D3D hooking
to provide visual cues for the user in the game world itself.&lt;/p&gt;

&lt;p&gt;It has taken me about three months of tinkering in my time off to put
together and I am more or less satisfied with what it has taught me
about physics and reverse engineering, which are two subjects that are
very easy to unlearn without practice.&lt;/p&gt;

&lt;p&gt;I have written up a couple pieces of interest encountered while
developing the Gustav, namely &lt;a href="http://www.gamedeception.net/threads/21070-Drawing-in-the-3D-world-of-a-modern-game-WITH-z-depth-testing"&gt;how to draw in 3D within the game world
while respecting Z-depth&lt;/a&gt; and a &lt;a href="http://www.gamedeception.net/threads/21167-finally-a-menu-class-that-doesn-t-suck"&gt;short primer on how to write a
tree-recursive menu&lt;/a&gt;, the latter of which will probably be nothing
new to any experienced dev.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>ships in the night</title>
    <link href="http://vincentwoo.com/2011/01/22/ships-in-the-night/" rel="alternate"/>
    <id>http://vincentwoo.com/2011/01/22/ships-in-the-night/</id>
    <published>2011-01-22T00:00:00Z</published>
    <updated>2011-01-22T00:00:00Z</updated>
    <author>
      <name></name>
    </author>
    <summary type="html">&lt;p&gt;I got a D90, low light shooting is fun again.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;I got a D90, low light shooting is fun again.&lt;/p&gt;
</content>
  </entry>
</feed>

