Poor turtle's implementation: two steps forward, one step back.
Log in to post a comment.
Canvas.setpenopacity(1); const DEBUG = false; const square = (steps) => (step) => Math.floor(4*step/steps) === 4*step/steps ? 90 : 0; const pentagon = (steps) => (step) => Math.floor(5*step/steps) === 5*step/steps ? 72 : 0; const star = (steps) => (step) => Math.floor(10*step/steps) === 10*step/steps ? Math.floor(5*step/steps) === 5*step/steps ? -72 : 144 : 0; const rod = (steps) => (step) => { const d = step/steps; if (d <= 0.1 || (d > 0.5 && d <= 0.6)) return -0.5; return 0; } const mysteryObject = (step) => 0.04 +0.04*Math.sin(0.3*Math.PI*step/1800+0.75*Math.PI)*Math.sin(0.3*Math.PI*step/7200+0.75*Math.PI) -0.04*Math.cos(0.15*Math.PI*step/7200+0.5*Math.PI)*Math.cos(0.15*Math.PI*step/7200+0.5*Math.PI); let originalObjs = [ { x: 15, y: -25, h: 0, dir: "right", angleFn: star(3600), stepsize: 0.08, steps: 3600, }, { x: 75, y: -0, h: 0, dir: "right", angleFn: pentagon(3600), stepsize: 0.04, steps: 3600, }, { x: 7, y: 5, h: 90, dir: "left", angle: 0.1, stepsize: 0.05, steps: 3600, }, { x: 80, y: 60, h: 25, dir: "right", angleFn: rod(3600), stepsize: 0.08, steps: 3600, }, { x: -25, y: 40, h: 180, dir: "right", angle: 0.1, stepsize: 0.05, steps: 3600, }, { x: -10, y: 80, h: 4, dir: "left", angleFn: square(3600), stepsize: 0.07, steps: 3600, }, { x: 42, y: 46, h: 262, dir: "right", angleFn: square(3600), stepsize: 0.05, steps: 3600, }, { x: 45, y: -85, h: 0, dir: "right", angle: 0.1, stepsize: 0.05, steps: 3600, }, { x: -55, y: -25, h: 0, dir: "right", angle: 0.1, stepsize: 0.0666, steps: 3600, }, { x: 3, y: -45, h: 80, dir: "left", angleFn: mysteryObject, stepsize: 0.01, steps: 21240, }, { x: 78, y: 37, h: 72, dir: "right", angleFn: rod(3600), stepsize: 0.09, steps: 3600, }, { x: 55, y: 60, h: 60, dir: "right", angleFn: pentagon(3600), stepsize: 0.05, steps: 3600, }, { x: 7, y: 90, h: -6, dir: "left", angleFn: square(3600), stepsize: 0.05, steps: 3600, }, { x: -28, y: -65, h: 315, dir: "right", angleFn: square(3600), stepsize: 0.06, steps: 3600, }, { x: -50, y: -50, h: 180, dir: "right", angleFn: star(3600), stepsize: 0.08, steps: 3600, }, ].map((val, key) => ({ id: key, ...val })); const objs = [...originalObjs]; for (let i = 0; i < originalObjs.length; i++) { const selected = Math.floor(Math.random()*(objs.length - i)); const elem = objs.splice(selected, 1); objs.push(...elem); } const innerLatchCooldown = 15; const T_ACCURACY = 0.01; const hitSize = 2; // size of X markers const round = (coord) => (Math.round(1/T_ACCURACY*(coord))*T_ACCURACY).toFixed(1); const toT = (xcoord, ycoord) => `${round(xcoord)}:${round(ycoord)}`; let obj = objs[0]; let objStep = 0; let T = {}; let currentT = {}; let latches = {}; Turtle.prototype.tryDraw = function (obj, objStep) { this.up(); const _x = this.x(); const _y = this.y(); this.executeObjStep(obj, objStep); const t = toT(this.x(), this.y()); // Tell the current T that this obj inhabits these coords. currentT[t] = [ ...(T[t] || []), obj.id ]; for (const [tLatchId, cooldown] of Object.entries(latches)) { // Skip own references. if (tLatchId === obj.id) continue; // undefined/null signifies that the latch exist but has cooled down, so can be toggled. if (cooldown != null) { if (cooldown > 0) { DEBUG && console.log(`PRE: Obj ${obj.id} Step ${objStep} at ${t}: Cooldown continues for removal of latch ${tLatchId}`) latches[tLatchId] = cooldown === 1 ? undefined : cooldown - 1; } else if (cooldown < 0) { DEBUG && console.log(`PRE: Obj ${obj.id} Step ${objStep} at ${t}: Cooldown continues for addition of latch ${tLatchId}`) latches[tLatchId] = cooldown === -1 ? null : cooldown + 1; } else if (cooldown === 0) { DEBUG && console.log(`PRE: Obj ${obj.id} Step ${objStep} at ${t}: Latch ${tLatchId} is cooled down`) latches[tLatchId] = null; } else { DEBUG && console.error(`Whoops!`) } } } // Did we hit another object? if (T[t]) { // For each existing object at the coords... for (const tLatchId of T[t] || []) { // Skip own references. if (tLatchId === obj.id) continue; // If the object is already latched, remove the latch. const cooldown = latches[tLatchId]; // undefined/null signifies that the latch exist but has cooled down, so can be toggled. if (cooldown === null) { DEBUG && console.log(`Obj ${obj.id} Step ${objStep} at ${t}: Removing latch for ${tLatchId}`) latches[tLatchId] = innerLatchCooldown; if (DEBUG && hitSize) { const aux = new Turtle(); aux.jump(_x, _y); aux.down(); aux.seth(90+22.5*objStep%3); aux.forward(hitSize); aux.backward(2*hitSize); aux.forward(hitSize); aux.seth(0+22.5*objStep%3); aux.forward(hitSize); aux.backward(2*hitSize); } } // Otherwise, add it. else if (typeof cooldown === "undefined") { DEBUG && console.log(`Obj ${obj.id} Step ${objStep} at ${t}: Adding latch for ${tLatchId}`) latches[tLatchId] = -innerLatchCooldown; if (DEBUG && hitSize) { const aux = new Turtle(); aux.jump(_x, _y); aux.down(); aux.seth(135+22.5*objStep%3); aux.forward(hitSize); aux.backward(2*hitSize); aux.forward(hitSize); aux.seth(45+22.5*objStep%3); aux.forward(hitSize); aux.backward(2*hitSize); } } } return false; } let stillLatched = Object.entries(latches) .filter(([key, val]) => val !== undefined && !(val > 0)) .length > 0; if (stillLatched) { DEBUG && console.log(`Still latched... ${Object.entries(latches).map(e => e.map(s => ''+s).join(':'))}`) return false; } this.executeObjStep(obj, objStep, true); this.down(); this.executeObjStep(obj, objStep); } Turtle.prototype.executeObjStep = function (obj, objStep, reverse = false) { const rev = obj.dir === "left" ? "right" : "left"; !reverse && obj.angle != null && this[obj.dir](obj.angle); !reverse && obj.angleFn != null && this[obj.dir](obj.angleFn(objStep)); obj.stepsize && this[reverse ? 'backward' : 'forward'](obj.stepsize); obj.stepsizeFn && this[reverse ? 'backward' : 'forward'](obj.stepsizeFn(objStep)); reverse && obj.angle != null && this[rev](obj.angle); reverse && obj.angleFn != null && this[rev](obj.angleFn(objStep)); } const turtle = new Turtle(); const findLatchesInDirection = (_keys, right = false) => { const keys = right ? _keys.sort() : _keys.sort().reverse(); let met = {}; for (const key of keys) { const ids = T[key]; for (const id of ids) { // Add latch if (met[id] == null) { met[id] = innerLatchCooldown; } // Is it, like, really hot? else if (met[id] > innerLatchCooldown - 1) { // Keep it hot. met[id] = innerLatchCooldown; } else if (met[id] === 0) { // Remove latch if cooled down and met again. met[id] = undefined; } } // Cool down all the met objects. for (const [key, cooldown] in Object.entries(met)) { if (cooldown > 1) met[key] = cooldown - 1; } } return Object.entries(met).filter(([key, cooldown]) => cooldown!==undefined).map(([key]) => key); } const findLatches = ({ id, x, y }, step) => { const Tkeys = Object.keys(T); if (Tkeys.length === 0) { DEBUG && console.log(`Obj ${id} Step ${step}: Skip latching for first drawn object`) return {}; } const xkeys = Tkeys.filter(k => { return parseFloat(k.split(':')[1]) == y; }); const lkeys = xkeys?.filter(xk => xk.split(':')[0] < x); const rkeys = xkeys?.filter(xk => xk.split(':')[0] > x); let leftLatches = [], rightLatches = []; if (lkeys.length > 0) { DEBUG && console.log(`Obj ${id} Step ${step}: Looking left from ${x}:${y}...`) leftLatches = findLatchesInDirection(lkeys); } if (rkeys.length > 0) { DEBUG && console.log(`Obj ${id} Step ${step}: Looking right from ${x}:${y}...`) rightLatches = findLatchesInDirection(rkeys, true); } DEBUG && console.log("Left latches: ", leftLatches) DEBUG && console.log("Right latches: ", rightLatches) let finalLatches = {}; for (const latch of [...leftLatches, ...rightLatches]) { // Both sides latched -> definitely inside this object if (leftLatches.includes(latch) && rightLatches.includes(latch)) { finalLatches[latch] = null; } // Otherwise we're not really inside that object... } return finalLatches; } function walk(step) { if (objStep === 0) { turtle.jump(obj.x, obj.y); obj.h!=null && turtle.seth(obj.h); obj.hFn!=null && turtle.seth(obj.hFn(objStep)); latches = findLatches(obj, step); } if (objStep < obj.steps) { turtle.tryDraw(obj, objStep); objStep++; } else { objStep = 0; objs.shift(); obj = objs.length ? objs[0] : null; let newT = {...T}; for (const key of Object.keys(currentT)) { const nonunique = [...(T[key] || []), ...currentT[key]]; const unique = {}; for (const val of nonunique) { unique[val] = null; } Object.assign(T, { [key]: Object.keys(unique)}); } DEBUG && console.log(T) currentT = {}; } return !!obj; }