Achilles

22 Jan 2015

This past weekend I participated in the CivicHacks "Game Jam for Good".
The goal was to raise awareness of the global water crisis and ultimately promote
PackH2O, a Columbus-based startup that designs water backpacks
for developing water-stressed regions.

The jam lasted 48 hours. My entry is called "Achilles".

Achilles is a multiplayer text-based simulation. You the player must manage a village in a
third-world country experiencing a water crisis. You can play it here
(bit.ly/waterjamachilles) assuming I haven't stopped
paying for the cloud server.

It's incredibly depressing. Really, only bad things can happen. I had plans to add pregnancy and
childbirth, but ran out of time. Here's a video of it in action:

The core idea was this: to convince people of the effectiveness of the PackH2O backpack,
a promotional game should put players in the shoes of the people who will use it.
And I think I accomplished that. In the game, the backpack allows people to carry twice as much water.
The player experiences first-hand through gameplay how useful the backpack is.

Despite the game's inherent awfulness, it won an "honorable mention", which actually turned out to be
good for a $500 gift card. :) You can check out the other winning games on the
CivicHacks website.

Technology

On the backend, I wrote Achilles in Python using Flask as a web
server framework (highly recommended).

I used gevent for greenlets, which made it very easy to write
time-based procedures for each character. For example, here's how a man builds a hut:

ifvillage['build_material']>0:village['build_material']-=1notify(world,village['id'])state['state']='building'notify(world,man['id'])gevent.sleep(world_seconds(world,60*60*17))village['huts']+=1notify(world,village['id'])man['state']=Nonenotify(world,man['id'])send(village['id'],{'event':'{0} finished building a hut.'.format(man['name'])})else:send(village['id'],{'event':'Not enough build material for a hut.'})

The "notify" and "send" functions send JSON objects to the relevant clients over WebSockets.

Compression-based programming

I used this game jam as an opportunity to try out
compression-based programming as
advocated by the excellent Casey Muratori. The idea is, instead of spending time upfront designing
a complex cathedral-like architecture, you should write straightforward, ugly, even repetitive code
(copying and pasting). When it's done, you go back and "compress" the code into something nicer
via refactoring.

Of course, some things are straightforward enough that you can compress them as you go. For example
I always knew there would be a "send" function.

Casey says one of the big problems with "normal" programming is that you assume you know everything
at the beginning of a project, when in reality, you often don't. Compression-based programming has
you architecting things only after you've seen the whole picture.

After a weekend of experimentation, I think this pattern of coding results in plain, boring, easy
to understand code, which is definitely a good thing. Normally I come up with some super fancy way
to write everything very elegantly, which creates more work than it's worth in the end.

Another paradigm Casey rails against is object-oriented programming, so I also tried writing
everything procedurally. The result was again boring but simple and easy.

The biggest win was separating state from behavior. On both the client and server, all the state
lived in a single object, parts of which were operated on by various procedures.

Normally, I often link state and behavior so closely that they're impossible to separate. Every
time I write a closure (probably my favorite bad habit), I squirrel away an opaque bit of state
inextricably tied to an anonymous bit of behavior.

Time to wrap this up: you should try coding in a boring, straightforward,
state-separated-from-behavior, procedural style. The result is a breath of fresh air in a world
of increasingly clever object models and cathedral architectures.