In the first part of this 3-part entry, we did some project setup and wrote the basis of our Conwayâ€™s Game of Life implementation.

At this moment, the game consists of a board which can have cells added to it. We also have a way to retrieve cells from the board using their identification as well as a way to calculate the number of living neighbors a specific cell has.

The next step is to implement the four rules for calculating a new board state:

â€¢ If a living cell has less than two living neighbors, it dies

â€¢ If a living cell has more than three living neighbors, it dies

â€¢ If a living cell has exactly two or three living neighbors, it lives

â€¢ If a dead cell has exactly three living neighbors, it comes back to life

We start with the first rule and write a test for it:

```
describe('calculateNextState', function() {
it('dies if there are less than 2 living neighbors', function() {
board.addCell(new Cell(0, 0, true));
expect(board.calculateNextState(cell).isAlive()).toBe(false);
});
});
```

```
```

So we add an additional cell to the board and expect it to die, because there are less than 2 living neighbors next to it.

Remember, that in our â€œbeforeEachâ€ method in part 1 of this post, we made it so that one cell is always added to the board before each test:

```
var board, cell;
beforeEach(function() {
board = new Board();
cell = new Cell(1, 1, true);
board.addCell(cell);
});
```

So now there are 2 cells on the board next to each other. We expect our new method â€œcalculateNextStateâ€ to return a cell object. So letâ€™s make the test pass:

```
calculateNextState: function(cell) {
return new Cell(cell.x, cell.y, false);
}
```

```
```

We just return a new temporary cell, which is dead. Now, to write a test to generalize our implementation, we tackle the second rule:

```
it('dies if there are more than 3 living neighbors', function() {
board.addCell(new Cell(0, 1, true));
board.addCell(new Cell(0, 2, true));
board.addCell(new Cell(0, 0, true));
board.addCell(new Cell(1, 2, true));
expect(board.calculateNextState(cell).isAlive()).toBe(false);
});
```

```
```

We add 4 additional cells, all of which are alive. We expect the cell under test do die, which it does with our current implementation. We are forced to write another test in order to generalize our implementation, so on the third rule:

```
it('lives if there are 2 or 3 living neighbors', function() {
board.addCell(new Cell(0, 0, true));
board.addCell(new Cell(0, 1, true));
expect(board.calculateNextState(cell).isAlive()).toBe(true);
});
```

Here we add 2 additional cells to the board and expect the cell under test to be alive in the next iteration. We finally have a failing test again! Letâ€™s make it pass:

```
calculateNextState: function(cell) {
var tempCell = new Cell(cell.x, cell.y, cell.alive);
var livingNeighbors = this.getAliveNeighbors(cell);
if(livingNeighbors == 2 || livingNeighbors == 3) {
tempCell.alive = true;
} else {
tempCell.alive = false;
}
return tempCell;
}
```

So this is quite a jump from our previous implementation, but the problem at hand isnâ€™t all that complicated, so we felt confident enough. First, we count the cellâ€™s living neighbors and simply apply our three rules in the form of an if-statement.

Now there is only one rule left to implement, so letâ€™s get on to writing a test for it:

```
it('comes back to live if there are exactly 3 living neighbors ', function() {
board.addCell(new Cell(0, 0, true));
board.addCell(new Cell(0, 1, true));
board.addCell(new Cell(0, 2, true));
cell.alive = false;
expect(board.calculateNextState(cell).isAlive()).toBe(true);
});
```

We add 3 additional cells and set the â€œaliveâ€ status of our cell under test to false. Unfortunately, this test passes, although we didnâ€™t implement this yet. This is a tricky situation, because we didnâ€™t implement this yet. At this stage it is a good idea to try and write a failing test for this rule:

```
it('does not come back to live if there are exactly 2 living neighbors ', function() {
board.addCell(new Cell(0, 0, true));
board.addCell(new Cell(0, 1, true));
cell.alive = false;
expect(board.calculateNextState(cell).isAlive()).toBe(false);
});
```

Yes! This test fails. We add 2 additional cells and expect our dead cell to stay dead, but instead it comes back to life. Now itâ€™s time to make it pass:

```
calculateNextState: function(cell) {
var tempCell = new Cell(cell.x, cell.y, cell.alive);
var livingNeighbors = this.getAliveNeighbors(cell);
if(cell.alive) {
if(livingNeighbors == 2 || livingNeighbors == 3) {
tempCell.alive = true;
} else {
tempCell.alive = false;
}
} else {
if(livingNeighbors == 3) {
tempCell.alive = true;
}
}
return tempCell;
}
```

Alright, so we just check if the cell is alive or dead and act according to our rules in each case. Simple stuff, right? 😉

This example, I think, is a nice reminder of how when using TDD you need the discipline to write all the necessary tests in order not to fall into the â€œgreen-testsâ€-trap. What this means is, that just because all your tests are green does not mean that your implementation does not contain any errors or that you thought of every possible case.

