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