Skip to content

Commit 733219b

Browse files
committed
feat: Solving day 18
1 parent 8c38657 commit 733219b

File tree

3 files changed

+1269
-0
lines changed

3 files changed

+1269
-0
lines changed

docs/day18.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
---
2+
url: "https://adventofcode.com/2024/day/18"
3+
---
4+
5+
## Day 18: RAM Run
6+
7+
You and The Historians look a lot more pixelated than you remember. You're inside a computer at the North Pole!
8+
9+
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!"
10+
11+
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.
12+
13+
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:
14+
15+
```txt
16+
5,4
17+
4,2
18+
4,5
19+
3,0
20+
2,1
21+
6,3
22+
2,4
23+
1,5
24+
0,6
25+
3,3
26+
2,6
27+
5,1
28+
1,2
29+
5,5
30+
2,5
31+
6,5
32+
1,4
33+
0,4
34+
6,4
35+
1,1
36+
6,1
37+
1,0
38+
0,5
39+
1,6
40+
2,0
41+
```
42+
43+
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.
44+
45+
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.
46+
47+
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.
48+
49+
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:
50+
51+
```txt
52+
...#...
53+
..#..#.
54+
....#..
55+
...#..#
56+
..#..#.
57+
.#..#..
58+
#.#....
59+
```
60+
61+
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:
62+
63+
```txt
64+
OO.#OOO
65+
.O#OO#O
66+
.OOO#OO
67+
...#OO#
68+
..#OO#.
69+
.#.O#..
70+
#.#OOOO
71+
```
72+
73+
Simulate the first kilobyte (`1024` bytes) falling onto your memory space. Afterward, what is the minimum number of steps needed to reach the exit?

src/day18/day18.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package day18
2+
3+
import (
4+
"slices"
5+
"strconv"
6+
"strings"
7+
)
8+
9+
const MAX_THREADS_PER_LOOP = 32
10+
11+
type point struct {
12+
x, y int
13+
}
14+
15+
type maze struct {
16+
walls map[point]bool
17+
start point
18+
end point
19+
width int
20+
height int
21+
}
22+
23+
func (m maze) valid(p point) bool {
24+
return p.x >= 0 && p.y >= 0 && p.x < m.width && p.y < m.height
25+
}
26+
27+
type thread struct {
28+
p point
29+
moves uint
30+
}
31+
32+
func Solve(input string, size int, square int) uint {
33+
m := maze{width: square, height: square, start: point{0, 0}, end: point{x: square - 1, y: square - 1}, walls: make(map[point]bool)}
34+
blocks := parseInput(input)
35+
for i := 0; i < size; i++ {
36+
m.walls[blocks[i]] = true
37+
}
38+
return solve(m)
39+
}
40+
41+
func parseInput(input string) []point {
42+
result := make([]point, 0)
43+
for _, line := range strings.Split(input, "\n") {
44+
d := strings.Split(line, ",")
45+
if len(d) != 2 {
46+
continue
47+
}
48+
49+
x, _ := strconv.Atoi(d[0])
50+
y, _ := strconv.Atoi(d[1])
51+
result = append(result, point{x: x, y: y})
52+
}
53+
return result
54+
}
55+
56+
func solve(m maze) uint {
57+
seenSpaces := make(map[point]uint)
58+
seenSpaces[m.start] = 1
59+
threads := []thread{{p: m.start}}
60+
coldStorage := make([]thread, 0)
61+
var solution uint = 0
62+
loops := 0
63+
var threadCount uint
64+
for {
65+
nextThreads := make([]thread, 0)
66+
threadCount += uint(len(threads))
67+
for i, t := range threads {
68+
if i >= MAX_THREADS_PER_LOOP {
69+
coldStorage = append(coldStorage, t)
70+
continue
71+
} else if t.p == m.end {
72+
if solution == 0 || t.moves < solution {
73+
solution = t.moves
74+
}
75+
} else {
76+
nextThreads = append(calcNextMoves(m, t, seenSpaces), nextThreads...)
77+
}
78+
}
79+
80+
if len(nextThreads) == 0 {
81+
if len(coldStorage) > 0 {
82+
x := MAX_THREADS_PER_LOOP
83+
if len(coldStorage) < x {
84+
x = len(coldStorage)
85+
}
86+
nextThreads = coldStorage[:x]
87+
coldStorage = slices.Delete(coldStorage, 0, x)
88+
} else {
89+
if solution == 0 {
90+
panic("No solution with no more spaces to check")
91+
}
92+
return solution
93+
}
94+
}
95+
for i, t := range nextThreads {
96+
if solution > 0 && t.moves >= solution {
97+
nextThreads = slices.Delete(nextThreads, i, i)
98+
}
99+
if seenSpaces[t.p] == 0 || t.moves < seenSpaces[t.p] {
100+
seenSpaces[t.p] = t.moves
101+
}
102+
}
103+
threads = nextThreads
104+
loops++
105+
}
106+
}
107+
108+
func calcNextMoves(m maze, curr thread, seenSpaces map[point]uint) []thread {
109+
nextMoves := make([]thread, 0)
110+
possibilities := []thread{{p: point{x: curr.p.x + 1, y: curr.p.y}, moves: curr.moves},
111+
{p: point{x: curr.p.x, y: curr.p.y + 1}, moves: curr.moves},
112+
{p: point{x: curr.p.x - 1, y: curr.p.y}, moves: curr.moves},
113+
{p: point{x: curr.p.x, y: curr.p.y - 1}, moves: curr.moves}}
114+
for _, t := range possibilities {
115+
if m.valid(t.p) && !m.walls[t.p] {
116+
t.moves++
117+
if seenSpaces[t.p] == 0 || t.moves < seenSpaces[t.p] {
118+
nextMoves = append(nextMoves, t)
119+
}
120+
}
121+
}
122+
return nextMoves
123+
}

0 commit comments

Comments
 (0)