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.
// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(0.2);
const turtle = new Turtle();
turtle.penup();
//note: can't exceed 32 due to integer bits.
//note: 16 seems the minimum size for the patterns.
const BLOCK_COUNT = 16;
const BLOCK_SIZE = 200 / BLOCK_COUNT;
const BLOCK_HALF_SIZE = BLOCK_SIZE / 2;
const MIDDLE_POINT = BLOCK_COUNT / 2;
//note: has major impact on the performance.
const LINE_SPACING = 0.5;
const matrix = new Array(BLOCK_COUNT).fill(0);
const buffer = new Array(BLOCK_COUNT).fill(0);
// Initialize a random pattern in the middle.
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!");
}
// The walk function will be called until it returns false.
function walk(i) {
// Render frame.
render();
// Calculate next generation.
next_generation();
// Should be enough. :)
return i < 50;
}
function render() {
for(x = 0; x < BLOCK_COUNT; x++) {
for(y = 0; y < BLOCK_COUNT; y++) {
if(isset(x, y)) {
draw(x, y);
}
}
}
}
function draw(x, y) {
let nx = -100 - BLOCK_HALF_SIZE + (x * BLOCK_SIZE);
let ny = -100 + BLOCK_HALF_SIZE + (y * BLOCK_SIZE);
for(cy = ny; cy < ny + BLOCK_SIZE; cy += LINE_SPACING) {
turtle.goto(nx, cy);
turtle.pendown();
turtle.goto(nx + BLOCK_SIZE, cy);
turtle.penup();
}
}
function next_generation() {
// Write grid to buffer.
for (i = 0; i < BLOCK_COUNT; i++) {
buffer[i] = matrix[i];
}
// Write new generation to buffer.
for (x = 0; x < BLOCK_COUNT; x++) {
for (y = 0; y < BLOCK_COUNT; y++) {
if (should_toggle_state(x, y)) {
buffer[y] ^= (1 << x);
}
}
}
// Write buffer to grid.
for (i = 0; i < BLOCK_COUNT; i++) {
matrix[i] = buffer[i];
}
}
function should_toggle_state(x, y) {
let isAlive = (matrix[y] & (1 << x)) == (1 << x);
let numberOfNeighbors = get_number_of_neighbors(x, y, isAlive);
if (isAlive && (numberOfNeighbors == 2 || numberOfNeighbors == 3)) {
return false;
}
if (!isAlive && numberOfNeighbors == 3) {
return true;
}
return isAlive;
}
function get_number_of_neighbors(x, y, isAlive) {
let sum = 0;
for(cy = y - 1; cy <= y + 1; cy++) {
if (cy < 0 || cy >= BLOCK_COUNT) {
continue;
}
let v = matrix[cy] >> (x - 1) & 0x7;
v = v - ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
sum += (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
}
return isAlive ? sum - 1 : sum;
}
function set(x, y, value) {
if(value) {
matrix[y] |= (1 << x);
} else {
matrix[y] &= ~(1 << x);
}
}
function isset(x, y) {
return (matrix[y] & (1 << x)) == (1 << x);
}
/*
Load pattern from RLE format.
b = Dead
o = Alive
$ = Newline
*/
function load_pattern(startx, starty, rle) {
let x = startx, y = starty;
let pattern = rle.replace(/(\d+)(\w)/g,
function(m,n,c){
return new Array(parseInt(n,10)+1).join(c);
}
);
for(ci = 0; ci < pattern.length; ci++) {
switch(pattern[ci]) {
//case "b":
// set(x, y, false);
// break;
case "o":
set(x, y, true);
break;
case "$":
x = startx;
y++;
continue;
}
x++;
}
}