let drawing = 1; const form = Array.from(document.forms.rules.elements); const canvas = document.querySelector('#canvas'); const ctx = canvas.getContext('2d'); const main = document.querySelector('#main'); const dead = document.querySelector('#dead'); const live = document.querySelector('#live'); const startBtn = document.querySelector('#start'); const resetBtn = document.querySelector('#reset'); const cellSize = document.querySelector('#cellSize'); const loop = document.querySelector('#loop'); canvas.width = main.offsetWidth; canvas.height = main.offsetHeight; // TODO: Hide iterator inside function evolve(state, acc, rules) { const [x, y, z, ...xs] = state; if (!xs.length) { return acc[acc.length - 1] + acc + acc[0]; } const rule = x + y + z; const newAcc = acc.concat(rules[rule]); return evolve(y + z + xs.join(''), newAcc, rules); } function getRules() { const rules = {}; form.reduce((_, i) => { if (i !== undefined && i.type === 'checkbox') { if (i.checked) rules[i.name] = '1'; else rules[i.name] = '0'; } return rules; }, rules); return rules; } function getDrawingValues(state, acc) { const cellDim = cellSize.value; return Object.keys(state).map( (key) => { const fillStyle = state[key] === '1' ? live.value : dead.value; return { move: [key * cellDim, acc * cellDim], fill: [key * cellDim, acc * cellDim, cellDim, cellDim], fillStyle, }; }, ); } function getRandomInt(min, max) { const minVal = Math.ceil(min); const maxVal = Math.floor(max); // The maximum is exclusive and the minimum is inclusive return Math.floor(Math.random() * (maxVal - minVal) + minVal); } async function sleep(ms) { await new Promise((resolve) => setTimeout(resolve, ms)); } async function draw(state, acc) { if (drawing === 0) { return; } const position = acc * cellSize.value; if (position >= canvas.height && !loop.checked) return; const rules = getRules(); const newState = evolve(state, '', rules); const line = getDrawingValues(newState, acc); line.map((cell) => { ctx.moveTo(...cell.move); ctx.fillRect(...cell.fill); ctx.fillStyle = cell.fillStyle; return cell; }); await sleep(40); const newAcc = () => { if (position >= canvas.height && loop.checked) return 1; return acc; }; await draw(newState, newAcc() + 1); } function reset() { drawing = 0; ctx.clearRect(0, 0, canvas.width, canvas.height); } startBtn.addEventListener('click', async () => { reset(); await sleep(60); drawing = 1; const initialState = [...Array(canvas.width)].map( () => getRandomInt(0, 2).toString(), ).join(''); draw(initialState, 1); }); resetBtn.addEventListener('click', async () => { reset(); });