Sooooâ€¦ whatâ€™s next? We have a board, cells, neighbors and even a way to calculate the next iteration for each one of our cells. The next step should probably be toâ€¦wellâ€¦actually do that, calculate the new board state that is.

With our tests in place, we are pretty confident that the logic concerning the rules and a cellâ€™s neighbors is stable. For testing a method, which just executes our existing methods on the whole board we will focus on three different tests. A test with all dead cells, a test with all living cells and a test with many mixed cells. Letâ€™s get to it:

```
describe('step', function() {
it('calculates the new state for all dying cells', function() {
board.addCell(new Cell(0, 0, true));
board.step();
expect(board.getCellAt(0, 0).isAlive()).toBe(false);
expect(board.getCellAt(1, 1).isAlive()).toBe(false);
});
});
```

Alright, simple enough, we populate the board with 2 cells and expect them both to die. Letâ€™s make it pass:

```
step: function() {
var cells = this.cells;
var tempBoard = {};
for(var c in this.cells) {
var cell = this.cells[c];
var newCell = this.calculateNextState(cell);
tempBoard[c] = newCell;
}
this.cells = tempBoard;
}
```

This implementation is actually pretty simple. We expect all of our above-mentioned tests to pass with it. We just get our cells, iterate through them, calculate their new state and add them to a temporary board. When we are done, we replace our current board with the temporary one. Of course, if we were concerned about performance issues at this point, we could come up with a faster, more sophisticated solution. But right now, this should suffice and it makes our first test pass. So letâ€™s write another one:

```
it('calculates the new state for all living cells', function() {
board.addCell(new Cell(0, 0, true));
board.addCell(new Cell(1, 2, true));
board.step();
expect(board.getCellAt(0, 0).isAlive()).toBe(false);
expect(board.getCellAt(1, 1).isAlive()).toBe(true);
});
```

This time, we add 3 living cells and assert that the one with 2 neighbors will survive, while the cell with 1 neighbor dies. It passes, so on the our final test:

```
it('calculates the new state correctly for many cells', function() {
board.addCell(new Cell(0, 1, true));
board.addCell(new Cell(0, 2, true));
board.addCell(new Cell(0, 0, false));
board.addCell(new Cell(2, 1, true));
board.addCell(new Cell(1, 0, true));
board.addCell(new Cell(2, 2, true));
board.addCell(new Cell(1, 2, false));
board.addCell(new Cell(2, 0, false));
board.step();
expect(board.getCellAt(1, 1).isAlive()).toBe(false);
expect(board.getCellAt(0, 1).isAlive()).toBe(true);
expect(board.getCellAt(2, 2).isAlive()).toBe(true);
});
```

In this test, we go crazy and add a bunch of cells to the board, asserting their correct state afterwards. This can get quite complex, so it can be helpful to draw a small board with all cells and their states on a piece of paper to be able to visualize it a little better. Alas, the test passes and we are confident that our implementation of the â€œstepâ€ function is stable. We could, of course go ahead and write an infinite amount of tests covering all possible board states, but with our stable tests for the internal functions and the relative simplicity of the â€œstepâ€ function, this should suffice.

Well! This concludes part 2 of this series, I hope it was enjoyable and / or enlightening in one way or the other. In the third and final part we will be looking at a way to include this wonderful little program we just created into a website!

Full source code of this example so far:

Tests: (spec.js)

