Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Advent of Code 2022 - Day 1

aoc2022-01.livemd

Advent of Code 2022 - Day 1

Puzzle description

Day 1: Calorie Counting.

Input

defmodule Input do
  def load! do
    "input/01.txt"
    |> Path.expand(__DIR__)
    |> File.stream!()
  end

  @doc """
  Returns normalized output when generating streams from files or string input.
  """
  def to_stream(input) when is_binary(input) do
    input
    |> String.trim("\n")
    |> String.splitter("\n")
  end

  def to_stream(stream) do
    stream
    |> Stream.map(fn string -> String.trim_trailing(string, "\n") end)
  end
end

Solution

defmodule Part1 do
  # preflight:
  ## split string into lines (without break characters)
  def run(input) do
    # 1) transform input to grouped totals
    # 2) return the max of the totals
    input
    |> to_grouped_sums()
    |> Enum.max()
  end

  def to_grouped_sums(input) do
    input
    |> Stream.transform(
      fn -> [] end,
      fn
        ## if the item is the empty string (""), then:
        ### add the accumulated items to the new stream
        ### reset the accumulator to the empty list
        "", acc ->
          {[acc], []}

        item, acc ->
          ## otherwise, add the item to the head of the accumulator
          {[], [String.to_integer(item) | acc]}
      end,
      fn
        [] -> {:halt, []}
        acc -> {[acc], []}
      end,
      fn _ -> :ok end
    )
    |> Stream.map(&Enum.sum/1)
  end
end

defmodule Part2 do
  # preflight:
  ## split string into lines (without break characters)
  def run(input) do
    # 1) transform input to grouped totals
    # 2) sort totals descending (sort + reverse)
    # 3) take the top 3 totals
    # 4) return the sum of the top 3 totals
    input
    |> Part1.to_grouped_sums()
    |> Enum.into([])
    |> Enum.sort()
    |> Enum.reverse()
    |> Enum.take(3)
    |> Enum.sum()
  end
end

ExUnit.start(autorun: false)

defmodule Test do
  use ExUnit.Case, async: true
  @example_input ~s(
1000
2000
3000

4000

5000
6000

7000
8000
9000

10000
)

  @input Input.load!()

  test "part 1" do
    assert @example_input |> Input.to_stream() |> Part1.run() === 24000

    @input |> Input.to_stream() |> Part1.run() |> dbg()
  end

  test "part 2" do
    assert @example_input |> Input.to_stream() |> Part2.run() === 45000

    @input |> Input.to_stream() |> Part2.run() |> dbg()
  end
end

ExUnit.configure(trace: true)
ExUnit.run()