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; }