xxxxxxxxxx
251
const buf = [];
const lookBack = 4;
const chaosAfterCycles = 5;
let gol;
let period = 0;
let chaosBlink = 0;
function setup() {
createCanvas(430, 160);
frameRate(8);
buf.push(0);
buf.push(0);
buf.push(0);
buf.push(0);
gol = new GameOfLife();
let nStartSegs = 1 + Math.floor(random(2));
for (let i = 0; i < nStartSegs; ++i) {
while (true) {
let ix = Math.floor(random(28));
let dix = Math.floor(ix / 7);
let six = ix % 7;
if (getSeg(dix, six)) continue;
setSeg(dix, six);
break;
}
}
}
function draw() {
period++;
if (chaosBlink > 0) {
background("#f4f4f4");
clearBuf();
if ((chaosBlink % 2) == 0) {
buf[0] = 0b011010011;
buf[1] = 0b000111111;
buf[2] = 0b001110111;
buf[3] = 0b001101011;
}
drawBuf();
--chaosBlink;
if (chaosBlink > 0) return;
setChaosAnim(false);
period = period - (period % 4);
}
if (period % 4 != 0) return;
background("#f4f4f4");
// showNextNeighborhood();
// drawBuf();
drawBuf();
gol.step();
}
let preChaosBuf;
function setChaosAnim(active) {
if (active) {
preChaosBuf = [];
for (let i = 0; i < 4; ++i)
preChaosBuf.push(buf[i]);
chaosBlink = 12;
}
else {
for (let i = 0; i < 4; ++i)
buf[i] = preChaosBuf[i];
preChaosBuf = null;
}
}
let dix = 0, six = 0, show = false;
function showNextNeighborhood() {
clearBuf();
setSeg(dix, six);
if (!show) show = true;
else {
let neighbors = gol.getNeighbors(dix, six);
for (const n of neighbors) {
setSeg(n[0], n[1]);
}
++six;
if (six == 7) {
six = 0;
++dix;
}
if (dix == 4) dix = 0;
show = false;
}
drawBuf();
}
function clearBuf() {
for (let i = 0; i < 4; ++i) buf[i] = 0;
}
function setSeg(digitIx, segIx) {
let bit = 1 << segIx;
buf[digitIx] |= bit;
}
function clearSeg(digitIx, segIx) {
let bit = 1 << segIx;
bit = 255 - bit;
buf[digitIx] &= bit;
}
function getSeg(digitIx, segIx) {
let bit = 1 << segIx;
let val = buf[digitIx] & bit;
return val != 0;
}
function drawBuf() {
for (let i = 0; i < 4; ++i) {
for (let j = 0, bit = 1; j < 7; ++j, bit <<= 1) {
let val = buf[i] & bit;
drawSeg(i, j, val != 0);
}
}
}
function drawSeg(digitIx, segIx, isOn) {
const dark = "#505050",
light = "#a0a0a0";
const long1 = 36,
long2 = 46,
short = 8;
let x = 50 + digitIx * 2 * long2;
let y = 20;
stroke(light);
strokeWeight(1);
if (isOn) fill(dark);
else noFill();
if (segIx == 0) rect(x + short, y, long1, short);
else if (segIx == 1) rect(x, y + short, short, long2);
else if (segIx == 2) rect(x + short + long1, y + short, short, long2);
else if (segIx == 3) rect(x + short, y + short + long2, long1, short);
else if (segIx == 4) rect(x, y + 2 * short + long2, short, long2);
else if (segIx == 5) rect(x + short + long1, y + 2 * short + long2, short, long2);
else if (segIx == 6) rect(x + short, y + 2 * short + 2 * long2, long1, short);
}
function dmod(digitIx) {
if (digitIx < 0) digitIx += 4;
else if (digitIx >= 4) digitIx -= 4;
return digitIx;
}
class GameOfLife {
constructor() {
this.lastBufs = [];
this.inCycle = 0;
this.cycleLen = 0;
}
getNeighbors(digitIx, segIx) {
if (segIx == 0) return [[digitIx, 6], [digitIx, 1], [digitIx, 2]];
else if (segIx == 1) return [[dmod(digitIx-1), 2], [digitIx, 0], [digitIx, 3], [digitIx, 4]];
else if (segIx == 2) return [[digitIx, 0], [digitIx, 3], [digitIx, 5], [dmod(digitIx+1), 1]];
else if (segIx == 3) return [[digitIx, 1], [digitIx, 2], [digitIx, 4], [digitIx, 5]];
else if (segIx == 4) return [[dmod(digitIx-1), 5], [digitIx, 1], [digitIx, 3], [digitIx, 6]];
else if (segIx == 5) return [[digitIx, 3], [digitIx, 2], [digitIx, 6], [dmod(digitIx+1), 4]];
else if (segIx == 6) return [[digitIx, 4], [digitIx, 5], [digitIx, 0]];
}
step() {
if (this.cycleLen > 0 && this.inCycle / this.cycleLen > chaosAfterCycles) {
if (this.cycleLen == 2) this.chaos(); // If cycle is short, double chaos
this.chaos();
}
let nbuf = [];
for (let i = 0; i < 4; ++i) buf.push(0);
for (let i = 0; i < 4; ++i) {
for (let j = 0; j < 7; ++j) {
let alive = getSeg(i, j);
let neighbors = this.getNeighbors(i, j);
let aliveNeighbors = 0;
for (const [dix, six] of neighbors) {
if (getSeg(dix, six)) ++aliveNeighbors;
}
let nalive;
// Rules of the game!
// 1 1 2: oscillates quickly
// 2 1 2: long irregular games
// 2 1 3: oscillates quickly
// 2 1 4: long games, interesting oscillations <=== LIKE
if (alive) nalive = aliveNeighbors == 2;
else {
if (aliveNeighbors < 1) nalive = false;
else if (aliveNeighbors > 4) nalive = false;
else nalive = true;
}
if (nalive) nbuf[i] |= (1 << j);
}
}
this.lastBufs.push([]);
for (let i = 0; i < 4; ++i) {
this.lastBufs[this.lastBufs.length-1].push(buf[i]);
buf[i] = nbuf[i];
}
if (this.lastBufs.length > lookBack) this.lastBufs.shift();
this.cycleLen = this.getCycleLen();
if (this.cycleLen > 0) ++this.inCycle;
}
getCycleLen() {
for (let i = 0; i < this.lastBufs.length; ++i) {
if (isSame(buf, this.lastBufs[this.lastBufs.length - 1 - i]))
return i + 1;
}
return 0;
function isSame(buf1, buf2) {
for (let i = 0; i < 4; ++i)
if (buf1[i] != buf2[i]) return false;
return true;
}
}
chaos() {
this.cycleLen = 0;
this.inCycle = 0;
let dix = Math.floor(random(4));
let six = Math.floor(random(7));
let val = getSeg(dix, six);
if (val) {
clearSeg(dix, six);
}
else {
setSeg(dix, six);
}
setChaosAnim(true);
}
}