Stain of Life
Using the Game of Life rules, create a heatmap based on the patterns present.
Added basic support for RLE notation (left out the headers): conwaylife.com/wiki/run_length_encoded
Log in to post a comment.
Canvas.setpenopacity(0.2);
const turtle = new Turtle();
turtle.penup();
const BLOCK_COUNT = 16;
const BLOCK_SIZE = 200 / BLOCK_COUNT;
const BLOCK_HALF_SIZE = BLOCK_SIZE / 2;
const MIDDLE_POINT = BLOCK_COUNT / 2;
const LINE_SPACING = 0.5;
const GRID_LEFT = -100 - BLOCK_HALF_SIZE;
const GRID_TOP = -100 + BLOCK_HALF_SIZE;
const POP3 = [0, 1, 1, 2, 1, 2, 2, 3];
let matrix = new Array(BLOCK_COUNT).fill(0);
let buffer = new Array(BLOCK_COUNT).fill(0);
if (Math.random() < 0.5) {
load_pattern(MIDDLE_POINT - 1, MIDDLE_POINT - 2, "bob$3o$obo$bob!");
} else {
load_pattern(MIDDLE_POINT - 2, MIDDLE_POINT - 3, "obobo$o3bo$o3bo$o3bo$obobo!");
}
function walk(i) {
render();
next_generation();
return i < 50;
}
function render() {
turtle.pendown();
for (let y = 0; y < BLOCK_COUNT; y++) {
const rowBits = matrix[y];
if (rowBits === 0) {
continue;
}
const baseY = GRID_TOP + (y * BLOCK_SIZE);
for (let cy = baseY; cy < baseY + BLOCK_SIZE; cy += LINE_SPACING) {
let bits = rowBits;
let x = 0;
while (bits !== 0 && x < BLOCK_COUNT) {
while ((bits & 1) === 0 && x < BLOCK_COUNT) {
bits >>>= 1;
x++;
}
if (x >= BLOCK_COUNT || bits === 0) {
break;
}
const start = x;
while ((bits & 1) === 1 && x < BLOCK_COUNT) {
bits >>>= 1;
x++;
}
turtle.jump(GRID_LEFT + (start * BLOCK_SIZE), cy);
turtle.goto(GRID_LEFT + (x * BLOCK_SIZE), cy);
}
}
}
turtle.penup();
}
function next_generation() {
for (let y = 0; y < BLOCK_COUNT; y++) {
const top = y > 0 ? matrix[y - 1] : 0;
const mid = matrix[y];
const bottom = y + 1 < BLOCK_COUNT ? matrix[y + 1] : 0;
let nextRow = 0;
for (let x = 0; x < BLOCK_COUNT; x++) {
const alive = (mid >>> x) & 1;
const mask = x === 0 ? 0x3 : 0x7;
const topSlice = (top >>> (x === 0 ? 0 : x - 1)) & mask;
const midSlice = (mid >>> (x === 0 ? 0 : x - 1)) & mask;
const bottomSlice = (bottom >>> (x === 0 ? 0 : x - 1)) & mask;
const neighbors = POP3[topSlice] + POP3[midSlice] + POP3[bottomSlice] - alive;
if (neighbors === 3 || (alive === 1 && neighbors === 2)) {
nextRow |= (1 << x);
}
}
buffer[y] = nextRow;
}
const tmp = matrix;
matrix = buffer;
buffer = tmp;
}
function set(x, y, value) {
if (value) {
matrix[y] |= (1 << x);
} else {
matrix[y] &= ~(1 << x);
}
}
function load_pattern(startx, starty, rle) {
let x = startx;
let y = starty;
let runLen = 0;
for (let i = 0; i < rle.length; i++) {
const ch = rle[i];
if (ch >= "0" && ch <= "9") {
runLen = (runLen * 10) + (ch.charCodeAt(0) - 48);
continue;
}
const count = runLen === 0 ? 1 : runLen;
runLen = 0;
if (ch === "o") {
for (let j = 0; j < count; j++) {
set(x + j, y, true);
}
x += count;
continue;
}
if (ch === "b") {
x += count;
continue;
}
if (ch === "$") {
y += count;
x = startx;
continue;
}
if (ch === "!") {
break;
}
}
}