Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Game of Life in 50 lines and 20 specs
// http://en.wikipedia.org/wiki/Conway's_Game_of_Life
var Game = {
init: function (options) {
this.grid = new Grid(options);
},
putCell: function (x, y, state) {
this.grid.putCell(x, y, state);
},
getCell: function (x, y) {
return this.grid.getCell(x, y);
},
nextStep: function () {
var cells = this.grid.allCells();
_.each(cells, function(currentCell) {
var neighboursCount = getNeighboursCountFor(currentCell, cells);
var nextCellState = getNextCellState(currentCell, neighboursCount);
currentCell.cell.setStateTo(nextCellState);
});
function getNeighboursCountFor(currentCell, cells) {
var neighboursCells = _.filter(cells, function (otherCell) {
return isCloseTo(currentCell, otherCell);
function isCloseTo(currentCell, otherCell) {
var yDistance = otherCell.y - currentCell.y;
var xDistance = otherCell.x - currentCell.x;
return (yDistance !== 0 || xDistance !== 0) && (yDistance >= 1 || xDistance >= 1);
}
});
return neighboursCells.length;
}
function getNextCellState(currentCell, neighboursCount) {
if (currentCell.cell.state === CellState.Dead && neighboursCount === 3) {
return CellState.Live;
} else if (neighboursCount < 2 || neighboursCount >= 3) {
return CellState.Dead;
}
return currentCell.cell.state;
}
}
};
describe('game spec', function () {
describe('when game is initialing', function () {
beforeEach(function () {
Game.init();
});
it ('should create game field', function () {
expect(Game.grid).toBeDefined();
});
});
describe('when game is constructing with options', function () {
beforeEach(function () {
Game.init({ height: 4, width: 14});
});
it ('should create game field with dimentions', function () {
expect(Game.grid.height).toEqual(4);
expect(Game.grid.width).toEqual(14);
});
});
describe('when cell are placed on game', function () {
beforeEach(function () {
Game.init({ height: 4, width: 4});
Game.putCell(0, 2);
});
it ('should field have corresponding cell', function () {
expect(Game.getCell(0, 2)).toBeDefined();
});
});
describe('when game step is happen', function () {
beforeEach(function () {
Game.init({ height: 4, width: 4});
});
describe('live cell with fewer than two live neighbours', function () {
beforeEach(function () {
Game.putCell(0, 0);
Game.nextStep();
});
it ('should cell die', function () {
expect(Game.getCell(0, 0).state).toEqual(CellState.Dead);
});
});
describe('live cell with fewer than two live neighbours', function () {
beforeEach(function () {
Game.putCell(0, 0);
Game.putCell(0, 1);
Game.nextStep();
});
it ('should cell die', function () {
expect(Game.getCell(0, 0).state).toEqual(CellState.Dead);
});
});
describe('live cell with two or three live neighbours lives on to the next generation', function () {
beforeEach(function () {
Game.putCell(0, 0);
Game.putCell(0, 1);
Game.putCell(1, 0);
Game.nextStep();
});
it ('should cell live', function () {
expect(Game.getCell(0, 0).state).toEqual(CellState.Live);
});
it ('rest cells should be dead', function () {
expect(Game.getCell(0, 1).state).toEqual(CellState.Dead);
expect(Game.getCell(1, 0).state).toEqual(CellState.Dead);
});
});
describe('live cell with more than three live neighbours dies, as if by overcrowding', function () {
beforeEach(function () {
Game.putCell(0, 0);
Game.putCell(0, 1);
Game.putCell(1, 0);
Game.putCell(1, 1);
Game.nextStep();
});
it ('should die', function () {
expect(Game.getCell(0, 0).state).toEqual(CellState.Dead);
});
});
describe('dead cell with exactly three live neighbours becomes a live cell, as if by reproduction', function () {
beforeEach(function () {
Game.putCell(0, 0, CellState.Dead);
Game.putCell(0, 1);
Game.putCell(1, 0);
Game.putCell(1, 1);
Game.nextStep();
});
it ('should live', function () {
expect(Game.getCell(0, 0).state).toEqual(CellState.Live);
});
});
});
});
describe('when game step is happen 2x2 grid', function () {
beforeEach(function () {
Game.init({ height: 2, width: 2});
});
describe('live cell with fewer than two live neighbours', function () {
beforeEach(function () {
Game.putCell(0, 0);
Game.nextStep();
});
it ('should cell die', function () {
expect(Game.getCell(0, 0).state).toEqual(CellState.Dead);
});
});
describe('live cell with fewer than two live neighbours', function () {
beforeEach(function () {
Game.putCell(0, 0);
Game.putCell(0, 1);
Game.nextStep();
});
it ('should cell die', function () {
expect(Game.getCell(0, 0).state).toEqual(CellState.Dead);
});
});
describe('live cell with two or three live neighbours lives on to the next generation', function () {
beforeEach(function () {
Game.putCell(0, 0);
Game.putCell(0, 1);
Game.putCell(1, 0);
Game.nextStep();
});
it ('should cell live', function () {
expect(Game.getCell(0, 0).state).toEqual(CellState.Live);
});
it ('rest cells should be dead', function () {
expect(Game.getCell(0, 1).state).toEqual(CellState.Dead);
expect(Game.getCell(1, 0).state).toEqual(CellState.Dead);
});
});
describe('live cell with more than three live neighbours dies, as if by overcrowding', function () {
beforeEach(function () {
Game.putCell(0, 0);
Game.putCell(0, 1);
Game.putCell(1, 0);
Game.putCell(1, 1);
Game.nextStep();
});
it ('should die', function () {
expect(Game.getCell(0, 0).state).toEqual(CellState.Dead);
});
});
describe('dead cell with exactly three live neighbours becomes a live cell, as if by reproduction', function () {
beforeEach(function () {
Game.putCell(0, 0, CellState.Dead);
Game.putCell(0, 1);
Game.putCell(1, 0);
Game.putCell(1, 1);
Game.nextStep();
});
it ('should live', function () {
expect(Game.getCell(0, 0).state).toEqual(CellState.Live);
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment