Skip to content

Instantly share code, notes, and snippets.

@bvanderlaan
Created January 2, 2018 03:18
Show Gist options
  • Save bvanderlaan/bdcda67395f8782958842d8e8d3b7e28 to your computer and use it in GitHub Desktop.
Save bvanderlaan/bdcda67395f8782958842d8e8d3b7e28 to your computer and use it in GitHub Desktop.
Here is my implementation of Conway's game of life which I banged out while between flights
'use strict';
const { expect } = require('chai');
// GAME ///////////////////////////////////
class Cell {
constructor(alive = true) {
this.isAlive = alive;
}
die() {
this.isAlive = false;
}
resurrect() {
this.isAlive = true;
}
}
class Universe {
constructor() {
this.grid = [];
}
get cellCount() {
return this.grid.filter(({ cell }) => cell.isAlive).length;
}
addCell(x, y, cell) {
this.grid = this.grid.filter((tuple) => !((tuple.x === x) && (tuple.y === y)))
this.grid.push({ x, y, cell });
}
getCell(x,y) {
const tuple = this.grid.find((tuple) => (tuple.x === x) && (tuple.y === y));
return tuple ? tuple.cell : new Cell(false);
}
getNeighbours(x, y) {
const neighbours = [
this.getCell(x - 1, y),
this.getCell(x - 1, y - 1),
this.getCell(x - 1, y + 1),
this.getCell(x + 1, y),
this.getCell(x + 1, y - 1),
this.getCell(x + 1, y + 1),
this.getCell(x, y - 1),
this.getCell(x, y + 1),
];
return neighbours.filter(cell => cell.isAlive);
}
}
class Rules {
constructor(universe) {
this.universe = universe;
}
process() {
const cell = this.universe.getCell(0,0);
const neighbours = this.universe.getNeighbours(0,0);
if ((neighbours.length < 2) || (neighbours.length > 3)) {
cell.die();
} else if (neighbours.length === 3) {
cell.resurrect();
}
}
}
// ////////////////////////////////////////
describe('Game of Life', () => {
describe('Cell', () => {
it('should be able to be born', () => {
const cell = new Cell();
expect(cell.isAlive).to.be.true;
});
it('should be able to start dead', () => {
const cell = new Cell(false);
expect(cell.isAlive).to.be.false;
});
it('should be able to be killed', () => {
const cell = new Cell();
cell.die();
expect(cell.isAlive).to.be.false;
});
it('should be able to be reborn', () => {
const cell = new Cell();
cell.die();
cell.resurrect();
expect(cell.isAlive).to.be.true;
})
});
describe('Universe', () => {
it('should report zero if no cells', () => {
const universe = new Universe();
expect(universe.cellCount).to.equal(0);
});
it('should be able to add cells', () => {
const universe = new Universe();
const cell = new Cell();
universe.addCell(0,0,cell);
expect(universe.cellCount).to.equal(1);
});
it('should not report dead cells', () => {
const universe = new Universe();
const cell1 = new Cell();
universe.addCell(0,0,cell1);
const cell2 = new Cell();
universe.addCell(0, 1, cell2);
cell2.die();
expect(universe.cellCount).to.equal(1);
});
it('should allow a cell to be retrieved', () => {
const universe = new Universe();
const cell = new Cell();
cell.die();
universe.addCell(0,0, cell);
expect(universe.getCell(0,0)).to.equal(cell);
});
it('should return a dead cell if cell does not exist', () => {
const universe = new Universe();
expect(universe.getCell(0,0)).to.deep.equals(new Cell(false));
})
it('should allow cells to be overwritten', () => {
const universe = new Universe();
const deadCell = new Cell();
deadCell.die();
universe.addCell(0,0, deadCell);
const liveCell = new Cell();
universe.addCell(0,0, liveCell);
expect(universe.getCell(0,0)).to.equal(liveCell);
});
it('should allow negative coordinates', () => {
const universe = new Universe();
const cell = new Cell();
universe.addCell(-20,-10, cell);
expect(universe.getCell(-20,-10)).to.equal(cell);
});
it('should be able to get a cells living neighbours', () => {
const universe = new Universe();
universe.addCell(0, 0, new Cell());
universe.addCell(0, -1, new Cell());
universe.addCell(1, 1, new Cell());
universe.addCell(1, 0, new Cell(false));
universe.addCell(10, 10, new Cell());
expect(universe.getNeighbours(0,0)).to.deep.equals([
new Cell(),
new Cell(),
]);
});
it('should be able to get a cells living neighbours even if the cell does not exist', () => {
const universe = new Universe();
universe.addCell(0, -1, new Cell());
universe.addCell(1, 1, new Cell());
universe.addCell(1, 0, new Cell(false));
universe.addCell(10, 10, new Cell());
expect(universe.getNeighbours(0,0)).to.deep.equals([
new Cell(),
new Cell(),
]);
});
it('should return an empty array if cell has no living neighbours', () => {
const universe = new Universe();
universe.addCell(0, 0, new Cell());
universe.addCell(0, -1, new Cell());
universe.addCell(1, 1, new Cell());
universe.addCell(1, 0, new Cell(false));
universe.addCell(10, 10, new Cell());
expect(universe.getNeighbours(1000,0)).to.deep.equals([]);
});
});
describe('Rules', () => {
// /////////////////
// fewer then 2 = dead
// 2 or 3 = live
// 3 or more = dead
// /////////////////
it('should kill cell if less then 2 neighours', () => {
const universe = new Universe();
universe.addCell(0,0, new Cell());
universe.addCell(0,1, new Cell());
const rules = new Rules(universe);
rules.process();
expect(universe.getCell(0,0).isAlive).to.be.false;
});
it('should leave cell alive if it has 2 neighours', () => {
const universe = new Universe();
universe.addCell(0,0, new Cell());
universe.addCell(0,1, new Cell());
universe.addCell(1,1, new Cell());
const rules = new Rules(universe);
rules.process();
expect(universe.getCell(0,0).isAlive).to.be.true;
});
it('should leave cell dead if it has only 2 neighours', () => {
const universe = new Universe();
universe.addCell(0,0, new Cell(false));
universe.addCell(0,1, new Cell());
universe.addCell(1,1, new Cell());
const rules = new Rules(universe);
rules.process();
expect(universe.getCell(0,0).isAlive).to.be.false;
});
it('should resurrect cell dead if it has at least 3 neighours', () => {
const universe = new Universe();
universe.addCell(0,0, new Cell(false));
universe.addCell(0,1, new Cell());
universe.addCell(1,1, new Cell());
universe.addCell(1,0, new Cell());
const rules = new Rules(universe);
rules.process();
expect(universe.getCell(0,0).isAlive).to.be.true;
});
it('should kill cell if it has more then 3 neighours', () => {
const universe = new Universe();
universe.addCell(0,0, new Cell());
universe.addCell(0,1, new Cell());
universe.addCell(1,1, new Cell());
universe.addCell(1,0, new Cell());
universe.addCell(-1,0, new Cell());
const rules = new Rules(universe);
rules.process();
expect(universe.getCell(0,0).isAlive).to.be.false;
});
it('should apply rules to all cells', () => {
const universe = new Universe();
universe.addCell(0,0, new Cell());
universe.addCell(0,1, new Cell());
universe.addCell(1,1, new Cell());
universe.addCell(1,0, new Cell());
universe.addCell(-1,0, new Cell());
// todo
const rules = new Rules(universe);
rules.process();
expect(universe.getCell(0,0).isAlive).to.be.false;
expect(universe.getCell(0,1).isAlive).to.be.false;
expect(universe.getCell(1,0).isAlive).to.be.false;
expect(universe.getCell(1,1).isAlive).to.be.false;
expect(universe.getCell(-1,0).isAlive).to.be.false;
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment