Powered by AppSignal & Oban Pro

Day 3 - Advent of Code 2025

lib/advent_of_code/2025/day-03.livemd

Day 3 - Advent of Code 2025

Mix.install([:kino, :benchee])

Links

Prompt

— Day 3: Lobby —

You descend a short staircase, enter the surprisingly vast lobby, and are quickly cleared by the security checkpoint. When you get to the main elevators, however, you discover that each one has a red light above it: they’re all offline.

“Sorry about that,” an Elf apologizes as she tinkers with a nearby control panel. “Some kind of electrical surge seems to have fried them. I’ll try to get them online soon.”

You explain your need to get further underground. “Well, you could at least take the escalator down to the printing department, not that you’d get much further than that without the elevators working. That is, you could if the escalator weren’t also offline.”

“But, don’t worry! It’s not fried; it just needs power. Maybe you can get it running while I keep working on the elevators.”

There are batteries nearby that can supply emergency power to the escalator for just such an occasion. The batteries are each labeled with their joltage rating, a value from 1 to 9. You make a note of their joltage ratings (your puzzle input). For example:

987654321111111
811111111111119
234234234234278
818181911112111

The batteries are arranged into banks; each line of digits in your input corresponds to a single bank of batteries. Within each bank, you need to turn on exactly two batteries; the joltage that the bank produces is equal to the number formed by the digits on the batteries you’ve turned on. For example, if you have a bank like 12345 and you turn on batteries 2 and 4, the bank would produce 24 jolts. (You cannot rearrange batteries.)

You’ll need to find the largest possible joltage each bank can produce. In the above example:

  • In 987654321111111, you can make the largest joltage possible, 98, by turning on the first two batteries.
  • In 811111111111119, you can make the largest joltage possible by turning on the batteries labeled 8 and 9, producing 89 jolts.
  • In 234234234234278, you can make 78 by turning on the last two batteries (marked 7 and 8).
  • In 818181911112111, the largest joltage you can produce is 92.

The total output joltage is the sum of the maximum joltage from each bank, so in this example, the total output joltage is 98 + 89 + 78 + 92 = 357.

There are many batteries in front of you. Find the maximum joltage possible from each bank; what is the total output joltage?

To begin, get your puzzle input.

— Part Two —

The escalator doesn’t move. The Elf explains that it probably needs more joltage to overcome the static friction of the system and hits the big red “joltage limit safety override” button. You lose count of the number of times she needs to confirm “yes, I’m sure” and decorate the lobby a bit while you wait.

Now, you need to make the largest joltage by turning on exactly twelve batteries within each bank.

The joltage output for the bank is still the number formed by the digits of the batteries you’ve turned on; the only difference is that now there will be 12 digits in each bank’s joltage output instead of two.

Consider again the example from before:

987654321111111
811111111111119
234234234234278
818181911112111

Now, the joltages are much larger:

  • In 987654321111111, the largest joltage can be found by turning on everything except some 1s at the end to produce 987654321111.
  • In the digit sequence 811111111111119, the largest joltage can be found by turning on everything except some 1s, producing 811111111119.
  • In 234234234234278, the largest joltage can be found by turning on everything except a 2 battery, a 3 battery, and another 2 battery near the start to produce 434234234278.
  • In 818181911112111, the joltage 888911112111 is produced by turning on everything except some 1s near the front.

The total output joltage is now much larger: 987654321111 + 811111111119 + 434234234278 + 888911112111 = 3121910778619.

What is the new total output joltage?

Although it hasn’t changed, you can still get your puzzle input.

Input

input = Kino.Input.textarea("Please paste your input file:")
input = input |> Kino.Input.read()
"987654321111111\n811111111111119\n234234234234278\n818181911112111"

Solution

