Skip to content

Instantly share code, notes, and snippets.

@devill
Last active November 29, 2021 22:06
Show Gist options
  • Select an option

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

Select an option

Save devill/e91be7ac68e0bf1bb43affb9ad103b34 to your computer and use it in GitHub Desktop.
import expect from 'expect.js'
Array.prototype.groupBy = function (callback) {
return this.reduce((acc, item) => {
const key = callback(item);
acc[key] = acc[key] || [];
acc[key].push(item);
return acc;
}, {})
}
Array.prototype.listGroupsBy = function (callback) {
return Object.values(this.groupBy(callback));
}
class Board {
alive = {};
isAlive(cell) {
return !!this.alive[cell];
}
spawn(cell) {
this.alive[cell] = cell;
return this;
}
list() {
return Object.values(this.alive);
}
}
class Cell {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `(${this.x},${this.y})`
}
neighbours() {
return [
new Cell(this.x - 1,this.y - 1),
new Cell(this.x - 1,this.y + 0),
new Cell(this.x - 1,this.y + 1),
new Cell(this.x + 0,this.y - 1),
new Cell(this.x + 0,this.y + 1),
new Cell(this.x + 1,this.y - 1),
new Cell(this.x + 1,this.y + 0),
new Cell(this.x + 1,this.y + 1),
];
}
}
const evolved = (board) => {
let shouldBeAlive = ({cell, neighbourCount}) =>
(neighbourCount === 2 && board.isAlive(cell)) || neighbourCount === 3;
return board
.list()
.flatMap(cell => cell.neighbours())
.listGroupsBy(item => item.toString())
.map(group => { return { cell: group[0], neighbourCount: group.length }; })
.filter(shouldBeAlive)
.reduce((newBoard, {cell}) => newBoard.spawn(cell), new Board());
}
describe('Array.groupBy', () => {
specify('that empty array groups into empty object', () => {
expect([].groupBy(item => item)).to.eql({});
});
specify('that distinct elements map onto separate keys', () => {
expect([1,2,3].groupBy(item => item)).to.eql({
1: [1],
2: [2],
3: [3]
});
});
specify('that elements in the same group are grouped under the same key', () => {
expect([1,2,2,3,3,3].groupBy(item => item)).to.eql({
1: [1],
2: [2, 2],
3: [3, 3, 3]
});
});
specify('that elements are grouped by the key provided by the call back funcion', () => {
expect(['a','b','cc','dd','eee'].groupBy(item => item.length)).to.eql({
1: ['a','b'],
2: ['cc','dd'],
3: ['eee']
});
});
});
describe('Game of Life', () => {
let board = null;
const cell = (x,y) => new Cell(x,y);
const expectCellToBe = (alive) => {
return (cell) => {
return {
in: (board) => {
expect(board.isAlive(cell)).to.be(alive);
}
}
}
}
const expectDead = expectCellToBe(false);
const expectAlive = expectCellToBe(true);
beforeEach(() => {
board = new Board();
});
describe('Board', () => {
specify('that (0,0) cell is dead on empty board', () => {
expectDead(cell(0,0)).in(board);
});
specify('that (0,0) cell can be spawned', () => {
board.spawn(cell(0,0));
expectAlive(cell(0,0)).in(board);
});
specify('that (3,5) can be spawned independently of (0,0)', () => {
board.spawn(cell(3,5));
expectDead(cell(0,0)).in(board);
});
});
describe('evolved', () => {
specify('that (0,0) cell remains dead in an empty world', () => {
expectDead(cell(0,0)).in(evolved(board));
});
specify('that lonely (0,0) cell dies', () => {
board.spawn(cell(0,0));
expectDead(cell(0,0)).in(evolved(board));
});
specify('that (0,0) cell with two neighbours survives', () => {
board.spawn(cell(0,0));
board.spawn(cell(1,0));
board.spawn(cell(0,-1));
expectAlive(cell(0,0)).in(evolved(board));
});
specify('that (0,0) cell with three neighbours survives', () => {
board.spawn(cell(0,0));
board.spawn(cell(1,0));
board.spawn(cell(0,-1));
board.spawn(cell(1,-1));
expectAlive(cell(0,0)).in(evolved(board));
});
specify('that dead (0,0) cell with four neighbours does not spawn', () => {
board.spawn(cell(0,1));
board.spawn(cell(1,0));
board.spawn(cell(0,-1));
board.spawn(cell(1,-1));
expectDead(cell(0,0)).in(evolved(board));
});
specify('that dead (0,0) cell with two neighbours does not spawn', () => {
board.spawn(cell(1,0));
board.spawn(cell(0,-1));
expectDead(cell(0,0)).in(evolved(board));
});
specify('that dead (0,0) cell with three neighbours spawn', () => {
board.spawn(cell(1,0));
board.spawn(cell(0,-1));
board.spawn(cell(1,-1));
expectAlive(cell(0,0)).in(evolved(board));
});
specify('that (3,5) does not count as a neighbour of (0,0)', () => {
board.spawn(cell(0,0));
board.spawn(cell(0,-1));
board.spawn(cell(3,5));
expectDead(cell(0,0)).in(evolved(board));
});
specify('that (3,5) cell with two neighbours stays alive', () => {
board.spawn(cell(3,5));
board.spawn(cell(2,5));
board.spawn(cell(3,6));
expectAlive(cell(3,5)).in(evolved(board));
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment