1
1
import Solver from "../../../../../website/src/components/Solver.js"
2
2
3
3
# Day 14: Restroom Redoubt
4
+ by [ Bulby] ( https://github.com/TheDrawingCoder-Gamer )
5
+
4
6
5
7
## Puzzle description
6
8
7
9
https://adventofcode.com/2024/day/14
8
10
11
+ ## Solution Summary
12
+
13
+ 1 . Parse input into a ` List[Robot] `
14
+ 2 . Make function to advance state by ` n ` steps
15
+ 3 . Solve
16
+ * For ` part1 ` , this is advancing the state by 100 then calculating the safety score
17
+ * For ` part2 ` , this is finding the first state where a christmas tree is visible
18
+
19
+ ## Part 1
20
+
21
+ Part 1 shouldn't be too bad. Let's get started with our ` Robot ` class (and a ` Vec2i ` class):
22
+
23
+ ``` scala
24
+ case class Vec2i (x : Int , y : Int )
25
+
26
+ case class Robot (pos : Vec2i , velocity : Vec2i )
27
+ ```
28
+
29
+
30
+ Now we can parse our input:
31
+
32
+ ``` scala
33
+ def parse (str : String ): List [Robot ] =
34
+ str.linesIterator.map:
35
+ case s " p= $px, $py v= $vx, $vy" =>
36
+ Robot (Vec2i (px.toInt, py.toInt), Vec2i (vx.toInt, vy.toInt))
37
+ .toList
38
+ ```
39
+
40
+ Let's define our grid size in a ` val ` , as it can depend on if we are doing a test input or not:
41
+
42
+ ``` scala
43
+ val size = Vec2i (101 , 103 )
44
+ ```
45
+
46
+ The problem text states that when a robot goes off the edge it comes back on the other side, which sounds a lot like modulo.
47
+ Unfortunately, modulo is incorrect in our case for negative numbers. Let's define a remainder instead:
48
+
49
+ ``` scala
50
+ extension (self : Int )
51
+ infix def rem (that : Int ): Int =
52
+ val m = math.abs(self) % that
53
+ if self < 0 then
54
+ that - m
55
+ else
56
+ m
57
+ ```
58
+
59
+ Now we can add a function to ` Robot ` that advances the state by ` n ` :
60
+
61
+ ``` scala
62
+ case class Robot (pos : Vec2i , velocity : Vec2i ):
63
+ def stepN (n : Int = 1 ): Robot =
64
+ copy(pos = pos.copy(x = (pos.x + n * velocity.x) rem size.x, y = (pos.y + n * velocity.y) rem size.y))
65
+ ```
66
+
67
+ Now for the full ` List[Robot] ` , we can add ` stepN ` to that too, and also define our safety function:
68
+
69
+ ``` scala
70
+ extension (robots : List [Robot ])
71
+ def stepN (n : Int = 1 ): List [Robot ] = robots.map(_.stepN(n))
72
+
73
+ def safety : Int =
74
+ val middleX = size.x / 2
75
+ val middleY = size.y / 2
76
+
77
+ robots.groupBy: robot =>
78
+ (robot.pos.x.compareTo(middleX), robot.pos.y.compareTo(middleY)) match
79
+ case (0 , _) | (_, 0 ) => - 1
80
+ case ( 1 , - 1 ) => 0
81
+ case (- 1 , - 1 ) => 1
82
+ case (- 1 , 1 ) => 2
83
+ case ( 1 , 1 ) => 3
84
+ .removed(- 1 ).values.map(_.length).product
85
+ ```
86
+
87
+ Let's explain this a little. There are 4 quadrants, and as specified there are also lines that aren't in any quadrant.
88
+ We can use ` groupBy ` to group the robots into quadrants.
89
+
90
+ First, we get the midpoints by dividing the size by 2. We then compare the robot to the midpoint, returning ` -1 ` if it's on the line
91
+ (either comparison is equal), and otherwise sort the remaining 4 results into quadrants. We then remove the robots on the line,
92
+ get the length of each of the lists, and multiply them together.
93
+
94
+ With this ` part1 ` is easy to implement:
95
+
96
+ ``` scala
97
+ def part1 (input : String ): Int = parse(input).stepN(100 ).safety
98
+ ```
99
+
100
+ ## Part 2
101
+
102
+ Part 2 wants us to find an image which is really hard. Thankfully, there is one thing I know about Christmas trees: They have
103
+ a lot of lines in a row and are an organized shape.
104
+
105
+ We are assuming here that the tree is solid. This assumption is fine in this case, the space to search is finite
106
+ so the worst we can get is a "not found" answer, but in other problems we may want to be more sure about our input.
107
+
108
+ The christmas trees I know are really tall, but only in a few columns. So let's test the vertical columns for a few lines that are really long.
109
+ They also have a lot of shorter horizontal lines, so let's also check the rows for a lot of shorter lines.
110
+
111
+ Let's also inspect the input more: the grid size is 101 wide and 103 tall. If we've moved vertically 103 times, then we've moved a multiple
112
+ of 103 and are thus back at the start. The same logic applies for horizontal movement, but with 101 instead. This means a robot can, at most, be in
113
+ 101 * 103 unique positions, or 10,403, and because all robots are moved at the same time there will only ever be 10,403 unique states. This lets us
114
+ fail fast while writing our code in case we messed something up.
115
+
116
+
117
+ This is arbitrary and only really possible by print debugging your code, but here's my final code:
118
+
119
+ ``` scala
120
+ extension (robots : List [Robot ])
121
+ def findEasterEgg : Int =
122
+ (0 to (size.x * size.y)).find: i =>
123
+ val newRobots = robots.stepN(i)
124
+ newRobots.groupBy(_.pos.y).count(_._2.length >= 10 ) > 15 && newRobots.groupBy(_.pos.x).count(_._2.length >= 15 ) >= 3
125
+ .getOrElse(- 1 )
126
+ ```
127
+
128
+ We don't even need to check if the lines are contiguous - our test is strict enough with counting lines that it works regardless. Results may vary
129
+ on your input. Here I test if there are more than 15 horizontal lines with a length of 10 or more, and that there are 3 or more vertical lines
130
+ with length 15 or greater.
131
+
132
+ Then let's hook it up to ` part2 ` :
133
+
134
+ ``` scala
135
+ def part2 (input : String ): Int = parse(input).findEasterEgg
136
+ ```
137
+
138
+ My Christmas tree looks like this:
139
+
140
+ ```
141
+ ......................................................................................................
142
+ .................................................#....................................................
143
+ ..............................................#.......................................................
144
+ ................#.#............................#......................................................
145
+ .................................................................................#....................
146
+ ................................................................................................#.....
147
+ ..#...................................................................................................
148
+ .....#........................................................................................#.......
149
+ .#....................................................................................................
150
+ ......................................................................................................
151
+ ...........................................................#...........#........#.....................
152
+ .........#..........................................#.........#.......................................
153
+ ..............................................#........#..............................................
154
+ .......................................#..........#...................................................
155
+ ...................................................................................#.............#....
156
+ ............................................................#.........................................
157
+ .................#.......................#.............................#..............................
158
+ .............................#........................................................................
159
+ .................................#...............................#.........................#..........
160
+ .........#........................#..........................................#........................
161
+ ......................................................................................................
162
+ .............................#........................................................................
163
+ .......#.............................#....#...........................................................
164
+ ...#..............................................................#......................#............
165
+ ..........................#............................................#..............................
166
+ .......................................................................................#..............
167
+ ..............................................#.......................................................
168
+ .............#.......................................................................#................
169
+ ............#.................................#.....#.......................................#.........
170
+ ......................................................................................................
171
+ ................###############################.......................................................
172
+ ................#.............................#.......................................................
173
+ ................#.............................#.....#........................#........................
174
+ ................#.............................#.................#.....................................
175
+ ................#.............................#.......................................................
176
+ ................#..............#..............#.......................................................
177
+ ................#.............###.............#....#.........................................#........
178
+ ................#............#####............#.......................................................
179
+ ................#...........#######...........#.......................................................
180
+ ................#..........#########..........#.......................................................
181
+ ......#.........#............#####............#......................................................#
182
+ ................#...........#######...........#.......................................................
183
+ ................#..........#########..........#....................#...............................#..
184
+ ................#.........###########.........#........................................#..............
185
+ ...#......#.....#........#############........#..#....................................................
186
+ ................#..........#########..........#..............#.....................................#..
187
+ ................#.........###########.........#.......................................................
188
+ ................#........#############........#............................................#..........
189
+ ................#.......###############.......#.......................................................
190
+ ..........#.....#......#################......#.......................................................
191
+ ................#........#############........#..........................#.........#..................
192
+ ................#.......###############.......#....................................................#..
193
+ ........#.......#......#################......#......................................................#
194
+ ................#.....###################.....#.......................................................
195
+ ................#....#####################....#..#....................................................
196
+ ................#.............###.............#..........#............................................
197
+ ................#.............###.............#......................#................................
198
+ ................#.............###.............#..........................#............................
199
+ ................#.............................#.......................#...............................
200
+ ................#.............................#....................................#..................
201
+ ................#.............................#......................................#................
202
+ ......#.........#.............................#.......................................................
203
+ ................###############################............................#..........................
204
+ ......................................................................................................
205
+ ...............#...........................................#..........................................
206
+ .........................................#............................................................
207
+ ...................#.........................................................#........................
208
+ .....................................#.............................................................#..
209
+ ...........................#....................#.....................................................
210
+ ..........................................................#...........................#...............
211
+ ......................................................................................................
212
+ ..#............#................#............................................#........................
213
+ ........................................#..#..........................................................
214
+ ...........#......................................................................#...................
215
+ ............#..........#...............................................................#.#............
216
+ ............................................................#.........................................
217
+ ...#......................................................................#...........................
218
+ .....#................................................................................................
219
+ ......................................................................................................
220
+ ......................................................................................................
221
+ ............................................#......................................#..................
222
+ ...............#............................................................#.........................
223
+ ........................................#.............................................................
224
+ ............#..#......................................................................................
225
+ ...........#.............#.................#..........................................................
226
+ ................................#.....................................................................
227
+ .........#.................................#..........................................................
228
+ .......................#............................................................................#.
229
+ ...............#......................................................................................
230
+ ..............................#.#.....................................................................
231
+ ......................................................................................................
232
+ ......................................................................................................
233
+ ...........#..........................................................................................
234
+ .........................................................................................#............
235
+ ..........................#...........................................................................
236
+ ...............................#.......................#..............................................
237
+ ...............#......................................................................................
238
+ ...........#...............................#..........................................................
239
+ .................#.................#............................................#...#.................
240
+ ...................................................................................................#..
241
+ ...............................................................................#......................
242
+ ....#...................................................................#.............................
243
+ ........#...................#.....................#...................................................
244
+ ................................................................................#.....................
245
+ ```
246
+
247
+ With this information of how this looks we could make a smarter ` findEasterEgg ` with the knowledge of the border. The border
248
+ makes it much easier as we only have to check for 2 contiguous lines in each dimension.
249
+
250
+ ## Final Code
251
+
252
+ ``` scala
253
+ case class Vec2i (x : Int , y : Int )
254
+
255
+ val size = Vec2i (101 , 103 )
256
+
257
+ extension (self : Int )
258
+ infix def rem (that : Int ): Int =
259
+ val m = math.abs(self) % that
260
+ if self < 0 then
261
+ that - m
262
+ else
263
+ m
264
+
265
+ case class Robot (pos : Vec2i , velocity : Vec2i )
266
+ def stepN (n : Int = 1 ): Robot =
267
+ copy(pos = pos.copy(x = (pos.x + n * velocity.x) rem size.x, y = (pos.y + n * velocity.y) rem size.y))
268
+
269
+ def parse (str : String ): List [Robot ] =
270
+ str.linesIterator.map:
271
+ case s " p= $px, $py v= $vx, $vy" =>
272
+ Robot (Vec2i (px.toInt, py.toInt), Vec2i (vx.toInt, vy.toInt))
273
+ .toList
274
+
275
+ extension (robots : List [Robot ])
276
+ def stepN (n : Int = 1 ): List [Robot ] = robots.map(_.stepN(n))
277
+
278
+ def safety : Int =
279
+ val middleX = size.x / 2
280
+ val middleY = size.y / 2
281
+
282
+ robots.groupBy: robot =>
283
+ (robot.pos.x.compareTo(middleX), robot.pos.y.compareTo(middleY)) match
284
+ case (0 , _) | (_, 0 ) => - 1
285
+ case ( 1 , - 1 ) => 0
286
+ case (- 1 , - 1 ) => 1
287
+ case (- 1 , 1 ) => 2
288
+ case ( 1 , 1 ) => 3
289
+ .removed(- 1 ).values.map(_.length).product
290
+
291
+ def findEasterEgg : Int =
292
+ (0 to 10403 ).find: i =>
293
+ val newRobots = robots.stepN(i)
294
+ newRobots.groupBy(_.pos.y).count(_._2.length >= 10 ) > 15 && newRobots.groupBy(_.pos.x).count(_._2.length >= 15 ) >= 3
295
+ .getOrElse(- 1 )
296
+
297
+ def part1 (input : String ): Int = parse(input).stepN(100 ).safety
298
+
299
+ def part2 (input : String ): Int = parse(input).findEasterEgg
300
+ ```
301
+
302
+ ## Run it in the browser
303
+
304
+ ### Part 1
305
+
306
+ <Solver puzzle =" day14-part1 " year =" 2024 " />
307
+
308
+ ### Part 2
309
+
310
+ <Solver puzzle =" day14-part2 " year =" 2024 " />
311
+
312
+
9
313
## Solutions from the community
10
314
11
315
- [ Solution] ( https://github.com/nikiforo/aoc24/blob/main/src/main/scala/io/github/nikiforo/aoc24/D14T2.scala ) by [ Artem Nikiforov] ( https://github.com/nikiforo )
@@ -16,7 +320,6 @@ https://adventofcode.com/2024/day/14
16
320
- [ Solution] ( https://github.com/spamegg1/aoc/blob/master/2024/14/14.scala#L165 ) by [ Spamegg] ( https://github.com/spamegg1 )
17
321
- [ Solution] ( https://github.com/jnclt/adventofcode2024/blob/main/day14/restroom-redoubt.sc ) by [ jnclt] ( https://github.com/jnclt )
18
322
- [ Solution] ( https://github.com/Philippus/adventofcode/blob/main/src/main/scala/adventofcode2024/Day14.scala ) by [ Philippus Baalman] ( https://github.com/philippus )
19
- - [ Writeup] ( https://thedrawingcoder-gamer.github.io/aoc-writeups/2024/day14.html ) by [ Bulby] ( https://github.com/TheDrawingCoder-Gamer )
20
323
- [ Solution] ( https://github.com/jportway/advent2024/blob/master/src/main/scala/Day14.scala ) by [ Joshua Portway] ( https://github.com/jportway )
21
324
- [ Solution] ( https://github.com/AvaPL/Advent-of-Code-2024/tree/main/src/main/scala/day14 ) by [ Paweł Cembaluk] ( https://github.com/AvaPL )
22
325
0 commit comments