More Game of Life
At Recurse Center we are highly encouraged to pair program. By means of an introduction we did a workshop today to practice. The challenge was Game of Life, which already came up today in a different conversation, and I wrote about how to do it in Grasshopper a while back. Are Cellular Automata having a moment? Or are they just awesome?
Either way I worked with Joseph Martinez on a JavaScript implementation. Here's what we cooked up:
// grab a DOM element to use for output // and initialize the grid state const target = document.querySelector('#life'); let grid = [ [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], ]; // how many of the eight surrounding cells // at row, column are currently living? function countLiveNeighbors(row, col) { let w = grid[0].length - 1; let h = grid.length - 1; let neighbors = [ row >= 1 && col >= 1 ? grid[row - 1][col - 1] : 0, row >= 1 ? grid[row - 1][col] : 0, row >= 1 && col < w ? grid[row - 1][col + 1] : 0, col >= 1 ? grid[row ][col - 1] : 0, col < w ? grid[row ][col + 1] : 0, row < h && col >= 1 ? grid[row + 1][col - 1] : 0, row < h ? grid[row + 1][col] : 0, row < h && col < w ? grid[row + 1][col + 1] : 0, ]; let count = neighbors.reduce((accum, n) => accum + n, 0); return count; }; // loop over the grid, apply rules to each cell // build up the next grid state, make that current function applyRules() { let newGrid = []; for (let i = 0; i < grid.length; i++) { newGrid[i] = []; for (let j = 0; j < grid.length; j++) { let alive = grid[i][j]; let neighbors = countLiveNeighbors(i, j); if (neighbors < 2 && alive) alive = 0; else if (neighbors == (2 || 3) && alive) alive = 1; else if (neighbors > 3 && alive) alive = 0; else if (neighbors == 3 && !alive) alive = 1; newGrid[i][j] = alive; } } grid = newGrid; } // translate the grid state data to something readable function draw() { const gridAsString = grid.map((row) => row.join(' ')).join('\n'); target.innerHTML = gridAsString; } // every second, apply the rules and redraw setInterval(() => { applyRules() draw(); }, "1000");
Here it is in action:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
I'm sure there are more elegant ways to deal with the neighbor checking, and it would be fun to be able to use a different input state (maybe from a user-provided bitmap?). But that's good enough for today!