defmodule Day03 do
  defdelegate parse(input), to: __MODULE__.Input

  def part1(input) do
    input
    |> parse()
    |> Enum.map(&joltage/1)
    |> Enum.map(&String.to_integer/1)
    |> Enum.sum()
  end

  def part2(input) do
    input
    |> parse()
    |> Enum.map(&joltage_p2/1)
    |> Enum.map(&String.to_integer/1)
    |> Enum.sum()
  end
  
  defp joltage([a | rest]) do
    joltage({a, ?0, rest})
  end

  defp joltage({a, b, []}) do
    [a, b] |> to_string()
  end
  
  defp joltage({a, _, [c | rest]}) when c > a and rest != [] do
    joltage([c | rest])
  end

  defp joltage({a, b, [c | rest]}) when c > b do
    joltage({a, c, rest})
  end

  defp joltage({a, b, [_ | rest]}) do
    joltage({a, b, rest})
  end

  defp joltage_p2(list) when is_list(list) do
    list
    |> Enum.reverse()
    |> Enum.split(12)
    |> then(fn {a, b} ->
      joltage_p2({Enum.reverse(a), b})
    end)
  end

  defp joltage_p2({a, []}), do: to_string(a)

  defp joltage_p2({[a | tail_a], [b | tail_b]}) when b >= a do
    {
      replace_value([a, b], tail_a),
      tail_b
    }
    |> joltage_p2()
  end

  defp joltage_p2({a, [_ | tail_b]}) do
    joltage_p2({a, tail_b})
  end

  defp replace_value([a | head], [b | _] = tail) when b > a do
    Enum.reverse(head) ++ tail
  end

  defp replace_value([_ | tail], []) do
    Enum.reverse(tail)
  end

  defp replace_value(a, [b | tail]) do
    replace_value([b | a], tail)
  end
  

  defmodule Input do
    def parse(input) when is_binary(input) do
      input
      |> String.splitter("\n", trim: true)
      |> parse()
    end

    def parse(input) do
      Stream.map(input, &parse_line/1)
    end

    def parse_line(line) do
      String.to_charlist(line)
    end
  end
end
{:module, Day03, <<70, 79, 82, 49, 0, 0, 15, ...>>,
 {:module, Day03.Input, <<70, 79, 82, ...>>, {:parse_line, 1}}}

Find the maximum joltage possible from each bank; what is the total output joltage?

Your puzzle answer was 17301.

Day03.part1(input)
357

What is the new total output joltage?

Your puzzle answer was 172162399742349.

Day03.part2(input)
3121910778619

Both parts of this puzzle are complete! They provide two gold stars: **

At this point, you should return to your Advent calendar and try another puzzle.

If you still want to see it, you can get your puzzle input.

Tests

ExUnit.start(auto_run: false)

defmodule Day03Test do
  use ExUnit.Case, async: false

  setup_all do
    [
      input: "987654321111111\n811111111111119\n234234234234278\n818181911112111"
    ]
  end

  describe "part1/1" do
    test "returns expected value", %{input: input} do
      assert Day03.part1(input) == 357
    end
  end

  describe "part2/1" do
    test "returns expected value", %{input: input} do
      assert Day03.part2(input) == 3121910778619
    end
  end
end

ExUnit.run()
Running ExUnit with seed: 287164, max_cases: 28

..
Finished in 0.00 seconds (0.00s async, 0.00s sync)
2 tests, 0 failures
%{total: 2, failures: 0, excluded: 0, skipped: 0}

Benchmarking

defmodule Benchmarking do
  # https://github.com/bencheeorg/benchee
  def run(input) do
    Benchee.run(
      %{
        "Part 1" => fn -> Day03.part1(input) end,
        "Part 2" => fn -> Day03.part2(input) end
      },
      memory_time: 2,
      reduction_time: 2
    )

    nil
  end
end
{:module, Benchmarking, <<70, 79, 82, 49, 0, 0, 8, ...>>, {:run, 1}}
Benchmarking.run(input)
Operating System: macOS
CPU Information: Apple M4 Pro
Number of Available Cores: 14
Available memory: 48 GB
Elixir 1.18.3
Erlang 27.3
JIT enabled: true

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 2 s
reduction time: 2 s
parallel: 1
inputs: none specified
Estimated total run time: 22 s
Excluding outliers: false

Benchmarking Part 1 ...
Benchmarking Part 2 ...
Calculating statistics...
Formatting results...

Name             ips        average  deviation         median         99th %
Part 1       10.04 K       99.63 μs    ±10.81%       97.50 μs      127.71 μs
Part 2        6.12 K      163.49 μs    ±10.68%      159.87 μs      227.16 μs

Comparison: 
Part 1       10.04 K
Part 2        6.12 K - 1.64x slower +63.86 μs

Memory usage statistics:

Name           average  deviation         median         99th %
Part 1         0.97 MB     ±0.00%        0.97 MB        0.97 MB
Part 2         1.47 MB     ±0.00%        1.47 MB        1.47 MB

Comparison: 
Part 1         0.97 MB
Part 2         1.47 MB - 1.51x memory usage +0.50 MB

Reduction count statistics:

Name   Reduction count
Part 1         32.99 K
Part 2         50.47 K - 1.53x reduction count +17.48 K

**All measurements for reduction count were the same**
nil