ProjectsAOCBlogAbout

Day 09: Rope Bridge

**
Tracking the tail of a rope as the head moves around based on some rules. Part I is a very short rope. Part II is a longer rope.
Journal

Part I went pretty quickly. There's not a whole lot of code to implement the given rules. Basically, the head moves up, down, left, or right and you figure out where the tail moves.

It seemed like Part II was basically just extending the same thing to a longer rope. So, same stuff but chain the movements together treating each section as a head and tail. And it almost was. There's just an extra case to handle (they hint this in the description) and it took me a bit to figure out what that was.

The extra case is that now a "head" section may have moved diagonally.

The code here is the generalized version that works for Part I and Part II

Relevant Code
type Knot = [number, number];
const X = 0;
const Y = 1;
const getNewPosition = (head: Knot, tail: Knot) => {
const position: Knot = [...tail];
// head moved left or right of the tail in the same column far enough to move that way
if (Math.abs(head[X] - tail[X]) > 1 && head[Y] === tail[Y]) {
position[X] = head[X] - tail[X] > 0 ? head[X] - 1 : head[X] + 1;
// head moved up or down in the same row
} else if (Math.abs(head[Y] - tail[Y]) > 1 && head[X] === tail[X]) {
position[Y] = head[Y] - tail[Y] > 0 ? head[Y] - 1 : head[Y] + 1;
// head moved diagonal
} else if (
Math.abs(head[Y] - tail[Y]) > 1 ||
Math.abs(head[X] - tail[X]) > 1
) {
position[X] = head[X] - tail[X] > 0 ? tail[X] + 1 : tail[X] - 1;
position[Y] = head[Y] - tail[Y] > 0 ? tail[Y] + 1 : tail[Y] - 1;
}
return position;
};
export const getVisitedCount = (movesStr: string, numKnots: number) => {
const moves = movesStr.split("\n").map((move) => move.split(" "));
const knots: Knot[] = [];
for (let i = 0; i < numKnots; i++) {
knots.push([0, 0]);
}
const visited = new Set(["0,0"]);
moves.forEach(([direction, distance]) => {
const axis = ["L", "R"].includes(direction) ? X : Y;
const value = ["R", "U"].includes(direction) ? 1 : -1;
for (let i = 0; i < +distance; i++) {
// move the head
knots[0][axis] += value;
// move the rest of the rope
for (let j = 1; j < knots.length; j++) {
knots[j] = getNewPosition(knots[j - 1], knots[j]);
}
visited.add(knots[knots.length - 1].join(","));
}
});
return visited.size;
};
Output

Test Rope

  1. Part I: 13 positions visited (0 ms)
  2. Part II: 36 positions visited (0 ms) (this uses a different test input that I didn't bother to include below)

Real Rope

  1. Part I: 6269 positions visited (18 ms)
  2. Part II: 2557 positions visited (12 ms)