Advent of Code Day 9 — Rope Bridge

The plot takes a turn today as the protagonist, who is, I believe, human, is separated from the jungle-delving elves when the rope bridge they are crossing snaps under their weight.

Since the bridge is just one Plank length long, far, far smaller than the diameter of a proton, you’d think even the clumsiest human would be able to cross the gap. But it is what it is.

Part 1 of the problem considers a rope with a head and a tail, with the head moving randomly and the tail moving to keep within two units of the head. The answer is how many unique locations the tail visits. Part 2 extends the rope to 10 units long, but same deal otherwise.

Python 3.11

I do the Python in the morning without looking at anyone else’s solutions. So sometimes I miss obvious stuff. I’m sure there are much better ways to do this. More “Pythonic” ways, but it is what it is.

from math import sqrt

def walk_the_snake(puzzle: list, rope_size: int) -> int:
    snake = [(0,0)] * rope_size
    dir_map = {'U': (0, -1), 'D': (0, 1), 'L': (-1, 0), 'R': (1, 0)}
    tail_visited = set()
    tail_visited.add(snake[0])

    for command in puzzle:
        snake[-1] = (snake[-1][0] + command[1] * dir_map[command[0]][0], snake[-1][1] + command[1] * dir_map[command[0]][1])
        anything_moved = True
        while anything_moved:
            anything_moved = False
            for i in range(len(snake) - 1, 0, -1):
                head = snake[i]
                tail = snake[i-1]
                dist_from_tail = sqrt((head[0] - tail[0]) ** 2 + (head[1] - tail[1]) ** 2)
                if dist_from_tail < 2.0: break
                # tail takes one step towards head
                tail = (tail[0] + just_one_step(head[0], tail[0]), tail[1] + just_one_step(head[1], tail[1]))
                anything_moved = True
                snake[i-1] = tail
            tail_visited.add(snake[0])
    
    return len(tail_visited)

def code_gen(data: list):
    for l in data:
        t = l.split()
        yield t[0], int(t[1])

def just_one_step(a, b) -> int:
    return 0 if a == b else int(abs(a - b)/(a - b))

with open(r"2022\puzzle9.txt") as f:
    puzzle = [command for command in code_gen(f.read().splitlines())]

print ("Part 1: {}".format(walk_the_snake(puzzle, 2)))
print ("Part 2: {}".format(walk_the_snake(puzzle, 10)))

Java 14

The Java solution is informed by the stuff I found out reading other people’s solutions. Specifically, that Part 2 also solves Part 1. So there was no need to call it twice.

    @Override
    public void preprocess(String content) {
        final var puzzle = getInputDataByLine(content);
        final int ropeSize = 10;

        List<Point> snake = new ArrayList<>();
        Set<Point> part2Visited = new HashSet<>();
        Set<Point> part1Visited = new HashSet<>();
        final var origin = new Point(0, 0);

        snake = IntStream.range(0, ropeSize).mapToObj(i -> origin).collect(Collectors.toList());

        part2Visited.add(snake.get(0));
        part1Visited.add(snake.get(0));

        var curPoint = origin;

        for (var command : puzzle) {
            curPoint = movePoint(curPoint, command);

            snake.set(ropeSize - 1, curPoint);

            Boolean anythingMoved;
            do {
                anythingMoved = false;

                for (int i = ropeSize - 1; i > 0; i--) {
                    var head = snake.get(i);
                    var tail = snake.get(i - 1);

                    if (Math.sqrt(Math.pow(head.x - tail.x, 2) + Math.pow(head.y - tail.y, 2)) >= 2.0) {
                        snake.set(i - 1,
                                new Point(tail.x + justOneStep(head.x, tail.x), tail.y + justOneStep(head.y, tail.y)));
                        anythingMoved = true;
                    }
                }

                part1Visited.add(snake.get(ropeSize - 2));
                part2Visited.add(snake.get(0));
            } while (anythingMoved);

            part1Solution = part1Visited.size();
            part2Solution = part2Visited.size();
        }
    }

Otherwise it’s more or less the same as before.

The Game

I might start writing the games again. Tonight’s game would have just been a Snake variant… obviously 🙂