Conway's Life Question (Solution)
by Nick Iaconis
HTML
<div id="root">
<div>
<label>
<span>Grid size:</span>
<input id="size" name="size" type="number" min=2 />
</label>
<label>
<span>Init Life state:</span>
<input id="init" name="init" placeholder="2 dimension JSON array" />
</label>
<button id="generate">
Reset Grid
</button>
</div>
<div>
<input id="speed" type="range" min=32 max=512 step=8 value=128 />
<button id="simulate">
Start
</button>
</div>
<div class="grid">
</div>
</div>
CSS
.grid {
margin-top: .5em;
width: 100%;
padding-top: 100%;
display: flex;
flex-direction: column;
background-color: #eee;
}
.grid > :first-child {
margin-top: -100%;
}
.grid > * {
flex-grow: 1;
width: 100%;
display: flex;
}
.grid > * > * {
display: block;
flex-grow: 1;
}
.grid > :nth-child(odd) > :nth-child(even),
.grid > :nth-child(even) > :nth-child(odd) {
background-color: #ddd;
}
.grid > :nth-child(n) > .alive {
background-color: black;
}
JavaScript
let hasValidGrid = false;
let simulating = false;
let lastTick = 0;
let lifeState;
const root = document.getElementById('root');
const size = document.getElementById('size');
const init = document.getElementById('init');
const speed = document.getElementById('speed');
const simulate = document.getElementById('simulate');
root.addEventListener('click', event => {
switch (event.target.id) {
case "generate": {
// check grid size validity
const dimension = parseInt(size.value, 10);
if (isNaN(dimension) || dimension < 2) {
size.setCustomValidity('must be positive & ≥ 2');
size.reportValidity();
return;
}
size.setCustomValidity('');
// check Life init validity & populate initial state
let lifeInit = [];
if (init.value.length) {
try {
lifeInit = JSON.parse(init.value);
} catch (e) {
init.setCustomValidity(e.message);
init.reportValidity();
}
}
init.setCustomValidity('');
simulating = false;
updateSimulateButton();
// clear grid & life state
const oldGrid = root.getElementsByClassName('grid')[0];
const grid = oldGrid.cloneNode(false);
oldGrid.parentNode.replaceChild(grid, oldGrid);
lifeState = [];
// populate DOM elements & life state
for (let i = 0; i < dimension; i++) {
const row = document.createElement('div');
const lifeRow = [];
lifeState.push(lifeRow);
for (let j = 0; j < dimension; j++) {
const cell = document.createElement('div');
cell.setAttribute('data-row', i);
cell.setAttribute('data-cell', j);
row.appendChild(cell);
lifeRow.push(queryLife(lifeInit, i, j));
}
grid.appendChild(row);
}
hasValidGrid = true;
applyState(lifeState);
return;
}
case "simulate": {
if (!hasValidGrid) {
size.setCustomValidity('a grid is required for...