Powered by AppSignal & Oban Pro

Day 1 - Sonar Sweep

2021/day01.livemd

Day 1 - Sonar Sweep

Part 1

defmodule Setup do
  ## parsing the input, converting it to a list of integers
  def parse_input(prompt) do
    case IO.gets(prompt) do
      :eof -> []
      line -> [line |> String.trim() |> String.to_integer() | parse_input(prompt)]
    end
  end
end

input = Setup.parse_input("Input")
defmodule DepthMeasurements do
  def count_increments(measurements) do
    measurements
    # create shifted value tuples: {previous, current}
    |> Enum.zip(tl(measurements))
    # count when the current reading is bigger than the previous
    |> Enum.count(fn {previous, current} -> current > previous end)
  end
end
DepthMeasurements.count_increments(input)

Part 2

defmodule SlidingWindow do
  @moduledoc "create tri-sums with Enum.chunk_while to iterate over the list in triplets"

  defp chunk_fun(a, [b, c]) do
    # emit the tri-sum and create the next triplet
    {:cont, a + b + c, [a, b]}
  end

  defp chunk_fun(e, acc) do
    # collect until we have a three readings
    {:cont, [e | acc]}
  end

  defp after_fun(_) do
    # no new element in the list, ignore the remainder
    {:cont, []}
  end

  def trisums(input) do
    Enum.chunk_while(input, [], &chunk_fun/2, &after_fun/1)
  end
end
input
|> SlidingWindow.trisums()
|> DepthMeasurements.count_increments()

Bonus - Quick

defmodule QuickSolve do
  def solve(list, acc \\ 0)
  def solve([a, _, _, b | _] = list, acc) when b > a, do: solve(tl(list), acc + 1)
  def solve([_, _, _, _ | _] = list, acc), do: solve(tl(list), acc)
  def solve(_, acc), do: acc
end
QuickSolve.solve(input)
defmodule ChunkSolve do
  def solve(input) do
    input
    |> Enum.chunk_every(3, 1, :discard)
    |> Enum.map(&Enum.sum/1)
    |> DepthMeasurements.count_increments()
  end
end
ChunkSolve.solve(input)

Tests

ExUnit.start()

defmodule Tests do
  use ExUnit.Case

  setup do
    {:ok, input: [199, 200, 208, 210, 200, 207, 240, 269, 260, 263]}
  end

  test "count_increments", %{input: input} do
    assert DepthMeasurements.count_increments(input) == 7
  end

  test "sliding window", %{input: input} do
    assert input |> SlidingWindow.trisums() |> DepthMeasurements.count_increments() == 5
  end

  test "quick solve", %{input: input} do
    assert QuickSolve.solve(input) == 5
  end

  test "chunk solve", %{input: input} do
    assert ChunkSolve.solve(input) == 5
  end
end

ExUnit.run()

Benchmarking

Mix.install([:benchee])
Benchee.run(%{
  "QuickSolve" => fn -> QuickSolve.solve(input) end,
  "SlidingWindow" => fn ->
    SlidingWindow.trisums(input)
    |> DepthMeasurements.count_increments()
  end,
  "ChunkSolve" => fn -> ChunkSolve.solve(input) end
})

:ok