Last active
November 14, 2019 14:39
-
-
Save christianscott/60af56ea6f3d628a6ab437e5a7c11cd3 to your computer and use it in GitHub Desktop.
Conway's game of life
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @ts-check | |
const ALIVE = 1; | |
const DEAD = 0; | |
/** | |
* @param {0 | 1} cell | |
* @param {number} nNeighbours | |
*/ | |
function getNextCell(cell, nNeighbours) { | |
if (cell === DEAD) { | |
return nNeighbours === 3 ? ALIVE : DEAD; | |
} | |
if (nNeighbours === 2 || nNeighbours === 3) { | |
return ALIVE; | |
} | |
return DEAD; | |
} | |
/** | |
* @param {{ length: number; }} arr | |
* @param {number} index | |
*/ | |
function isOutOfBounds(arr, index) { | |
return index < 0 || index >= arr.length; | |
} | |
export const ConwaysGameBoard = { | |
/** @typedef {(0 | 1)[][]} ConwaysGameBoard */ | |
/** | |
* @param {any[][]} arr | |
*/ | |
_check(arr) { | |
const n = arr.length; | |
for (const row of arr) { | |
if (row.length !== n) { | |
throw new Error("not a square 2 dimensional array"); | |
} | |
if (!row.every(el => el === ALIVE || el === DEAD)) { | |
throw new Error("contains values other than ALIVE or DEAD"); | |
} | |
} | |
}, | |
/** | |
* @param {any[][]} board | |
* @returns {ConwaysGameBoard} | |
*/ | |
create(board) { | |
ConwaysGameBoard._check(board); | |
return board; | |
}, | |
/** @param {ConwaysGameBoard} board */ | |
update(board) { | |
const nextBoard = ConwaysGameBoard.clone(board); | |
for (let row = 0; row < board.length; row++) { | |
for (let col = 0; col < board[row].length; col++) { | |
const nNeighbours = ConwaysGameBoard.countNeighboursOf(board, { | |
row, | |
col | |
}); | |
const cell = board[row][col]; | |
const nextCell = getNextCell(cell, nNeighbours); | |
nextBoard[row][col] = nextCell; | |
} | |
} | |
return nextBoard; | |
}, | |
/** | |
* @param {ConwaysGameBoard} board | |
* @param {{ row: number; col: number }} cellRow | |
*/ | |
countNeighboursOf(board, { row: cellRow, col: cellCol }) { | |
let total = 0; | |
for (const row of [cellRow - 1, cellRow, cellRow + 1]) { | |
if (isOutOfBounds(board, row)) { | |
continue; | |
} | |
for (const col of [cellCol - 1, cellCol, cellCol + 1]) { | |
// don't count the current cell | |
if ( | |
isOutOfBounds(board[row], col) || | |
(row === cellRow && col === cellCol) | |
) { | |
continue; | |
} | |
total += board[row][col]; | |
} | |
} | |
return total; | |
}, | |
/** @param {ConwaysGameBoard} board */ | |
print(board) { | |
const printed = board.map(row => row.join("")).join("\n"); | |
console.log(printed + "\n"); | |
}, | |
/** | |
* @param {ConwaysGameBoard} board | |
* @returns {ConwaysGameBoard} | |
*/ | |
clone(board) { | |
return JSON.parse(JSON.stringify(board)); | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ConwaysGameBoard } from './conways_game_of_life'; | |
(function example() { | |
let board = ConwaysGameBoard.create([[1, 1, 1], [0, 0, 1], [0, 1, 1]]); | |
let iterations = 10; | |
while (iterations--) { | |
board = ConwaysGameBoard.update(board); | |
ConwaysGameBoard.print(board); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment