Skip to content

Instantly share code, notes, and snippets.

@devill
Created August 5, 2021 18:51
Show Gist options
  • Select an option

  • Save devill/3d6cd56871b3f7890c91855172021f57 to your computer and use it in GitHub Desktop.

Select an option

Save devill/3d6cd56871b3f7890c91855172021f57 to your computer and use it in GitHub Desktop.
import expect from 'expect.js'
class GameOfLifeBoard {
alvie = {};
isAlive(cell) {
return !!this.alvie[cell];
}
spawn(cell) {
this.alvie[cell] = cell
return this;
}
list() {
return Object.values(this.alvie);
}
}
class GameOfLife {
board = new GameOfLifeBoard();
isAlive(cell) {
return this.board.isAlive(cell);
}
spawn(cell) {
this.board.spawn(cell)
}
evolve() {
this.board = this.board
.list()
.flatMap((cell) => cell.neighbours())
.filter(cell => this.shouldBeAlive(cell))
.reduce((next, cell) => next.spawn(cell), new GameOfLifeBoard());
}
shouldBeAlive(cell) {
let count = this.countNeighbours(cell);
return (count === 2 && this.isAlive(cell)) || count === 3;
}
countNeighbours(cell) {
return cell.neighbours()
.filter(cell => this.isAlive(cell))
.length
}
}
class Cell {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `(${this.x},${this.y})`
}
neighbours() {
return [
{ x: -1, y: -1},
{ x: -1, y: 0},
{ x: -1, y: 1},
{ x: 0, y: -1},
{ x: 0, y: 1},
{ x: 1, y: -1},
{ x: 1, y: 0},
{ x: 1, y: 1},
]
.map(delta => new Cell(this.x + delta.x, this.y + delta.y))
}
}
describe("GameOfLife", () => {
let game = null;
const cell = (x, y) => new Cell(x, y);
const expectDead = (cell) => expect(game.isAlive(cell)).to.be(false);
const expectAlive = (cell) => expect(game.isAlive(cell)).to.be(true);
beforeEach(() => {
game = new GameOfLife();
});
specify("that an empty world has dead (0,0) cell", () => {
expectDead(cell(0,0));
});
specify("that a (0,0) cell can be spawned", () => {
game.spawn(cell(0,0));
expectAlive(cell(0,0));
});
specify("that a (3,5) cell can be spawned independently of (0,0) cell", () => {
game.spawn(cell(3, 5));
expectDead(cell(0,0));
});
specify("that empty world has dead (0,0) cell after being evolved", () => {
game.evolve();
expectDead(cell(0,0));
});
specify("that lonely (0,0) cell dies when evolved", () => {
game.spawn(cell(0,0));
game.evolve();
expectDead(cell(0,0));
});
specify("that (0,0) cell with single neighbour (-1, -1) dies", () => {
game.spawn(cell( 0, 0));
game.spawn(cell(-1,-1));
game.evolve();
expectDead(cell(0,0));
});
[
cell(-1, 0),
cell(-1, 1),
cell( 0,-1),
cell( 0, 1),
cell( 1,-1),
cell( 1, 0),
cell( 1, 1)
].forEach(neighbourCell => {
specify(`that (0,0) cell with two neighbours (-1, -1) and ${neighbourCell} stays alive`, () => {
game.spawn(cell( 0, 0));
game.spawn(cell(-1,-1));
game.spawn(neighbourCell);
game.evolve();
expectAlive(cell(0,0));
});
});
specify("that (0,0) cell with three neighbours (-1, -1), (-1, 0) and (-1,1) stays alive", () => {
game.spawn(cell( 0, 0));
game.spawn(cell(-1,-1));
game.spawn(cell(-1, 0));
game.spawn(cell(-1, 1));
game.evolve();
expectAlive(cell(0,0));
});
specify("that dead (0,0) cell with three neighbours (-1, -1), (-1, 0) and (-1,1) spawn", () => {
game.spawn(cell(-1,-1));
game.spawn(cell(-1, 0));
game.spawn(cell(-1, 1));
game.evolve();
expectAlive(cell(0,0));
});
specify("that dead (0,0) cell with two neighbours (0, -1) and (1,1) stays dead", () => {
game.spawn(cell( 0,-1));
game.spawn(cell( 1, 1));
game.evolve();
expectDead(cell(0,0));
});
specify("that (3,5) cell with two neighbours stays alive", () => {
game.spawn(cell(3,5));
game.spawn(cell(3,4));
game.spawn(cell(4,6));
game.evolve();
expectAlive(cell(3,5));
});
specify("that (-2,8) cell with two neighbours stays alive", () => {
game.spawn(cell(-2,8));
game.spawn(cell(-1,7));
game.spawn(cell(-3,9));
game.evolve();
expectAlive(cell(-2,8));
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment