Day 1: Sonar Sweep

Puzzle Description

Advent of Code 2021, Day 1

Scala Center Advent of Code 2021, Day 1

The first two days I actually wrote my solution in Elixir, but I quickly got tired of it. As a treat though, I've updated my Elixir code to a more sensible solution so it can stand alongside my Scala.

Solution Summary

  1. Parse input into List[Int]
  2. For part 2, calculate the sliding sums of this list.
  3. Count instances where a number is greater than its previous number

Part 1

Parsing is easy (in Scala, at least). Elixir forces you to always handle errors, but frankly I couldn't care less here.

def parse(str: String): List[Int] =
    str.linesIterator.map(_.toInt).toList

All Elixir functions will be inside defmodule Day1.

defmodule Day1 do
  def parse_int!(str) do
    case Integer.parse(str) do
      { v, "" } -> v
      :error -> nil
    end
  end

  def parse(str) do
    for line <- String.split(String.trim(str), "\n"), 
    line != "", 
    do: Day1.parse_int!(String.trim(line))
  end
end

Let's then do an easy measurement. It's just a sliding window:

def measureScans(scans: List[Int]): Int =
 scans.sliding(2).count: ls =>
   ls.head < ls.tail.head
# inside Day1...
def measurement_basic(list) do
  Enum.chunk_every(list, 2, 1, :discard) 
    |> Enum.count(fn a -> Enum.at(a, 0) < Enum.at(a, 1) end)
end

And hook it up:

def part1(input: List[Int]): Int =
  measureScans(input)
def part1(list) do
  measurement_basic(list)
end

And that's part 1.

Part 2

Part 2 is easy - we convert the input list into a new list then rerun part 1 on this new list.

Let's convert the list with sliding sums:

def slidingSums(scans: List[Int]): List[Int] =
  scans.sliding(3).map(_.sum).toList
def sliding_sum(list) do
  Enum.chunk_every(list, 3, 1, :discard)
    |> Enum.map(fn a -> Enum.sum(a) end)
end

Then hook it up:

def part2(input: List[Int]): Int =
  measureScans(slidingSums(input))
def part2(list) do
  list |> sliding_sum |> measurement_basic 
end

And that's part 2.

Final Code

def parse(str: String): List[Int] =
  str.linesIterator.map(_.toInt).toList

def measureScans(scans: List[Int]): Int =
  scans.sliding(2).count: ls =>
    ls.head < ls.tail.head
def part1(input: List[Int]): Int =
  measureScans(input)

def slidingSums(scans: List[Int]): List[Int] =
  scans.sliding(3).map(_.sum).toList

def part2(input: List[Int]): Int =
  measureScans(slidingSums(input))
defmodule Day1 do
  def parse_int!(str) do
    case Integer.parse(str) do
      { v, "" } -> v
      :error -> nil
    end
  end
  def measurement_basic(list) do
    Enum.chunk_every(list, 2, 1, :discard) 
      |> Enum.count(fn a -> Enum.at(a, 0) < Enum.at(a, 1) end)
  end
  def part1(list) do
    measurement_basic(list)
  end
  def sliding_sum(list) do
    Enum.chunk_every(list, 3, 1, :discard)
      |> Enum.map(fn a -> Enum.sum(a) end)
  end
  def part2(list) do
    list |> sliding_sum |> measurement_basic 
  end
  def parse(str) do
    for line <- String.split(String.trim(str), "\n"), 
      line != "", 
      do: Day1.parse_int!(String.trim(line))
  end
end

Benchmark

Part 1

Mean

Error

JVM

1.611 ms

+/- 0.208 ms

JS

2.566 ms

+/- 0.192 ms

Native

0.675 ms

+/- 0.092 ms

Part 2

Mean

Error

JVM

5.670 ms

+/- 0.331 ms

JS

5.893 ms

+/- 1.405 ms

Native

1.190 ms

+/- 0.041 ms

Run it in the browser!