Il Cubo - turtle toy 🎲

Writing "turtle toy"

Hit compile&run for different randomized outcome

Forked from Il Cubo

Log in to post a comment.

// Il Cubo. Created by Reinder Nijhoff 2019
// @reindernijhoff
//
// https://turtletoy.net/turtle/cc4d10a96c
//
// Reimplementation of Edward Zajec’s “Il Cubo” from 1971. 
// Based on the images from http://www.edwardzajec.com/tvc4/pil1/index.html (I don't speak Italian).
//
// Different at every Compile & Run.
//

const buffer_steps = 1 + Math.random()*2|0;
const turtle = new Turtle();
const drawFrame = Math.random() > .5;

const WHITE = 0b10110;
const OPEN = 0;

let buffer = [];

let mask = [
    '                       ',
    'ttt u u rrr ttt l   eee ',
    ' t  u u r    t  l   e  ',
    ' t  u u r    t  l   eex ',
    ' t  u u r    t  l   e  ',
    ' t  uuu r    t  lll eee ',
    '                       ',
    'xx   ttt  ooo  y y   xx',
    'xxxx  t   o o  y y xxxx',
    'xxxx  t   o o  yyy xxxx',
    'xxxx  t   o o   y  xxxx',
    'xxxx  t   ooo   y  xxxx',
    'xxxxx             xxxxx',
];

let buffer_size = mask[0].length;
// expand borders
for (let y = 0; y < (Math.random() * 20) | 0; y++) {
  if (Math.random() > 0.5) {
    buffer_size += 2;
    for (let x = 0; x < mask.length; x++) {
      mask[x] = "x" + mask[x] + "x";
    }
  }
}
const cell_size = 170 / buffer_size;

// fill unknown rows
while (mask.length < buffer_size) {
  let row = "";
  for (let x = 0; x < buffer_size; x++) row += "x";
  if (mask.length % 2 === 0) {
    mask.push(row);
  } else {
    mask.unshift(row);
  }
}

// reverse because everything is draw "op z'n kop"
mask.reverse();


String.prototype.replaceAt = function(index, replacement) {
    return this.substr(0, index) + replacement + this.substr(index + replacement.length);
}
// replace x in mask with random. random is based on distance from center
mask = mask.map((s,i) => {
    var r = s;
    while (true) {
        var idx = r.indexOf("x");
        if (idx >= 0) {
            var dx = idx - buffer_size/2 + 1;
            var dy = i - buffer_size/2+1;
            var d = Math.sqrt(dx*dx + dy*dy);
            var max = buffer_size;
            var t = d/max;
            r = r.replaceAt(idx, (t >= Math.random() *.75 ? " " : "y"));
        } else {
            break;
        }
    }
    return r;
})

function init_buffer() {
    for (let x=0; x<buffer_size; x++) {
        buffer[x] = [];
        for (let y=0; y<buffer_size; y++) {
            if (mask[y] && mask[y].charAt(x) != " ") {
                buffer[x][y] = WHITE;
            } else {
                buffer[x][y] = OPEN;
            }
        }
    }
}

function half_tile(type, x, y, lower) { 
    const s = type == 1 ? 8 : type == 2 ? 32 : 2;
    let xs, xe, ys, ye;
    
    for (let i=0; i<=s; i++) {
        if (type == 1) {  
            ys = ye = xe = i/s; xs = 0;
        } else if (type == 2) {
            ys = xs = xe = i/s; ye = 1;
        } else {
            ys = i/s; xe = 1-i/s; ye = 1; xs = 0;
        }
        if (lower) {
            draw_line(1-xs, ys, 1-xe, ye, x, y);
        } else {
            draw_line(xs, 1-ys, xe, 1-ye, x, y);
        }
    }
}

function tile(type, x, y) {
    if (!(type & 0b10000)) {
        const t1 = type >> 2, t2 = type & 0b11;
        if (t1 == 0b10) { // try to reuse as much turtles as possible
            half_tile(t2, x, y, true);
            half_tile(t1, x, y, false);
        } else {
            half_tile(t1, x, y, false);
            half_tile(t2, x, y, true);
        }
        if (t1 != t2) draw_line(0,1,1,0,x,y);
    } 
}

function buffer_step(buffer) {
    const nb = [], rb = (buffer, x, y) =>x >= 0 && y >= 0 ? buffer[x][y] : 0;
    for (let x=0; x<buffer_size; x++) {
        nb[x] = [];
        for (let y=0; y<buffer_size; y++) {
            nb[x][y] = buffer[x][y];
            if (!(nb[x][y] & 0b10000)) {
                nb[x][y] |= (rb(buffer,x-1,y) & 0b0011) << 2;
                nb[x][y] |= (rb(buffer,x,y-1) & 0b1100) >> 2;
            }
        }
    }
    return nb;
}

function draw_border(l,t,r,b,x,y) {
    l && draw_line(0,0,0,1,x,y);
    t && draw_line(0,1,1,1,x,y);
    r && draw_line(1,0,1,1,x,y);
    b && draw_line(0,0,1,0,x,y);
}

// line draw functions
const hashMap = {}, turtleMap = {};

function draw_line(xs, ys, xe, ye, x, y) {
    xs = (x+xs-.5)*cell_size, ys = (y+ys-.5)*cell_size;
    xe = (x+xe-.5)*cell_size, ye = (y+ye-.5)*cell_size;
    const ks = xs.toFixed(4)+ys.toFixed(4), ke = xe.toFixed(4)+ye.toFixed(4);
    
    // Don't draw the same line twice, and try to reuse as much turtles as possible
    if (ks != ke && !hashMap[ks+ke] && !hashMap[ke+ks]) {        
        hashMap[ks+ke] = hashMap[ke+ks] = true;
        if (turtleMap[ks]) {
            turtleMap[ks].goto(xe,ye);
            turtleMap[ke] = turtleMap[ks]; turtleMap[ks] = false;
        } else if (turtleMap[ke]) {
            turtleMap[ke].goto(xs,ys);
            turtleMap[ks] = turtleMap[ke]; turtleMap[ke] = false;
        } else {
            turtleMap[ke] = new Turtle(xs,ys);
            turtleMap[ke].goto(xe,ye);
        }
    }
}

function walk(i) {
    if (i==0) {
        init_buffer();
        for (let j=0; j<buffer_steps*2; j++) {
            buffer = buffer_step(buffer);
        }
    }
    const x = i % buffer_size, y = (i / buffer_size)|0;
    const xc = x-buffer_size/2 + .5, yc = buffer_size/2-y - .5;
    
    tile( buffer[x][y], xc, yc);
    
    if (buffer[x][y] & 0b10000) { // border for white squares
        draw_border(x>0 && !(buffer[x-1][y]&0b10000), y>0 && !(buffer[x][y-1]&0b10000),
        x<buffer_size-2 && !(buffer[x+1][y]&0b10000), y<buffer_size-2 && !(buffer[x][y+1]&0b10000),xc,yc);
    } 
    if (!(buffer[x][y] & 0b10000) || drawFrame) {
        draw_border(x==0, y==0, x==buffer_size-1, y==buffer_size-1, xc, yc);
    }
    return i < buffer_size*buffer_size-1;
}