11import Solver from "../../../../../website/src/components/Solver.js"
22
33# Day 14: Restroom Redoubt
4+ by [ Bulby] ( https://github.com/TheDrawingCoder-Gamer )
5+
46
57## Puzzle description
68
79https://adventofcode.com/2024/day/14
810
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+
9313## Solutions from the community
10314
11315- [ 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
16320- [ Solution] ( https://github.com/spamegg1/aoc/blob/master/2024/14/14.scala#L165 ) by [ Spamegg] ( https://github.com/spamegg1 )
17321- [ Solution] ( https://github.com/jnclt/adventofcode2024/blob/main/day14/restroom-redoubt.sc ) by [ jnclt] ( https://github.com/jnclt )
18322- [ 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 )
20323- [ Solution] ( https://github.com/jportway/advent2024/blob/master/src/main/scala/Day14.scala ) by [ Joshua Portway] ( https://github.com/jportway )
21324- [ Solution] ( https://github.com/AvaPL/Advent-of-Code-2024/tree/main/src/main/scala/day14 ) by [ Paweł Cembaluk] ( https://github.com/AvaPL )
22325
0 commit comments