const canvas = document.getElementById('canvas'); canvas.width = window.innerWidth - 50; canvas.height = window.innerHeight - 50; const canvwidth = canvas.width, canvheight = canvas.height, ctx = canvas.getContext('2d'); function randIndivC(a = 0, b = 255) { return Math.floor(Math.random() * (b-a) + a); } function cts(c) { const r = c.r < 16 ? '0' + c.r.toString(16) : c.r.toString(16), g = c.g < 16 ? '0' + c.g.toString(16) : c.g.toString(16), b = c.b < 16 ? '0' + c.b.toString(16) : c.b.toString(16); return '#' + r + g + b; } function mixedColor(base) { let randr = Math.floor(Math.random() * 200 + 55), randg = Math.floor(Math.random() * 200 + 55), randb = Math.floor(Math.random() * 200 + 55); if( base ) { randr = Math.floor((base.r + randr) / 2); randg = Math.floor((base.g + randg) / 2); randb = Math.floor((base.b + randb) / 2); } return cts({r:randr, g:randg, b:randb}); } function primaryOrSecondary() { const t = Math.random(), w = Math.floor(Math.random() * 3); if( t < 0.45 ) { const r = w === 0 ? 255 : 0, g = w === 1 ? 255 : 0, b = w === 2 ? 255 : 0; return {r, g, b}; } else if( t < 0.9 ) { const r = w !== 0 ? 255 : 0, g = w !== 1 ? 255 : 0, b = w !== 2 ? 255 : 0; return {r, g, b}; } else { const r = randIndivC(100, 255), g = randIndivC(100, 255), b = randIndivC(100, 255); return {r, g, b}; } } function spot(x, y, colour) { ctx.save(); ctx.fillStyle = colour; ctx.fillRect(x, y, 10, 10); ctx.restore(); } function generateInitialRow(lx = canvwidth / 10 + 1) { const row = []; for( let i = 0; i < lx; i++ ) { row.push( Math.random() < 0.5 ? false : true ); } return row; } const rules = { "111": false, "110": true, "101": true, "100": false, "011": true, "010": true, "001": true, "000": false, }; function applyRules(first, second, third) { return rules[ [+first, +second, +third].join('') ]; } function addRow(field) { if( !field.length ) { return field; } if( field[0].length < 3 ) { return field; } const last = field[ field.length - 1 ], newRow = last.map(function iter(val, ind) { const first = last[ind - 1] || 0, second = val, third = last[ind + 1] || 0; return applyRules(first, second, third); }); field.push(newRow); return field; } function drawRow(row, y) { row.forEach(function spotter(val, ind) { if( val ) { const c = mixedColor(primaryOrSecondary()); spot(ind * 10, y, c); } }); } function step(field) { requestAnimationFrame(descend.bind(null, field)); } function descend(field) { const n = field.length - 1; drawRow(field[n], n * 10); field = addRow(field); if( n * 10 < canvheight ) { setTimeout(step, 100, field); } else {//TODO: remove this clause after tests console.log('STOP!'); } } step([ generateInitialRow() ]);