### Stain of Life

Using the Game of Life rules, create a heatmap based on the patterns present.

```// 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);
}

/*
o = Alive
\$ = Newline
*/
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++;
}
}```