Day 1: Sonar Sweep
by @adpi2
Puzzle description
https://adventofcode.com/2021/day/1
Solution of Part 1
The first step is to transform the input to a sequence of integers. We can do so with:
val depths: Seq[Int] = input.linesIterator.map(_.toInt).toSeq
The second step and hardest challenge of this puzzle is to compute all pairs of consecutive depth measurments.
For each index i
from 0 until depths.size - 1
we can create a pair of the depths at index i
and i + 1
.
val pairs: Seq[(Int, Int)] =
for i <- 0 until depths.size - 1
yield (depths(i), depths(i + 1))
0 until n
is an exclusive range, it does not contain the upper boundn
.0 to n
is an inclusive range, it contains the upper boundn
.
For the input Seq(10, 20, 30, 40)
, pairs is Seq((10,20), (20, 30), (30, 40))
.
Then we can count the pairs whose first element is smaller than its second element.
pairs.count((first, second) => first < second)
That gives us:
def part1(input: String): Int =
val depths: Seq[Int] = input.linesIterator.map(_.toInt).toSeq
val pairs: Seq[(Int, Int)] =
for i <- 0 until depths.size - 1
yield (depths(i), depths(i + 1))
pairs.count((first, second) => first < second)
Solution of Part 2
In the second part we need to compute the sums of all consecutive three elements.
We can use a similar approach to part 1.
val sums: Seq[Int] =
for i <- 0 until depths.size - 2
yield depths(i) + depths(i + 1) + depths(i + 2)
Notice that we can sum the three elements in the yield
part of the for
comprehension.
The remaining code of this second puzzle is very similar to what we did in part 1.
Generalization to sliding
method
In part 1 we computed all pairs of consecutive elements. In part 2 we computed the sums of all consecutive three elements.
In each case there is a notion of sliding window of size n, where n is 2 or 3.
For example, the sliding window of size 3 of Seq(10, 20, 30, 40, 50)
is:
Seq(Seq(10, 20, 30), Seq(20, 30, 40), Seq(30, 40, 50))
.
We can generalize this procedure in a method that compute a sliding window of some size n on any sequence of elements.
Such a method already exists in the Scala standard library under the name sliding
. It returns an iterator of arrays.
$ Seq(10, 20, 30, 40, 50).sliding(3).toSeq
Seq(Array(10, 20, 30), Array(20, 30, 40), Array(30, 40, 50))
We can use the sliding method to make our code shorter and faster.
Final solution
def part1(input: String): Int =
val depths = input.linesIterator.map(_.toInt)
val pairs = depths.sliding(2).map(arr => (arr(0), arr(1)))
pairs.count((prev, next) => prev < next)
def part2(input: String): Int =
val depths = input.linesIterator.map(_.toInt)
val sums = depths.sliding(3).map(_.sum)
val pairs = sums.sliding(2).map(arr => (arr(0), arr(1)))
pairs.count((prev, next) => prev < next)
Run it locally
You can get this solution locally by cloning the scalacenter/scala-advent-of-code repository.
$ git clone https://github.com/scalacenter/scala-advent-of-code
$ cd scala-advent-of-code
The you can run it with scala-cli:
$ scala-cli 2021 -M day1.part1
The answer is 1559
$ scala-cli 2021 -M template1.part2
The answer is 1600
You can replace the content of the input/day1
file with your own input from adventofcode.com to get your own solution.
Run it in the browser
Part 1
Part 2
Bonus
There is a trick to make the solution of part 2 even smaller.
Indeed comparing depths(i) + depths(i + 1) + depths(i + 2)
with depths(i + 1) + depths(i + 2) + depths(i + 3)
is the same as comparing depths(i)
with depths(i + 3)
.
So the second part of the puzzle is almost the same as the first part of the puzzle.
Solutions from the community
- Solution of @tgodzik.
- Solution of @otobrglez.
- Solution of @s5bug using cats-effect and fs2
- Solution of @keynmol using C APIs on Scala Native
- Solution of @erdnaxeli.
- Solution of @tOverney.
- Solution of @rpiotrow using ZIO
- Solution by @philip_schwarz with and without Cats
- Solution of @FlorianCassayre.
- Solution with video tutorial by @justinhj
- Solution of Jan Boerman.
- Solution by Rui Alves
Share your solution to the Scala community by editing this page.