Glyphs

Inspired by notes I made while solving a puzzle.

Log in to post a comment.

// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(1);

let scale = 2.19;//  min=0.1 max=20 step=0.01
let columns = 8;// min = 1 max=100 step=1
let rows = 9;// min = 1 max=100 step=1
let spacing = 18; // min = 1 max=200 step=1
let lines = 1; // min=0, max=1, step=1, (Main, All)
let distPow = 1.9; // min = 0.5 max=5 step=0.01
let bend = 3.42;// min=0 max=10 step=0.01
let bendLimit = 3.93;// min=0 max=10 step=0.01
let lineDist = 2.5; // min=0 max=10 step=0.01

// Global code will be evaluated once.
const turtle = new Turtle();
const positions = [[0, 0], [1, 0], [2, 0], 
[0, 1], [1, 1], [2, 1],
[0, 2], [1, 2], [2, 2], 
[1, 3]
];

// 0 1 2
// 3 4 5
// 6 7 8
//   9
const groups = [
    [3, 0, 4, 6],
    [4, 1, 5, 7, 3],
    [5, 2, 4, 8],
    [7, 4, 8, 9, 6]
];

const NB = [
  [[1, 3], [4]],
  [[2,4,0],[5,3]],
  [[5,1],[4]],
  [[0,4,6],[1, 7]],
  [[1,5,7,3],[2,8,0,6]],
  [[2,4,8],[1,7]],
  [[3,7],[4,9]],
  [[4,8,9,6],[3,5]],
  [[5,7],[4,9]],
  [[7],[6,8]],
];

function i_to_dpos(i, p0)
{
    const p = positions[i];
    return [(p[0]-1) * scale + p0[0], (p[1]-1) * scale + p0[1]];
}

function edge_id(a, b) {
    return a < b ? 10 * a + b : 10 * b + a;
}

function draw_shape(shape)
{
    const p = turtle.pos();
    for (var i=0; i<10; i++) {
        if (shape & (1<<i)) {
            let p1 = i_to_dpos(i, p);
            
            const nbi = NB[i];
            let used = [];
            let hasEdge = false;
            for (const i2 of nbi[0])  {
                let p2 = i_to_dpos(i2, p);
                
                if (shape & (1 << i2)) {
                    let edgeID = edge_id(i, i2);
                    if (edgeID in used) {
                        continue;
                    }
                    used[edgeID] = true;
                    
                    turtle.jump(p1);
                    turtle.pendown();
                    turtle.goto(p2);
                    hasEdge = true;
                }
            }
            if (!hasEdge) {
                for (const i2 of nbi[1])  {
                    let p2 = i_to_dpos(i2, p);
                    
                    if (shape & (1 << i2)) {
                        let edgeID = edge_id(i, i2);
                        if (edgeID in used) {
                            continue;
                        }
                        used[edgeID] = true;
                        
                        turtle.jump(p1);
                        turtle.pendown();
                        turtle.goto(p2);
                        hasEdge = true;
                    }
                }   
            }
            if(!hasEdge) {
                let r = scale * 0.2;
                turtle.setheading(0);
                turtle.jump(p1[0], p1[1]-r);
                turtle.pendown();
                turtle.circle(r);
                //turtle.forward(scale * 0.3);
                //turtle.circle(scale * 0.2);
            }
        }
        if (lines > 0 && ((shape & (1<<i)) == 0)) {
            let p1 = i_to_dpos(i, p);
            
            const nbi = NB[i];
            let used = [];
            let hasEdge = false;
            for (const i2 of nbi[0])  {
                let p2 = i_to_dpos(i2, p);
                
                if ((shape & (1 << i2)) == 0) {
                    let edgeID = edge_id(i, i2);
                    if (edgeID in used) {
                        continue;
                    }
                    used[edgeID] = true;
                    
                    turtle.jump(p1);
                    turtle.pendown();
                    turtle.goto(p2);
                    hasEdge = true;
                }
            }
            
        }
    }
}



function next_states(state) {
    let result = [];
    for (const action of groups) {
        if (state & (1 << action[0])) {
            let s2 = state;
            let tmpBits=[];
            for (let i=1; i<action.length; i++) {
                if (state & (1 << action[i])) {
                    tmpBits.push(1);
                } else {
                    tmpBits.push(0);
                }
                s2 &= ~(1<<action[i]);
            }
            tmpBits.push(tmpBits.shift());
            for (let i=1; i<action.length; i++) {
                if (tmpBits[i-1] > 0) {
                    s2 |= (1<<action[i]);
                }
            }
            result.push(s2);
        }
        
    }
    return Array.from(new Set(result));
}

let visited = [];
let q = [0x77];
let rc = [0, 0];

let states = [];
let states2 = [];

function dist(a, b, order) {
    let i1 = order.indexOf(a);   
    let i2 = order.indexOf(b);
    let x1 = i1 % columns;
    let y1 = Math.floor(i1 / columns);
    let x2 = i2 % columns;
    let y2 = Math.floor(i2 / columns);
    let dx = Math.abs(x1-x2);
    let dy = Math.abs(y1-y2);
    //return dx+dy;
    //return dx*dx+dy*dy;
    let d= Math.sqrt(dx*dx+dy*dy)
    return Math.pow(d, distPow);
}

function dist_v(a, order) {
    let res = 0;
    
    //
    for (const n of states[a]) {
        //console.log(`sd ${a} ${n}`)
        res += dist(a, n, order);
    }
    for (const n of states2[a]) {
        //console.log(`sd ${a} ${n}`)
        res += dist(a, n, order);
    }
    if (a == 0x77) {
     //   console.log(`zzzzzzdd 0x77 ${res} ${order.indexOf(a)}`);
    }
    //console.log(`tmp ${res}`);
    return res;
}

