bjorng

bjorng

Erlang Core Team

Advent of Code 2025 - Day 5

Easy puzzles two days in a row. I suspect tomorrow’s puzzle will not be that easy…

defmodule Day05 do
  def part1(input) do
    {ranges, ingredients} = parse(input)
    Enum.count(ingredients, &in_any_range?(&1, ranges))
  end

  defp in_any_range?(ingredient, ranges) do
    Enum.any?(ranges, &(ingredient in &1))
  end

  def part2(input) do
    {ranges, _ingredients} = parse(input)
    ranges = ranges
    |> Enum.sort
    |> combine_ranges
    Enum.reduce(ranges, 0, &(Range.size(&1) + &2))
  end

  defp combine_ranges([r]), do: [r]
  defp combine_ranges([r1, r2 | rest]) do
    case Range.disjoint?(r1, r2) do
      true ->
        [r1 | combine_ranges([r2 | rest])]
      false ->
        r = min(r1.first, r2.first) .. max(r1.last, r2.last)
        combine_ranges([r | rest])
    end
  end

  defp parse([ranges, ingredients]) do
    ranges = Enum.map(ranges, fn range ->
      [first, last] = String.split(range, "-")
      String.to_integer(first) .. String.to_integer(last)
    end)
    ingredients = Enum.map(ingredients, &String.to_integer(&1))
    {ranges, ingredients}
  end
end

Most Liked

hauleth

hauleth

That is why I created RangeSet:

Parse

[fresh, ingridients] = String.split(puzzle_input, "\n\n")

fresh =
  fresh
  |> String.split()
  |> Enum.map(fn range ->
    [a, b] = range |> String.split("-") |> Enum.map(&String.to_integer/1)

    a..b//1
  end)
  |> RangeSet.new()

ingridients =
  ingridients
  |> String.split()
  |> Enum.map(&String.to_integer/1)

Part 1

Enum.count(ingridients, & &1 in fresh)

Part 2

Enum.count(fresh)
mudasobwa

mudasobwa

Creator of Cure

For the first time I needed a helper private function with multiple heads to handle a recursion properly.

  defmodule Day5 do
    @input "day5_1.input" |> File.read!() |> String.split("\n\n", trim: true)

    def calc([ranges, ids] \\ @input) do
      ranges =
        ranges
        |> String.split(["\s", "\n"], trim: true)
        |> Enum.map(&String.split(&1, "-", trim: true))
        |> Enum.map(fn [b, e] -> String.to_integer(b)..String.to_integer(e)//1 end)

      ids
      |> String.split(["\s", "\n"], trim: true)
      |> Enum.reduce({0, []}, fn id, {count, ids} ->
        id = String.to_integer(id)

        Enum.reduce_while(ranges, {count, ids}, fn range, {count, ids} ->
          if id in range, do: {:halt, {count + 1, [id | ids]}}, else: {:cont, {count, ids}}
        end)
      end)
    end

    def in_ranges([ranges, _ids] \\ @input) do
      ranges
      |> String.split(["\s", "\n"], trim: true)
      |> Enum.map(&String.split(&1, "-", trim: true))
      |> Enum.map(fn range -> Enum.map(range, &String.to_integer/1) end)
      |> Enum.sort()
      |> merge([])
      |> Enum.sum_by(fn [f, l] -> l - f + 1 end)
    end

    defp merge([], acc), do: Enum.reverse(acc)

    defp merge([range | rest], []), do: merge(rest, [range])

    defp merge([[f2, l2] | rest], [[f1, l1] | acc]) do
      if f2 <= l1 + 1 do
        merged = [f1, max(l1, l2)]
        merge(rest, [merged | acc])
      else
        merge(rest, [[f2, l2], [f1, l1] | acc])
      end
    end
  end
vkryukov

vkryukov

Yep, today was an easy one; parsing takes almost more lines than the rest of the code :).

defmodule Y2025.Day05 do
  def fresh?(_id, []), do: false

  def fresh?(id, [{a, b} | rest]) do
    cond do
      id < a -> false
      a <= id && id <= b -> true
      true -> fresh?(id, rest)
    end
  end

  def parse(s) do
    [ranges, ids] = String.split(s, "\n\n")

    ranges =
      ranges
      |> String.split("\n")
      |> Enum.map(fn line ->
        [a, b] = String.split(line, "-")
        {String.to_integer(a), String.to_integer(b)}
      end)
      |> Enum.sort() # <- important for fresh? and do_part2 to work!

    ids = ids |> String.split("\n") |> Enum.map(&String.to_integer/1)

    {ranges, ids}
  end

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

    ids
    |> Enum.filter(fn id -> fresh?(id, ranges) end)
    |> length()
  end

  def part2(s) do
    {ranges, _} = parse(s)

    do_part2(ranges, 0)
  end

  def do_part2([], n), do: n
  def do_part2([{a, b}], n), do: n + b - a + 1

  def do_part2([{a, b}, {c, d} | rest], n) do
    cond do
      b < c -> do_part2([{c, d} | rest], n + b - a + 1)
      b >= d -> do_part2([{a, b} | rest], n)
      # b >= c & b < d
      true -> do_part2([{a, d} | rest], n)
    end
  end
end

Where Next?

Popular in Challenges Top

sasajuric
Note: This topic is to talk about Day 12 of the Advent of Code. For general discussion about the Advent of Code 2018 and links to topics...
New
dominicletz
This topic is about Day 8 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/le...
New
sneako
Note by the Moderators: This topic is to talk about the first day of the Advent of Code. For general discussion about the Advent of Code...
New
bjorng
My solution finishes both parts in 5 seconds on my computer. That time should be possible to reduce by optimizing my rather naive tilt/2 ...
New
bjorng
Note: This topic is to talk about Day 16 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can joi...
New
code-shoily
Here’s my day 3 code https://github.com/code-shoily/advent_of_code/blob/master/lib/2024/day_03.ex This was quite easy. I was afraid Par...
New
seeplusplus
Hello all, hopefully I post this before someone else does and I don’t dupe. IMO Day 4 was much easier than Day 3 (yay, I can sleep befor...
New
Aetherus
Don’t know why the regex ~r/[\W &amp;&amp; [^\.]]/x does not work in Elixir. It works pretty well in Ruby. Anyway, here is my solution: ...
New
rugyoga
Fairly straightforward Dijkstra’s algorithm import AOC aoc 2023, 17 do def compute(input, candidates) do {{max_row, max_col}, ite...
New
christhekeele
Thought I’d kick today’s thread off! Parsing Enum rocks, so most of my code was actually in parsing input. ▶ Preprocessing input Part 1...
New

Other popular topics Top

9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30877 112
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 52341 488
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
marick
I had some trouble figuring out how to make many-to-many associations work. Once I got it working, I wrote a blog post. Because I’m a nov...
New

We're in Beta

About us Mission Statement