Skip to content

Commit

Permalink
Add solution for 2024, day 18
Browse files Browse the repository at this point in the history
A fun challenge, perhaps a good leg up to finally finish day 16.
  • Loading branch information
tbusser committed Dec 29, 2024
1 parent 16a0335 commit e26623f
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 0 deletions.
118 changes: 118 additions & 0 deletions 2024/18/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Day 18: RAM Run

I started off with a Dijkstra algorithm to solve the example input. This worked well enough and quickly got me an answer. When I ran it on the actual input my solution didn't fare too well. I'm sure it would've found me an answer at some point but after waiting more than 10 seconds I ended the run.

Since Dijkstra was too slow I figured I would need some other algorithm and the firs that came to mind was A*. For A* I needed a priority queue which required an implementation of a binary heap. With this done I could focus on the A* algorithm itself. Since it is quite similar to Dijkstra it didn't take a long time to implement. After running the solution it gave me the solution is about 20ms, not too bad!

Part 2 was luckily quite simple. There is probably a smarter way to do it then by checking the maze after corrupting the memory spaces after the 1024th one-by-one but if it works it ain't stupid.

Because I had to look up the A* algorithm and find some inspiration for a binary heap, I have to rate part 1 as a medium difficulty.

Rating: **Medium** / **Easy**

## Challenge description

### Part One

You and The Historians look a lot more pixelated than you remember. You're *inside a computer* at the North Pole!

Just as you're about to check out your surroundings, a program runs up to you. "This region of memory isn't safe! The User misunderstood what a *pushdown automaton* is and their algorithm is pushing whole **bytes** down on top of us! Run!"

The algorithm is fast - it's going to cause a byte to fall into your memory space once every *nanosecond*! Fortunately, you're **faster**, and by quickly scanning the algorithm, you create a **list of which bytes will fall** (your puzzle input) in the order they'll land in your memory space.

Your memory space is a two-dimensional grid with coordinates that range from `0` to `70` both horizontally and vertically. However, for the sake of example, suppose you're on a smaller grid with coordinates that range from `0` to `6` and the following list of incoming byte positions:

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

Each byte position is given as an `X,Y` coordinate, where `X` is the distance from the left edge of your memory space and `Y` is the distance from the top edge of your memory space.

You and The Historians are currently in the top left corner of the memory space (at `0,0`) and need to reach the exit in the bottom right corner (at `70,70` in your memory space, but at `6,6` in this example). You'll need to simulate the falling bytes to plan out where it will be safe to run; for now, simulate just the first few bytes falling into your memory space.

As bytes fall into your memory space, they make that coordinate **corrupted**. Corrupted memory coordinates cannot be entered by you or The Historians, so you'll need to plan your route carefully. You also cannot leave the boundaries of the memory space; your only hope is to reach the exit.

In the above example, if you were to draw the memory space after the first `12` bytes have fallen (using `.` for safe and `#` for corrupted), it would look like this:

```
...#...
..#..#.
....#..
...#..#
..#..#.
.#..#..
#.#....
```

You can take steps up, down, left, or right. After just 12 bytes have corrupted locations in your memory space, the shortest path from the top left corner to the exit would take **`22`** steps. Here (marked with `O`) is one such path:

```
**O****O**.#**O****O****O**
.**O**#**O****O**#**O**
.**O****O****O**#**O****O**
...#**O****O**#
..#**O****O**#.
.#.**O**#..
#.#**O****O****O****O**
```

Simulate the first kilobyte (`1024` bytes) falling onto your memory space. Afterward, **what is the minimum number of steps needed to reach the exit?**


### Part Two

The Historians aren't as used to moving around in this pixelated universe as you are. You're afraid they're not going to be fast enough to make it to the exit before the path is completely blocked.

To determine how fast everyone needs to go, you need to determine **the first byte that will cut off the path to the exit**.

In the above example, after the byte at `1,1` falls, there is still a path to the exit:

```
**O**..#**O****O****O**
**O**##**O****O**#**O**
**O**#**O****O**#**O****O**
**O****O****O**#**O****O**#
###**O****O**##
.##**O**###
#.#**O****O****O****O**
```

However, after adding the very next byte (at `6,1`), there is no longer a path to the exit:

```
...#...
.##..#**#**
.#..#..
...#..#
###..##
.##.###
#.#....
```

So, in this example, the coordinates of the first byte that prevents the exit from being reachable are **`6,1`**.

