@@ -2,10 +2,245 @@ import Solver from "../../../../../website/src/components/Solver.js"
22
33# Day 6: Trash Compactor
44
5+ by [ @scarf005 ] ( https://github.com/scarf005 )
6+
57## Puzzle description
68
79https://adventofcode.com/2025/day/6
810
11+ ## Solution Summary
12+
13+ Processing data row-by-row is usually straightforward, but handling columns can be tricky.
14+ Here comes [ transpose] ( https://www.scala-lang.org/api/3.7.4/scala/collection/IterableOps.html#transpose-5d3 ) to the rescue!
15+ The ` transpose ` method switches the rows and columns of a 2D collection, which is exactly what we need for this puzzle.
16+
17+ ## Part 1
18+
19+ ```
20+ 123 328 51 64
21+ 45 64 387 23
22+ 6 98 215 314
23+ * + * +
24+ ```
25+
26+ To make working with columns easier, we'd like the inputs to be like this:
27+
28+ ```
29+ 123 45 6 *
30+ 328 64 98 +
31+ 51 387 215 *
32+ 64 23 314 +
33+ ```
34+
35+ First, let's split the input into a 2D grid:
36+
37+ ``` scala
38+ def part1 (input : String ) = input.linesIterator.toVector // split input into lines
39+ .map(_.trim.split(raw " \s+ " )) // split lines into words by whitespaces
40+
41+ // Vector(
42+ // Array(123, 328, 51, 64),
43+ // Array(45, 64, 387, 23),
44+ // Array(6, 98, 215, 314),
45+ // Array(*, +, *, +)
46+ // )
47+ ```
48+
49+ After we transpose it, we get the desired output:
50+
51+ ``` scala
52+ def part1 (input : String ) = input.linesIterator.toVector
53+ .map(_.trim.split(raw " \s+ " ))
54+ .transpose
55+
56+ // Vector(
57+ // Vector(123, 45, 6, *),
58+ // Vector(328, 64, 98, +),
59+ // Vector(51, 387, 215, *),
60+ // Vector(64, 23, 314, +)
61+ // )
62+ ```
63+
64+ Now it's a matter of processing each column, which is fairly straightforward.
65+ Let's define an [ extension method] ( https://docs.scala-lang.org/scala3/reference/contextual/extension-methods.html ) to improve readability.
66+
67+ ``` scala
68+ extension (xs : IterableOnce [(symbol : String , nums : IterableOnce [String ])])
69+ inline def calculate : Long = xs.iterator.collect {
70+ case (" *" , nums) => nums.iterator.map(_.toLong).product
71+ case (" +" , nums) => nums.iterator.map(_.toLong).sum
72+ }.sum
73+ ```
74+
75+ Finally:
76+
77+ ``` scala
78+ def part1 (input : String ): Long = input.linesIterator.toVector
79+ .map(_.trim.split(raw " \s+ " ))
80+ .transpose
81+ .iterator
82+ .map { col => (col.last, col.init) }
83+ .calculate
84+ ```
85+
86+ ## Part 2
87+
88+ This time it's tricky, but what if we transpose the entire input string?
89+
90+ ```
91+ 123 328 51 64
92+ 45 64 387 23
93+ 6 98 215 314
94+ * + * +
95+ ```
96+
97+ would become
98+
99+ ```
100+ 1 *
101+ 24
102+ 356
103+
104+ 369+
105+ 248
106+ 8
107+
108+ 32*
109+ 581
110+ 175
111+
112+ 623+
113+ 431
114+ 4
115+ ```
116+
117+ Which is exactly what we need to calculate cephalopod math!
118+
119+ ``` scala
120+ def part2 (input : String ) =
121+ val lines = input.linesIterator.toVector // get list of lines
122+ val ops = lines.last.split(raw " \s+ " ).toVector // we'll use them later
123+ lines
124+ .init // transposing requires all rows to be of equal length, so remove symbols from last line for simplicity
125+ .transpose
126+
127+ // Vector(
128+ // Vector(1, , ),
129+ // Vector(2, 4, ),
130+ // Vector(3, 5, 6),
131+ // Vector( , , ),
132+ // Vector(3, 6, 9),
133+ // Vector(2, 4, 8),
134+ // Vector(8, , ),
135+ // Vector( , , ),
136+ // Vector( , 3, 2),
137+ // Vector(5, 8, 1),
138+ // Vector(1, 7, 5),
139+ // Vector( , , ),
140+ // Vector(6, 2, 3),
141+ // Vector(4, 3, 1),
142+ // Vector( , , 4)
143+ // )
144+ ```
145+
146+ Now we can easily convert each column into cephalopod number strings:
147+
148+ ``` scala
149+ def part2 (input : String ) =
150+ val lines = input.linesIterator.toVector // get list of lines
151+ val ops = lines.last.split(raw " \s+ " ).toVector // we'll use them later
152+ lines.init.transpose.map(_.mkString.trim)
153+
154+ // Vector(
155+ // "1",
156+ // "24",
157+ // "356",
158+ // "",
159+ // "369",
160+ // "248",
161+ // "8",
162+ // "",
163+ // "32",
164+ // "581",
165+ // "175",
166+ // "",
167+ // "623",
168+ // "431",
169+ // "4",
170+ // )
171+ ```
172+
173+ The only thing left is to split this Vector by separator ` "" ` . Sadly, the scala
174+ standard library doesn't have an ` Iterable.splitBy ` method (yet!), so we'll define our own:
175+
176+ ``` scala
177+ extension [A ](xs : IterableOnce [A ])
178+ // we're using Vector.newBuilder to build the result efficiently
179+ inline def splitBy (sep : A ) =
180+ val (b, cur) = (Vector .newBuilder[Vector [A ]], Vector .newBuilder[A ]) // b stores the result, cur stores the current chunk
181+ for e <- xs.iterator do
182+ if e != sep then cur += e // if current element is not the separator, add it to the current chunk
183+ else { b += cur.result(); cur.clear() } // else, append the current chunk to result and clear it
184+ (b += cur.result()).result() // finally, append the last chunk and return the result
185+ ```
186+
187+ ``` scala
188+ def part2 (input : String ) =
189+ val lines = input.linesIterator.toVector // get list of lines
190+ val ops = lines.last.split(raw " \s+ " ).toVector // we'll use them later
191+ lines.init.transpose.map(_.mkString.trim).splitBy(" " )
192+
193+ // Vector(
194+ // Vector(1, 24, 356),
195+ // Vector(369, 248, 8),
196+ // Vector(32, 581, 175),
197+ // Vector(623, 431, 4)
198+ // )
199+ ```
200+
201+ Reusing the ` calculate ` extension method from part 1, we can now finish part 2:
202+
203+ ``` scala
204+ def part2 (input : String ): Long =
205+ val lines = input.linesIterator.toVector
206+ val ops = lines.last.split(raw " \s+ " ).toVector
207+ val xss = lines.init.transpose.map(_.mkString.trim).splitBy(" " )
208+
209+ (ops lazyZip xss) // zip the operations with the chunks, lazily for efficiency
210+ .calculate
211+ ```
212+
213+ ## Final Code
214+
215+ ``` scala
216+ extension [A ](xs : IterableOnce [A ])
217+ inline def splitBy (sep : A ) =
218+ val (b, cur) = (Vector .newBuilder[Vector [A ]], Vector .newBuilder[A ])
219+ for e <- xs.iterator do
220+ if e != sep then cur += e else { b += cur.result(); cur.clear() }
221+ (b += cur.result()).result()
222+
223+ extension (xs : IterableOnce [(symbol : String , nums : IterableOnce [String ])])
224+ inline def calculate : Long = xs.iterator.collect {
225+ case (" *" , nums) => nums.iterator.map(_.toLong).product
226+ case (" +" , nums) => nums.iterator.map(_.toLong).sum
227+ }.sum
228+
229+ def part1 (input : String ): Long = input.linesIterator.toVector
230+ .map(_.trim.split(raw " \s+ " ))
231+ .transpose
232+ .iterator
233+ .map { col => (col.last, col.view.init) }
234+ .calculate
235+
236+ def part2 (input : String ): Long =
237+ val lines = input.linesIterator.toVector
238+ val ops = lines.last.split(raw " \s+ " ).toVector
239+ val xss = lines.init.transpose.map(_.mkString.trim).splitBy(" " )
240+
241+ (ops lazyZip xss).calculate
242+ ```
243+
9244## Solutions from the community
10245
11246Share your solution to the Scala community by editing this page.
0 commit comments