```
describe('Conways Game of Life', function() {
it('is a sanity test', function() {
expect(true).toBe(true);
});
var board, cell;
beforeEach(function() {
board = new Board();
cell = new Cell(1, 1, true);
board.addCell(cell);
});
describe('addCell', function() {
it('adds a cell to a board', function() {
expect(board.cells.x1y1).toEqual(cell);
});
});
describe('getCellAt', function() {
it('returns the cell at the provided coordinates', function() {
expect(board.getCellAt(1, 1)).toEqual(cell);
});
});
describe('getAliveNeighbors', function() {
it('returns 0 if there are no other cells', function() {
expect(board.getAliveNeighbors(cell)).toEqual(0);
});
it('returns 1 if there is one alive cell next to the cell', function() {
var neighborCell = new Cell(0, 1, true);
board.addCell(neighborCell);
expect(board.getAliveNeighbors(cell)).toEqual(1);
});
it('returns 8 if there are 8 neighbors available', function() {
board.addCell(new Cell(0, 1, true));
board.addCell(new Cell(0, 2, true));
board.addCell(new Cell(0, 0, true));
board.addCell(new Cell(2, 1, true));
board.addCell(new Cell(1, 0, true));
board.addCell(new Cell(2, 2, true));
board.addCell(new Cell(1, 2, true));
board.addCell(new Cell(2, 0, true));
expect(board.getAliveNeighbors(cell)).toEqual(8);
});
it('returns 1 if there are 7 dead cells next available', function() {
board.addCell(new Cell(0, 1, true));
board.addCell(new Cell(0, 2, false));
board.addCell(new Cell(0, 0, false));
board.addCell(new Cell(2, 1, false));
board.addCell(new Cell(1, 0, false));
board.addCell(new Cell(2, 2, false));
board.addCell(new Cell(1, 2, false));
board.addCell(new Cell(2, 0, false));
expect(board.getAliveNeighbors(cell)).toEqual(1);
});
});
describe('calculateNextState', function() {
it('dies if there are less than 2 living neighbors', function() {
board.addCell(new Cell(0, 0, true));
expect(board.calculateNextState(cell).isAlive()).toBe(false);
});
it('dies if there are more than 3 living neighbors', function() {
board.addCell(new Cell(0, 1, true));
board.addCell(new Cell(0, 2, true));
board.addCell(new Cell(0, 0, true));
board.addCell(new Cell(1, 2, true));
expect(board.calculateNextState(cell).isAlive()).toBe(false);
});
it('lives if there are 2 or 3 living neighbors', function() {
board.addCell(new Cell(0, 0, true));
board.addCell(new Cell(0, 1, true));
expect(board.calculateNextState(cell).isAlive()).toBe(true);
});
it('comes back to live if there are exactly 3 living neighbors ', function() {
board.addCell(new Cell(0, 0, true));
board.addCell(new Cell(0, 1, true));
board.addCell(new Cell(0, 2, true));
cell.alive = false;
expect(board.calculateNextState(cell).isAlive()).toBe(true);
});
it('does not come back to live if there are exactly 2 living neighbors ', function() {
board.addCell(new Cell(0, 0, true));
board.addCell(new Cell(0, 1, true));
cell.alive = false;
expect(board.calculateNextState(cell).isAlive()).toBe(false);
});
});
describe('step', function() {
it('calculates the new state for all dying cells', function() {
board.addCell(new Cell(0, 0, true));
board.step();
expect(board.getCellAt(0, 0).isAlive()).toBe(false);
expect(board.getCellAt(1, 1).isAlive()).toBe(false);
});
it('calculates the new state for all living cells', function() {
board.addCell(new Cell(0, 0, true));
board.addCell(new Cell(1, 2, true));
board.step();
expect(board.getCellAt(0, 0).isAlive()).toBe(false);
expect(board.getCellAt(1, 1).isAlive()).toBe(true);
});
it('calculates the new state correctly for many cells', function() {
board.addCell(new Cell(0, 1, true));
board.addCell(new Cell(0, 2, true));
board.addCell(new Cell(0, 0, false));
board.addCell(new Cell(2, 1, true));
board.addCell(new Cell(1, 0, true));
board.addCell(new Cell(2, 2, true));
board.addCell(new Cell(1, 2, false));
board.addCell(new Cell(2, 0, false));
board.step();
expect(board.getCellAt(1, 1).isAlive()).toBe(false);
expect(board.getCellAt(0, 1).isAlive()).toBe(true);
expect(board.getCellAt(2, 2).isAlive()).toBe(true);
});
});
});
```

Implementation (conway.js):

```
var Cell = function(x, y, alive) {
this.x = x;
this.y = y;
this.alive = alive;
};
Cell.prototype = {
isAlive : function() {
return this.alive;
}
};
var Board = function() {
this.cells = {};
};
Board.prototype = {
addCell: function(cell){
this.cells[getCellRepresentation(cell.x, cell.y)] = cell;
},
getCellAt: function(x, y) {
return this.cells[getCellRepresentation(x, y)];
},
getAliveNeighbors: function(cell) {
var x = cell.x;
var y = cell.y;
var aliveCells = 0;
for (var i = -1; i < 2; i++) {
for(var j = -1; j < 2; j++) {
if(i === 0 && i == j) {
continue;
}
var currentCell = this.getCellAt(x + i, y + j);
if(currentCell && currentCell.isAlive()) {
aliveCells++;
}
}
}
return aliveCells;
},
calculateNextState: function(cell) {
var tempCell = new Cell(cell.x, cell.y, cell.alive);
var livingNeighbors = this.getAliveNeighbors(cell);
if(cell.alive) {
if(livingNeighbors == 2 || livingNeighbors == 3) {
tempCell.alive = true;
} else {
tempCell.alive = false;
}
} else {
if(livingNeighbors == 3) {
tempCell.alive = true;
}
}
return tempCell;
},
step: function() {
var cells = this.cells;
var tempBoard = {};
for(var c in this.cells) {
var cell = this.cells[c];
var newCell = this.calculateNextState(cell);
tempBoard[c] = newCell;
}
this.cells = tempBoard;
}
};
function getCellRepresentation (x, y) {
return "x" + x + "y" + y;
}
```