Public
Edited
Dec 28, 2022
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function getMotions(motionString) {
return motionString
.trim()
.split("\n")
.map(row => row
.split(" ")
)
.map(row => [row[0], Number(row[1])]);
};
Insert cell
motions = getMotions(input);
Insert cell
testMotions = getMotions(testInput);
Insert cell
Insert cell
headTailPositions = {
const h = {grid: [["s"]], x: 0, y: 0};
const t = {grid: [["s"]], x: 0, y: 0};
function tailShouldMove(key) {
return (Math.abs(h[key] - t[key])) > 1;
}
for (const [direction, step] of motions.slice(0, index)) {
// for (const [direction, step] of motions) {
switch (direction) {
case "U":
for (let i = 0; i < step; i++) {
// Add row that doesn't exist
if (h.y + 1 > h.grid.length - 1) {
h.grid.push(new Array(h.grid[0].length));
t.grid.push(new Array(h.grid[0].length));
}
h.grid[h.y][h.x] = 1;
h.y += 1;
h.grid[h.y][h.x] = 8;
// Check how new h.y will affect t
if (tailShouldMove("y")) {
t.grid[t.y][t.x] = 1;
t.x = h.x;
t.y = h.y - 1;
t.grid[t.y][t.x] = 8;
}
}
break;
case "D":
for (let i = 0; i < step; i++) {
// Add row that doesn't exist. Adjust coords accordingly.
if (h.y - 1 < 0) {
h.grid.unshift(new Array(h.grid[0].length));
h.y++;
t.grid.unshift(new Array(h.grid[0].length));
t.y++;
}
h.grid[h.y][h.x] = 1;
h.y -= 1;
h.grid[h.y][h.x] = 8;
// Check how new h.y will affect t
if (tailShouldMove("y")) {
t.grid[t.y][t.x] = 1;
t.x = h.x;
t.y = h.y + 1;
t.grid[t.y][t.x] = 8;
}
}
break;
case "L":
for (let i = 0; i < step; i++) {
// Add cell that doesn't exist. Adjust coords accordingly.
if (h.x - 1 < 0) {
h.grid.map(row => row.unshift(undefined));
h.x++;
t.grid.map(row => row.unshift(undefined));
t.x++;
}
h.grid[h.y][h.x] = 1;
h.x -= 1;
h.grid[h.y][h.x] = 8;
// Check how new h.x will affect t
if (tailShouldMove("x")) {
t.grid[t.y][t.x] = 1;
t.x = h.x + 1;
t.y = h.y;
t.grid[t.y][t.x] = 8;
}
}
break;
case "R":
for (let i = 0; i < step; i++) {
// Add cell that doesn't exist
if (h.x + 1 > h.grid[h.y].length - 1) {
h.grid.map(row => row.push(undefined));
t.grid.map(row => row.push(undefined));
}
h.grid[h.y][h.x] = 1;
h.x += 1;
h.grid[h.y][h.x] = 8;
// Check how new h.x will affect t
if (tailShouldMove("x")) {
t.grid[t.y][t.x] = 1;
t.x = h.x - 1;
t.y = h.y;
t.grid[t.y][t.x] = 8;
}
}
break;
}
}

return t.grid
.map(row => Array.from(row, v => v || 0));
}
Insert cell
headTailPositions.reduce((total, row) => total + row.reduce((rowTotal, v) => rowTotal + (v ? 1 : 0)), 0);
Insert cell
Insert cell
Insert cell
testMotions2 = getMotions(testInput2);
Insert cell
Insert cell
Insert cell
calcTailPosition(10)
.reduce((total, row, i) => total + row.reduce((rowTotal, v, i) => rowTotal + (v > 0 ? 1 : 0), 0), 0);
Insert cell
function calcTailPosition(numKnots = 2) {
const knots = [];
for (let i = 0; i < numKnots; i++) {
knots.push({grid: [["s"]], x: 0, y: 0});
}

function updateTailPosition(h, t) {
// A tailing knot always needs to get behind the head if possible, except when it moves diagonally to be diagonal to the head
const dX = h.x - t.x;
const dY = h.y - t.y;

if (dX > 1 && dY > 1) {
// Move tail right and up
t.grid[t.y][t.x] = 1;
t.x = h.x - 1;
t.y = h.y - 1;
t.grid[t.y][t.x] = 8;
} else if (dX > 1 && dY < -1) {
// Move tail right and down
t.grid[t.y][t.x] = 1;
t.x = h.x - 1;
t.y = h.y + 1;
t.grid[t.y][t.x] = 8;
} else if (dX < -1 && dY > 1) {
// Move tail left and up??
t.grid[t.y][t.x] = 1;
t.x = h.x + 1;
t.y = h.y - 1;
t.grid[t.y][t.x] = 8;
} else if (dX < -1 && dY < -1) {
// Move tail right and down??
t.grid[t.y][t.x] = 1;
t.x = h.x + 1;
t.y = h.y + 1;
t.grid[t.y][t.x] = 8;
} else if (dX > 1 && Math.abs(dY) > 0) {
// Move tail right and vertically
t.grid[t.y][t.x] = 1;
t.x = h.x - 1;
t.y = h.y;
t.grid[t.y][t.x] = 8;
} else if (dX < -1 && Math.abs(dY) > 0) {
// Move tail left and vertically
t.grid[t.y][t.x] = 1;
t.x = h.x + 1;
t.y = h.y;
t.grid[t.y][t.x] = 8;
} else if (dY > 1 && Math.abs(dX) > 0) {
// Move tail up and horizontally
t.grid[t.y][t.x] = 1;
t.x = h.x;
t.y = h.y - 1;
t.grid[t.y][t.x] = 8;
} else if (dY < -1 && Math.abs(dX) > 0) {
// Move tail down
t.grid[t.y][t.x] = 1;
t.x = h.x;
t.y = h.y + 1;
t.grid[t.y][t.x] = 8;
} else if (dY > 1 && dX === 0) {
// Move tail up
t.grid[t.y][t.x] = 1;
// t.x = h.x;
t.y = h.y - 1;
t.grid[t.y][t.x] = 8;
} else if (dY < -1 && dX === 0) {
// Move tail down
t.grid[t.y][t.x] = 1;
// t.x = h.x;
t.y = h.y + 1;
t.grid[t.y][t.x] = 8;
} else if (dX > 1 && dY === 0) {
// Move tail right
t.grid[t.y][t.x] = 1;
t.x = h.x - 1;
// t.y = h.y;
t.grid[t.y][t.x] = 8;
} else if (dX < -1 && dY === 0) {
// Move tail left
t.grid[t.y][t.x] = 1;
t.x = h.x + 1;
// t.y = h.y;
t.grid[t.y][t.x] = 8;
}
}
// for (const [dirIndex, [direction, step]] of testMotions2.slice(0, index).entries()) {
for (const [direction, step] of motions) {
switch (direction) {
case "U":
for (let i = 0; i < step; i++) {
for (let j = 0; j < knots.length - 1; j++) {
const h = knots[j];
const t = knots[j + 1];
if (j === 0) {
// Add row that doesn't exist
if (h.y + 1 > h.grid.length - 1) {
knots.forEach(k => k.grid.push(new Array(knots[0].grid[0].length)));
}
h.grid[h.y][h.x] = 1;
h.y += 1;
h.grid[h.y][h.x] = 8;
}
// Check how new h.y will affect t
updateTailPosition(h, t);
}
}
break;
case "D":
for (let i = 0; i < step; i++) {
for (let j = 0; j < knots.length - 1; j++) {
const h = knots[j];
const t = knots[j + 1];
// Add row that doesn't exist. Adjust coords accordingly.
if (j === 0) {
if (h.y - 1 < 0) {
knots.forEach(k => {
k.grid.unshift(new Array(knots[0].grid[0].length))
k.y++;
});
}
h.grid[h.y][h.x] = 1;
h.y -= 1;
h.grid[h.y][h.x] = 8;
}
// Check how new h.y will affect t
updateTailPosition(h, t);
}
}
break;
case "L":
for (let i = 0; i < step; i++) {
for (let j = 0; j < knots.length - 1; j++) {
const h = knots[j];
const t = knots[j + 1];
// Add cell that doesn't exist. Adjust coords accordingly.
if (j === 0) {
if (h.x - 1 < 0) {
knots.forEach(k => {
k.grid.forEach(row => row.unshift(undefined))
k.x++;
});
}
h.grid[h.y][h.x] = 1;
h.x -= 1;
h.grid[h.y][h.x] = 8;
}
// Check how new h.x will affect t
updateTailPosition(h, t);
}
}
break;
case "R":
for (let i = 0; i < step; i++) {
for (let j = 0; j < knots.length - 1; j++) {
const h = knots[j];
const t = knots[j + 1];
// Add cell that doesn't exist
if (j === 0) {
if (h.x + 1 > h.grid[h.y].length - 1) {
knots.forEach(k => {
k.grid.forEach(row => row.push(undefined))
});
}
h.grid[h.y][h.x] = 1;
h.x += 1;
h.grid[h.y][h.x] = 8;
}
// Check how new h.x will affect t
updateTailPosition(h, t);
}
}
break;
}
}

const knotsDisplay = [];
for (let i = 0; i < knots[0].grid.length; i++) {
knotsDisplay.push(new Array(knots[0].grid[0].length));
}
knots.forEach((k, index) => {
knotsDisplay[k.y][k.x] = index + 1;
});

return knots.at(-1).grid.map(row => Array.from(row, v => v || 0));
// return knotsDisplay.map(row => Array.from(row, v => v || 0));
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more