function slerp(a, b, t) {
    return t*b + (1-t)*a;
}

function xydist(a, b) {
    let dx = a[0]-b[0];
    let dy = a[1]-b[1];
    return Math.sqrt(dx*dx+dy*dy);
}

// The walk function will be called until it returns false.
function walk(i) {
    let order = [];
    visited[0x77] = true;
    while (q.length > 0) {
        let s1 = q.shift();
        let neighbours = next_states(s1);
        states[s1] = neighbours;
        for (const state of neighbours){
            if (!(state in states2)) {
                states2[state]=[];
            }
            states2[state].push(s1);
            if (visited[state]) {
                continue;
            }
            visited[state] = true;
            q.push(state);
        }
        order.push(s1);
    }
    let fillers = -1;
    while (order.length < rows*columns) {
        order.push(fillers);
        states[fillers] = [];
        states2[fillers] = [];
        fillers -= 1;
    }
    /*for (let i = order.length - 1; i >= 0; i--) {
        let j = Math.floor(Math.random() * (i + 1));
        if (j >= i) {
            j = 0;
        }
        let temp = order[i];
        order[i] = order[j];
        order[j] = temp;
    }*/
    let dst =0;
    for (let i=0; i<order.length; i++) {
        //console.log(`wtf ${i} ${order[i]}`);
        dst += dist_v(order[i], order);
    }
    console.log("optimizing --------------")
    for (let x=0; x<15; x++) {
        //console.log(`>>>>>>>>> ${x}`);
        for (let i=0; i<order.length; i++) {
            for (let j=0; j<order.length; j++) {
                //console.log(`pos |${i} ${j}| v:${order[i]} ${order[j]}`);
                let d1 = dist_v(order[i], order) + dist_v(order[j], order);
                let temp = order[i];
                order[i] = order[j];
                order[j] = temp;
                let d2 = dist_v(order[i], order) + dist_v(order[j], order);
               // console.log(`d1: ${d1} d2:${d2}`);
                if (d2 >= d1) {
                    let temp = order[i];
                    order[i] = order[j];
                    order[j] = temp;
                } else {
                   // console.log(`tmpswpa ${d1}->${d2} ${i} ${j} ${order[i]} ${order[j]}`);
                }
            }
        }
        /*let dst2 =0;
        for (let i=0; i<order.length; i++) {
            dst2 += dist_v(order[i], order);
        }*/
        //console.log(`opt ${x} ${dst2}`);
    }
    console.log("AAAAAAAAAA optim")
    let index = 0;
    for (let r=0; r<rows; r++) {
        for (let c=0; c<columns; c++) {
            if (index >= order.length) {
                continue;
            }
            let p = [c * spacing - (columns-1)*spacing*0.5, 
                        r * spacing - (rows-1)*spacing*0.5];
            turtle.jump(p);
            if (order [index] > 0){
            draw_shape(order[index]);
            
            //console.log(`N: ${states[order[index]]}`);
            for (let j=0; j<order.length; j++) {
                if (states[order[index]].includes(order[j])) {
                    //console.log(`${order[index]} -> ${order[j]}`)
                    
                    if (index == j) {
                        continue;
                    }
                    let c2 = j % columns;
                    let r2 = Math.floor(j/ columns);
                    //console.log(`${order[j]} ${c2} ${r2}`);
                    
                    let p1 = p;
                    let p2 = [c2 * spacing - (columns-1)*spacing*0.5, 
                        r2 * spacing - (rows-1)*spacing*0.5];
                    
                    let dx = p2[0] -p1[0];
                    let dy = p2[1] -p1[1];
                    if (Math.abs(r-r2) ==1 && Math.abs(c-c2) == 1) {
                        if (p1[1] < p2[1]) {
                            p1[1] += 2 * scale;
                        } else {
                            p2[1] += 2 * scale;
                        }
                    }

                    turtle.jump(p);
                    for (let t=0; t<1; t += (1.0/128.0)) {
                        
                        let px =slerp(p1[0], p2[0], t);
                        let py =slerp(p1[1], p2[1], t);
                        let shift = bend*scale * (1 - 4 * ((t-0.5)*(t-0.5))) * Math.min(bendLimit, (((Math.abs(dx)+Math.abs(dy)) / spacing)-1));
                        if (Math.abs(dx) < Math.abs(dy)) {
                            px += shift;
                        } else {
                            py -= shift;
                        }
                        if (xydist(p, [px, py]) < scale*lineDist ||
                            xydist(p2, [px, py]) < scale*lineDist ) {
                            turtle.penup();       
                        } else {
                            turtle.pendown();
                        }
                        turtle.goto(px, py);
                    }
                    turtle.goto(p2);
                }
            }
                
            }
            index++;
        }
    }
    return false;   
}

/*function walk(i) {
    if (q.length <= 0) {
        return false;
    }
    if (rc[1] >= columns) {
        rc[1] = 0;
        rc[0] += 1;
    }
    if (rc[0] >= rows) {
        return false;
    }
    let s1 = q.shift();
    turtle.jump([rc[1] * spacing - (columns-1)*spacing*0.5, 
                 rc[0] * spacing - (rows-1)*spacing*0.5]);
    draw_shape(s1);
    let neighbours = next_states(s1);
    for (const state of neighbours){
        if (state in visited) {
            continue;
        }
        visited[state] = true;
        q.push(state);
    }
    rc[1] += 1;
    //turtle.jump(0, 0);
    //draw_shape(0x77);
    return true;
}*/