// handles negative index and index bigger than its array length function guard(index, array) { if (index > (array.length - 1)) return 0; if (index < 0) return (array.length - 1); return index; } // get the next evolution of a 1D CA initial state function evolve1d(state, rules) { function getCell(index) { const safeIndex = guard(index, state); return state[safeIndex]; } const newState = state.map((_, x) => { const cells = [ getCell(x - 1), getCell(x), getCell(x + 1)]; return rules[cells.join('')]; }); return newState.map(Number); } // create a 2D board from a 1D CA initial state function createBoard(state, rules, height) { function createBoardAcc(s, h, acc) { if (h === 0) return acc; const newState = evolve1d(s, rules); const newAcc = acc.concat([s]); return createBoardAcc(newState, h - 1, newAcc); } return createBoardAcc(state, height, []); } // Find the neighbor of a given cell in a 2D CA board function getCellNeighbors(board, cellCoordinates) { const [x, y] = cellCoordinates; const rowLength = board[0].length; // caca? // handles board edges where the cell is missing neighbors function getCell(xx, yy) { const safeX = guard(xx, board); const safeY = guard(yy, rowLength); return board[safeX][safeY]; } // the current cell is not included in the result return [ getCell(x - 1, y - 1), getCell(x, y - 1), getCell(x + 1, y - 1), getCell(x - 1, y), getCell(x + 1, y), getCell(x - 1, y + 1), getCell(x, y + 1), getCell(x + 1, y - 1), ]; } // Sums the value of a cell's neighbors function getNeighborsSum(cells) { return cells.reduce((cell, acc) => cell + acc, 0); } // Get the next evolution of a cell according to // Conway's game of life rules function conwayRules(cell, neighbors) { // loneliness rule if (cell === 1 && neighbors < 2) return 0; // overpopulation rule if (cell === 1 && neighbors > 3) return 0; // born when three live neighbors rule if (cell === 0 && neighbors === 3) return 1; // the cell remains the same if none apply return cell; } // get the next evolution of a 2D CA initial state function evolve2d(board, rulesFn) { return board.map((row, x) => row.map((cell, y) => { const neighbors = getCellNeighbors(board, [x, y]); const sum = getNeighborsSum(neighbors); return rulesFn(cell, sum); })); } function getDrawingValues(state, acc, cell) { const d = cell.dimension; return Object.keys(state).map( (key) => { const fillStyle = (() => { if (state[key] === '1') return cell.liveColor; return cell.deadColor; })(); return { move: [key * d, acc * d], fill: [key * d, acc * d, d, d], fillStyle, }; }, ); } // Populates the first state with a single living cell in the center function create1dStateOneCell(width) { return [...Array(width)].map( (cell, index) => { if (index === width / 2 || index === width + 1 / 2) return 1 return 0 }) } // Populates the first state of a 1D CA with cells returned // by initFn function create1dState(width, initFn, args) { return [...Array(width)].map( () => initFn(...args) ); } // Populates the first state of a 2D CA with cells returned // by initFn function create2dState(width, height, initFn, args) { return [...Array(height)].map( () => [...Array(width)].map( () => initFn(...args), ), ); } export { getDrawingValues, create1dState, create2dState, createBoard, create1dStateOneCell, conwayRules, evolve1d, evolve2d };