Powered by AppSignal & Oban Pro

Advent of code 2025 - Day 5

day05.livemd

Advent of code 2025 - Day 5

Description

Day 5 - Cafeteria

defmodule Load do
  def input do
    File.read!("#{__DIR__}/inputs/day05.txt")
  end
end
defmodule Day5 do
  defp parse(input) do
    [ranges, ids] =
      input
      |> String.split("\n\n")

    {
      ranges
      |> String.trim()
      |> String.split("\n")
      |> Enum.map(fn s -> String.split(s, "-") |> Enum.map(&String.to_integer/1) end),
      ids
      |> String.trim()
      |> String.split("\n")
      |> Enum.filter(&(&1 != ""))
      |> Enum.map(&String.to_integer/1)
    }
  end

  def part1(input) do
    {ranges, ids} = parse(input)

    ids
    |> Enum.reduce(0, fn id, acc ->
      if Enum.any?(ranges, fn [s, e] -> id >= s &amp;&amp; id <= e end) do
        acc + 1
      else
        acc
      end
    end)
  end

  def merge_ranges(ranges) do
    ranges
    |> Enum.sort_by(fn [s, _e] -> s end)
    |> Enum.reduce([], fn [s, e], acc ->
      case acc do
        [] -> [[s, e]]
        [[prev_s, prev_e] | rest] when s <= prev_e + 1 ->
          [[prev_s, max(prev_e, e)] | rest]
        _ -> [[s, e] | acc]
      end
    end)
  end
  
  def part2(input) do
    {ranges, _ids} = parse(input)

    merge_ranges(ranges)
    |> Enum.reduce(0, fn [s, e], acc -> acc + (e - s + 1) end)
  end
end
ExUnit.start(autorun: false)

defmodule Test do
  use ExUnit.Case, async: true

  @input """
  3-5
  10-14
  16-20
  12-18

  1
  5
  8
  11
  17
  32  
  """

  test "part 1" do
    assert Day5.part1(@input) == 3
  end

  test "part 2" do
    assert Day5.part2(@input) == 14
  end
end

ExUnit.run()
Day5.part1(Load.input())
Day5.part2(Load.input())