HomeRamblings  ⁄  ProgrammingJavaScript

Conway's Game of Life in CoffeeScript

Published: December 31, 2013 (almost 4 years ago)
Updated: over 2 years ago

I was recently invited by Jonathan Wallace to a local Code Retreat where the developers were going to tackle Tim Conway’s famous Game of Life  Unfortunately, I was unable to attend, but the whole idea sparked my own self-exploration into both CoffeeScript and Middleman, which I had little practice and practical experience with.  You can find the resulting code in its entirety at https://github.com/mwlang/life_with_coffeescript

I learned some interesting things about CoffeeScript along the way as well as DOM manipulation and JavaScript performance issues that are worth sharing.

In case you have not seen the game of life before, below is a screen shot of the game in action with a world that is 25 x 25.

 

The above shows a simple form that allows the size of the world grid to be changed and the world itself is shown with the red cells being the cells that are “alive”.  The world grid is nothing more than an HTML TABLE populated with appropriate number of TR (table rows) filled with same number of TD (table data) which I called the “cell.”

One of my goals in this exercise was to see just how little code I needed to write to implement the game.  I’m not talking about one-liner terseness so much as chasing Einstein’s objective in writing a proof of theory with the objective that “the supreme goal of all theory is to make the irreducible basic elements as simple and as few as possible without having to surrender the adequate representation of a single datum of experience” – that is the commonly paraphrased statement, “as simple as possible, but no simpler” often attributed to him.

To start, I wondered if I could treat the DOM as my data structure instead of creating Arrays to represent the cells of the world in the game and indeed, I was able to accomplish this in my first pass through implementing the game.  There were no Array variables or cached versions of the world within the JavaScript. Everything was in the DOM and I determined the state of the cell as “alive” or “dead” by checking the hasClass property on the individual cell represented by the TD element.  Since the world itself was simply a HTML table with TR and TD being the rows and columns.  I gave each TD an “R1C1”, “R1C2”, … ID and was able to iterate over the entire world and fetch the cells I needed with jQuery’s $(“#R1C1”), $(“#R1C2”), … finders that were dynamically constructed during the loop iterations.  To my surprise, performance was respectable with small worlds (under 25 x 25) as JavaScript and DOM traversal has never been all that fast.  To me, seeing the Game of Life running purely from DOM elements told me just how far JavaScript has come in terms of performance in the last few years.  A 25 x 25 world evolved each generation about every 2 seconds, but a 50 x 50 world was probably closer to 10 seconds per evolution.

Alas, I wanted to see the game go faster and with bigger worlds, so an Array of the cells cached was introduced via the $cells variable, which you’ll see in the code snippets below.  One of the things I really liked about CoffeeScript is the ability to define a range like so:

  $worldRange = [0..$worldSize - 1]

and then use this whenever I needed to iterate over the world:

for y in $worldRange
  for x in $worldRange
    victim = $cells[x][y]
    ...

It is clear in the above that my intent is to iterate over every cell in the #world table without having to remember whether to start with zero or one in dereferencing the $cells (a mental mistake I actually made during first attempt at getting the game to run).

What surprised me about CoffeeScript is just how many of the attempted reduction in verbose code actually worked and one of the most surprising one is this one liner that creates the $cells matrix (array of arrays):

$cells = $worldRange.map -> $worldRange.map -> null

When I was optimizing and caching the DOM into the $cells variable, I came up with this solution:

for y in $worldRange
  $("#world").append(newRow = $("<tr></tr>"))
  for x in $worldRange
    newRow.append($cells[x][y] = $("<td></td>")) 
    $cells[x][y].addClass('alive') if Math.random() < 0.05

 

One of the nice tricks of jQuery is ability to get an immediate handle on any bit of string intended as DOM constructs with wrapping that string with $(…) as with $(“<tr></tr>”) and I found that readability of the code was preserved, if not enhanced by assigning newRow and $cells[x][y] at the same time I was initializing its contents with the $(“#world”).append(newRow = $(“<tr></tr>”) pattern of constructing the cells.

In almost every implementation I saw while looking for inspiration for how to implement the evolve_world method (which advances to next generation by applying the rules of life), the entire array was being reconstructed.  However, since I originally implemented purely in the DOM, I had to find an alternative approach lest I be faced with constantly creating and destroying elements of the DOM, an approach I was sure would lead to a drastically slower running game as it were.  It was almost by accident that I came up with this fairly elegant solution to evolving the world:

evolve_world = ->

  birthing_cells = []
  dying_cells = []

  for y in $worldRange
    for x in $worldRange
      victim = $cells[x][y]
      alive = victim.hasClass('alive')
      neighbors = count_neighbors(x, y)

      dying_cells.push victim if (neighbors < 2) || (neighbors > 3) && alive
      birthing_cells.push victim if (neighbors == 2) || (neighbors == 3) && !alive

  victim.removeClass('alive') for victim in dying_cells
  victim.addClass('alive') for victim in birthing_cells

 

What we’re basically doing is iterating over every cell of the world and pushing the cell into either the birthing_cells or dying_cells array, which we then iterate over to add/remove the “alive” class.  JavaScript is not generally known for being the most readable code, so I was pleasantly surprised at just how readable CoffeeScript can be.  What makes the above elegant to my eyes is that it tells a good story with the beginning, middle, and ending all well defined, delineated and self-explanatory, which is a hallmark of good coding.   The ability to use trailing conditionals made it clear when an operation would be performed without two or three additional lines you’d need in JavasScript and the expressive “for x in y” definitely made for far more concise expression of iterations.

comments powered by Disqus