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
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.
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
a.k.a. Code Connoisseur
- ICQ ‐ 25239620
- AIM ‐ mwlang88
- Yahoo! ‐ mwlang88
- Google ‐ mwlang
- Twitter ‐ @mwlang88
EducationBachelor of Science
Information and Computer Science
- On Hiring Good People
- Week Five in the Gym
- The True Power of the Internet
- Rekindling a desire to workout consistently
- I'd Rather Eat my Britches than Do This
- Mold Killer Recipe
- Gonna be Starting Something New
- Pitch Camp, what is it good for?
- Less communication can be more
- Let the Musings Begin
- Working on a Referral Pre-Launch Site
- Making Commitments, Reaching Out
- Preparing for Countdown
- Ground Zero
- A Reflection of the Technologies Built Things With
- Dynamic Routing in Rails Revisited
- Creating Dynamic Routes at runtime in Rails 4
- Adding Google Analytics script to Sprockets
- Gems you should consider for every Rails projects
- Weak Password will get you Hacked!
- Status updating...