Simulate more of the bytes that are about to corrupt your memory space. **What are the coordinates of the first byte that will prevent the exit from being reachable from your starting position?** (Provide the answer as two integers separated by a comma with no other characters.)
84 changes: 84 additions & 0 deletions 2024/18/helpers/memory-space.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { MinHeap } from '@helpers/min-heap.js';
import { Coordinate, Grid, Position } from '@helpers/grid.js';

/* ========================================================================== */

type QueueItem = {
cost: number;
index: number;
distance: number;
};

const symbol = {
corrupted: '#',
empty: '.'
};

/* ========================================================================== */

export class MemorySpace extends Grid {
static createInstance(gridSize: number) {
const grid = new Array(gridSize * gridSize).fill(symbol.empty);

return new MemorySpace(grid, gridSize);
}

/* ---------------------------------------------------------------------- */

private endCoordinate: Coordinate = { x: this.columns - 1, y: this.columns - 1 };
private endIndex = this.coordinateToIndex(this.endCoordinate);

/* ---------------------------------------------------------------------- */

private calculateDistance(x: number, y: number): number {
return (
Math.abs(this.endCoordinate.x - x) +
Math.abs(this.endCoordinate.y - y)
);
}

/* ---------------------------------------------------------------------- */

public findShortestPath(): number {
const queuedNodes = new Set<number>([0]);
const visitedNodes = new Set<number>();
const queue = new MinHeap<QueueItem>(item => item.distance);

queue.push({
index: 0,
cost: 0,
distance: this.calculateDistance(0, 0)
});

while (queue.size > 0) {
const entry = queue.pop();
visitedNodes.add(entry.index);

const neighbors = this.neighbors(entry.index, ['up', 'right', 'down', 'left'])
.filter(neighbor =>
neighbor.value !== symbol.corrupted &&
!visitedNodes.has(neighbor.index) &&
!queuedNodes.has(neighbor.index)
);

for (const neighbor of neighbors) {
if (neighbor.index === this.endIndex) {
return entry.cost + 1;
}

queuedNodes.add(neighbor.index);
queue.push({
index: neighbor.index,
cost: entry.cost + 1,
distance: entry.cost + this.calculateDistance(neighbor.x, neighbor.y)
});
}
}

return Infinity;
}

public markMemorySpaceCorrupted(position: Position) {
this.grid[this.positionToIndex(position)] = symbol.corrupted;
}
}
13 changes: 13 additions & 0 deletions 2024/18/helpers/parse-input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Coordinate } from '@helpers/grid.js';

/* ========================================================================== */

export function parseInput(input: string): Coordinate[] {
const lines = input.split('\n');

return lines.map(lines => {
const [x, y] = lines.split(',').map(Number);

return { x, y };
});
}
21 changes: 21 additions & 0 deletions 2024/18/part-1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { MemorySpace } from './helpers/memory-space.js';
import { parseInput } from './helpers/parse-input.js';

/* ========================================================================== */

function solver(input: string): number {
const corruptedSpaces = parseInput(input);
corruptedSpaces.splice(1024);
const memorySpace = MemorySpace.createInstance(71);

corruptedSpaces.forEach(corruptedSpace => memorySpace.markMemorySpaceCorrupted(corruptedSpace));

return memorySpace.findShortestPath();
}

/* ========================================================================== */

export default {
prompt: 'Minimum number of steps to reach the exit',
solver
} satisfies Solution;
26 changes: 26 additions & 0 deletions 2024/18/part-2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { MemorySpace } from './helpers/memory-space.js';
import { parseInput } from './helpers/parse-input.js';

/* ========================================================================== */

function solver(input: string): string {
const corruptedSpaces = parseInput(input);
const items = corruptedSpaces.splice(1024);
const memorySpace = MemorySpace.createInstance(71);

corruptedSpaces.forEach(corruptedSpace => memorySpace.markMemorySpaceCorrupted(corruptedSpace));

for (const coordinate of items) {
memorySpace.markMemorySpaceCorrupted(coordinate);
if (memorySpace.findShortestPath() === Infinity) {
return `${coordinate.x},${coordinate.y}`;
}
}
}

/* ========================================================================== */

export default {
prompt: 'Coordinate of the first byte to make the exit unreachable',
solver
} satisfies Solution<string>;
1 change: 1 addition & 0 deletions 2024/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ A list of the challenges for 2024. I've rated each challenge on how difficult I
| [Day 13: Claw Contraption](./13/) | Easy | |
| [Day 14: Restroom Redoubt](./14/) | Easy | Hard |
| [Day 17: Chronospatial Computer](./17) | Easy | |
| [Day 18: RAM Run](./18) | Medium | Easy |

## Legend

Expand Down

0 comments on commit e26623f

Please sign in to comment.