Skip to content

Instantly share code, notes, and snippets.

@ajeffrey
Last active September 30, 2017 16:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ajeffrey/092b71a3ad5f2034601574262c5c1167 to your computer and use it in GitHub Desktop.
Save ajeffrey/092b71a3ad5f2034601574262c5c1167 to your computer and use it in GitHub Desktop.
Functional CGoL
const irange = (a, b) => R.range(a, b + 1);
const neighbourRange = x => irange(x - 1, x + 1);
const between = R.curry((a, b, x) => x >= a && x < b);
const cellExists = (x, y) => between(0, grid.length, y) && between(0, grid[0].length, x);
const getCell = R.curry((x, y, grid) => cellExists(x, y) ? grid[y][x] : 0);
const neighbourhood = (x, y) => R.xprod(neighbourRange(x), neighbourRange(y));
const adjacent = R.curry((x, y) => R.reject(R.equals([x, y]), neighbourhood(x, y)));
const getCells = R.curry((cells, grid) => R.map(([x, y]) => getCell(x, y, grid), cells));
const isAlive = (cell, neighbours) => neighbours === 3 || (neighbours === 2 && cell);
const neighbours = (x, y, grid) => getCells(adjacent(x, y), grid);
const countAlive = R.reduce(R.add, 0);
const calculateLife = R.curry((x, y, grid) => isAlive(getCell(x, y, grid), countAlive(neighbours(x, y, grid))));
const computeRow = R.curry((y, grid) => R.map(x => calculateLife(x, y, grid), irange(-1, grid[0].length)));
const computeNext = (grid) => R.map(y => computeRow(y, grid), irange(-1, grid.length));
const rowIsEmpty = R.none(R.equals(true));
const col = R.curry((x, grid) => R.map(R.nth(x), grid));
const lastCol = (grid) => col(grid[0].length - 1, grid);
const colIsEmpty = R.none(R.equals(true));
const dropRowsHead = R.dropWhile(rowIsEmpty);
const dropRowsTail = R.dropLastWhile(rowIsEmpty);
const firstColIsEmpty = R.pipe(col(0), colIsEmpty);
const lastColIsEmpty = R.pipe(lastCol, colIsEmpty);
const dropFirstCol = R.map(R.tail);
const dropLastCol = R.map(R.init);
const dropColsHead = R.when(firstColIsEmpty, R.pipe(dropFirstCol, dropColsHead));
const dropColsTail = R.when(lastColIsEmpty, R.pipe(dropLastCol, dropColsTail));
const cullGrid = R.pipe(dropRowsHead, dropRowsTail, dropColsHead, dropColsTail);
function draw(grid) {
const target = document.getElementById('container');
let html = '';
let x, y;
for(y = 0; y < grid.length; y++) {
html += '<div class="row">';
for(x = 0; x < grid[y].length; x++) {
const className = 'cell' + (getCell(x, y, grid) ? ' alive' : '');
html += `<div class="${className}"></div>`;
}
html += '</div>';
}
//target.style = `-webkit-transform: scale(${1 / Math.max(x, y) * 10})`;
target.innerHTML = html;
};
let grid = [
[0, true, 0, 0, true, 0, 0, true, true],
[true, 0, 0, 0, 0, 0, 0, true, true],
[true, 0, 0, 0, true, 0, 0, 0, 0],
[true, true, true, true, 0, 0, 0, 0, 0]
];
let interval = null;
function drawSmoothly() {
draw(grid);
requestAnimationFrame(drawSmoothly);
};
drawSmoothly();
function step() {
grid = cullGrid(computeNext(grid));
// console.log(grid);
draw(grid);
}
function run() {
interval = setTimeout(() => {
step();
run();
}, 1000 / 10);
}
function pause() {
clearInterval(interval);
interval = null;
}
window.step = step;
window.run = run;
window.pause = pause;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment