18th December, 2024

The Matrix strikes back, I guess

661 words
4 min.
Post preview image
Advent of Code 2024: Day 18

A bit of a breather, after the challenging tasks of the past couple of days! ☺️

On day 2 I said that the task's input felt like a representation of the Matrix - I found this to be even truer for today's task, lol.

We start with a list of pairs that we're told represent an X,Y coordinate:

5,4
4,2
4,5
3,0
2,1
6,3
2,4
1,5
0,6
3,3
2,6
5,1

Starting from the top, each coordinate is the position of a byte that falls into the memory space, making it corrupted - so for example after the first 12 falling bytes we have a grid like this:

...#...
..#..#.
....#..
...#..#
..#..#.
.#..#..
#.#....

The task for part 1 is to simulate the fall of the first kilobyte, that is the first 1024 bytes, onto the memory space. Then we need to find the best path from the top-left corner to the bottom-right corner avoiding all the bytes.

SO.#OOO
.O#OO#O
.OOO#OO
...#OO#
..#OO#.
.#.O#..
#.#OOOE

Nothing too complicated for now.

We simply need to read the first 1024 lines of the input and parse the coordinates to determine where the # "obstacles" are placed.

const grid: Grid = new Map<string, string>();
for (let y = 0; y < side; y++) {
    for (let x = 0; x < side; x++) {
        const coord = `${x},${y}`;
        if (lines.slice(0, firstCount).some((l) => l === coord)) {
            grid.set(`{${coord}}`, '#');
        } else {
            grid.set(`{${coord}}`, '.');
        }
    }
}

We can then figure the best path out via a classical exploration algorithm using BFS:

function findPath(grid: Grid, end: Coord, side: number, firstCount: number) {
    let currentPos = new Coord(0, 0);

    const queue = new PriorityQueue<State>((a, b) => a.length - b.length);
    queue.enqueue({ position: currentPos.serialize(), length: 0 });
    let visited = new Set<string>();

    while (queue.size() > 0) {
        const { position, length } = queue.dequeue()!;
        const curr = Coord.deserialize(position);

        if (curr.equals(end)) {
            return length;
        }

        if (visited.has(position)) continue;
        visited.add(position);

        for (let n of getNeighborCoords(curr)) {
            const neighbor = n.serialize();
            if (grid.get(neighbor) === '.') {
                queue.enqueue({ position: neighbor, length: length + 1 });
            }
        }
    }

    return -1;
}

Moving to part 2, it is a matter of using the remaining part of the input - of course, when has anyone ever seen an AoC task where part of the input is ignored until the end? 🙄

The bytes keep falling onto our memory grid, and we're told there's a moment when all the possible paths to the end get blocked: our goal is to find the first coordinates of such byte, effectively making the end unreachable.

I could bet there is a more efficient way to achieve this, but I decided not to try and be too smart - after all my function for part 1 was already pretty fast (~90ms). My idea was simply to keep adding bytes to the grid and call the pathfinding function, until it returned -1 meaning that a path could not be found.

function findFirstBlockingByte(
    lines: string[],
    end: Coord,
    side: number,
    firstCount: number
) {
    let count = firstCount + 1; // Starting from 1024 + 1
    let grid: Grid = new Map<string, string>();

    while (count < lines.length) {
        for (let y = 0; y < side; y++) {
            for (let x = 0; x < side; x++) {
                const coord = `${x},${y}`;
                if (lines.slice(0, count).some((l) => l === coord)) {
                    // Introducing a new byte to the grid with each iteration
                    grid.set(`{${coord}}`, '#');
                } else {
                    grid.set(`{${coord}}`, '.');
                }
            }
        }

        const path = findPath(lines, end, side, count); // Use the function from part 1 to find a path, if any
        if (path === -1) {
            return lines[count - 1]; // If path not found, return the coordinates of the byte that caused the blockage
        }
        count++;
    }
}

The 3-bit Problem
The Towel Trials