Ember: Conway's Game of Life22 Sep 2015

During my apprenticeship, we were encouraged to keep a breakable toy for
trying out new concepts or developing a deeper understanding for the
framework/tools you are using. The basic idea is to have a project you don’t
have to ship so you can spend time experimenting.

Recently I implemented Conway’s Game of Life for the first time using Ember.
This exposed me to parts of Ember that I think are interesting to talk about.

Disclaimer: As this is a breakable toy, before I start I like to choose a
concept or addon that I haven’t used before to see how it feels. For this I
chose ember-computed-decorators to try out.

The game

Conway’s Game of Life is a zero player game, meaning that it requires no input
from any players and the result can be determined from the initial setup. The
game is played on an infinite two-dimensional board made up of cells. A cell is
in one of two states, alive or dead. On a turn, each cell has its state change
depending on the states of its surrounding eight neighbours. All cells change
state at the same time. The next state is determined from the following rules:

If the cell is alive and has less than two living neighbours then the cell
dies.

If the cell is alive and has two or three living neighbours then it remains
alive.

If the cell is alive and has more than three living neighbours then the
cell dies.

If the cell is dead and has exactly three live neighbours then the cell
will come to life.

The board

Let’s get the most obvious problem out of the way first. How should we model a
infinite board? I decided to model my board as a torus, which means the top
and bottom edges are considered to be attached as are the left and right sides.
This means that we have an infinite, 2-dimensional board that repeats every
certain number of cells.

We’ll need a way to generate the board given a width and height at which the
universe repeats, and then tell each cell who its eight neighbours are.
Let’s start with a game-board component:

So I’ve started here with code I wish I had. My board is initialized with the
generateBoard function and then the neighbours for each cell needs to be
registered with that cell. Let’s start by generating our board:

The above code loops over the height and width to create a matrix of cells.
We’ll need a Cell model to contain the logic for working out the next state
according to the rules, but we’ll come back to that later. Let’s move on to
registering neighbours:

Here we use computed properties to keep a count of living neighbours, then we
use that to determine what the nextState should be. Finally, we’ve added a
step function that changes the alive property to the nextState.

Let’s see how this runs:

Something isn’t right here.

Ember run loop

Turns out, we’ve forgotten that all cells change state at the same time. Our
code that runs row.invoke('step') will iterate through each cell in the row
and tell it to move to the next state. This causes the state to change and
affect the neighbouring cells when they should be interested in the previous
state. To fix this we need to look at Ember’s run loop.

The run loop is used to schedule work into queues. Each iteration of the run
loop runs each queue in a particular order to ensure that operations are done in
the most efficient way possible. For example, there is a routerTransitions
queue which contains router transition jobs. This come just before the render
queue which is responsible for updating the DOM with the current state of the
application. If these queues were reversed, then it would take two iterations
of the run loop to move between pages instead of one.

The default queues are as follows: sync, actions, routerTransitions,
render, afterRender and finally destroy. We can put tasks into a specific
queue using Ember.run.schedule. Armed with this knowledge let’s edit our
step function: