/** * Game of Life * @module lib/life.js; */ let w, h, a, b, notes, assign; function init(z, fn) { // really bad notes = z; assign = fn; build(); setTempo(50); } function build() { w = notes.length; h = notes[0].length; a = a || new Array(w); b = b || new Array(w); for (var i = 0; i < w; i++) { a[i] = a[i] || new Array(h); b[i] = b[i] || new Array(h); for (var j = 0; j < h; j++) { a[i][j] = b[i][j] = notes[i][j] && notes[i][j].playing ? 1 : 0; } } } let timeout, delay = 1200; // ~120 bpm function toggle() { build(); if (timeout) { clearTimeout(timeout); timeout = null; } else { step(); } } function assign_item(freq, state) { b[freq.i][freq.j] = state ? 1 : 0; } function setTempo(bpm) { console.log("bpm:", bpm); delay = 60000 / bpm; } function swap() { var tmp = a; a = b; b = tmp; } function step() { clearTimeout(timeout); timeout = setTimeout(step, delay); swap(); let i, j, ni, pi, nj, pj, score, state; for (i = 0; i < w; i++) { for (j = 0; j < h; j++) { ni = i === 0 ? w - 1 : i - 1; pi = i === w - 1 ? 0 : i + 1; nj = j === 0 ? h - 1 : j - 1; pj = j === h - 1 ? 0 : j + 1; score = a[ni][nj] + a[ni][j] + a[ni][pj] + a[i][nj] + a[i][pj] + a[pi][nj] + a[pi][j] + a[pi][pj]; state = fitness(a[i][j], score); b[i][j] = state; if (a[i][j] !== state) { assign(notes[i][j], state); } } } } function fitness(old, score) { if (old === 1) { if (score === 2 || score === 3) return 1; } else { if (score === 3) return 1; } return 0; } function isRunning() { return !!timeout; } export default { init, step, assign_item, toggle, setTempo, isRunning }; ///// grid functions const gliderShape = [ [0, 0, 0, 0, 0], [0, 1, 1, 1, 0], [0, 0, 0, 1, 0], [0, 0, 1, 0, 0], [0, 0, 0, 0, 0], ]; const gliderShapeFlip = gliderShape.map((a) => a.slice(0).reverse()); const gliderShapes = [ gliderShape, gliderShapeFlip, gliderShape.slice(0).reverse(), gliderShapeFlip.slice(0).reverse(), ]; function glider() { const x = Math.floor(Math.random() * ws); const y = Math.floor(Math.random() * hs); const shape = choice(gliderShapes); weave(x, y, shape); } function weave(x, y, shape) { const xmag = shape.length; const ymag = shape[0].length; let i, j, px, py; for (i = 0; i < xmag; i++) { for (j = 0; j < ymag; j++) { px = (x + i) % ws; py = (y + j) % hs; assign(notes[px][py], shape[i][j]); } } } function forEach(f) { let i, j, note, s; for (i = 0; i < ws; i++) { for (j = 0; j < hs; j++) { note = notes[i][j]; s = f(i, j, note.playing); assign(note, s); } } } function clone() { let i, j; let a = []; for (i = 0; i < ws; i++) { a[i] = []; for (j = 0; j < hs; j++) { a[i][j] = notes[i][j].playing; } } return a; } function move(dx, dy) { let a = clone(); forEach((x, y, state) => { x = (x + dx + ws) % ws; y = (y + dy + hs) % hs; return a[x][y]; }); } function clear() { forEach(() => { return false; }); } function stripex(odd) { odd = !!odd; forEach((x) => { return x % 2 ? odd : !odd; }); } function stripey(odd) { odd = !!odd; forEach((x, y) => { return y % 2 ? odd : !odd; }); } function checker(odd, n) { odd = !!odd; n = n || 1; forEach((x, y) => { return Math.floor(x / n) % 2 ^ Math.floor(y / n) % 2 ? odd : !odd; }); } function noise(n) { n = n || 0.5; n = n * n; forEach(() => { return Math.random() < n; }); } ///////////////////////// let life_bpm = 50; window.addEventListener("keydown", keydown, true); function keydown(e) { // console.log(e.keyCode) if (e.altKey || e.ctrlKey || e.metaKey) return; switch (e.keyCode) { case 32: // space life.toggle(); break; case 188: // comma life_bpm += e.shiftKey ? 1 : 5; life.setTempo(life_bpm); break; case 190: // period life_bpm -= e.shiftKey ? 1 : 5; life_bpm = Math.max(1, life_bpm); life.setTempo(life_bpm); break; case 37: // left move(1, 0); break; case 38: // up move(0, 1); break; case 39: // right move(-1, 0); break; case 40: // down move(0, -1); break; case 71: // g glider(); break; case 83: // s swap_instrument(); break; case 67: // c clear(); break; case 87: // w clear(); break; case 78: // n noise(0.5); break; case 69: // e stripex(Math.random() < 0.5); break; case 82: // r stripey(Math.random() < 0.5); break; case 84: // t checker(Math.random() < 0.5, 1); break; case 89: // y checker(Math.random() < 0.5, 2); break; case 85: // u checker(Math.random() < 0.5, 3); break; case 73: // i checker(Math.random() < 0.5, 4); break; case 79: // o checker(Math.random() < 0.5, 5); break; case 80: // p checker(Math.random() < 0.5, 6); break; case 219: // [ checker(Math.random() < 0.5, 7); break; case 221: // ] checker(Math.random() < 0.5, 11); break; case 49: // 1 noise(0.1); break; case 50: // 2 noise(0.2); break; case 51: // 3 noise(0.3); break; case 52: // 4 noise(0.4); break; case 53: // 5 noise(0.5); break; case 54: // 6 noise(0.6); break; case 55: // 7 noise(0.7); break; case 56: // 8 noise(0.8); break; case 57: // 9 noise(0.9); break; case 48: // 0 noise(1); break; } }