KeithFrost

KeithFrost

Advent of Code 2025 - Day 3

2025 Dec 03

Lobby

defmodule Joltage do
  def parse_line(s) do
    String.trim(s)
      |> to_charlist()
      |> Enum.map(fn ch ->
        if ch <= ?9 and ch >= ?0 do
          ch - ?0
        end
      end)
  end

  def parse(lines) do
    Enum.map(lines, &parse_line/1)
  end

  def max_prefix(bank) do
    [_last | rbank] = Enum.reverse(bank)
    Enum.max(rbank)
  end

  def max_joltage(bank) do
    max1 = max_prefix(bank)
    [_max1 | tail] = Enum.drop_while(bank, fn j -> j < max1 end)
    max1 * 10 + Enum.max(tail)
  end

  def sum_max_joltages(banks) do
    Enum.reduce(banks, 0, fn bank, sum ->
      sum + max_joltage(bank)
    end)
  end
end
test_banks = """
987654321111111
811111111111119
234234234234278
818181911112111
""" |> String.split("\n", trim: true)
  |> Joltage.parse()
  |> IO.inspect()
Enum.map(test_banks, &Joltage.max_joltage/1)
  |> IO.inspect(charlists: :as_lists)
Joltage.sum_max_joltages(test_banks)
input_banks = File.stream!(__DIR__ <> "/dec-03-input.txt")
  |> Joltage.parse()
Joltage.sum_max_joltages(input_banks)

Part Two

defmodule Joltage2 do
  def max_prefix(bank, n) do
    Enum.reverse(bank)
      |> Enum.drop(n - 1)
      |> Enum.max()
  end
  
  @batteries 12

  def max_joltage(bank, n \\ @batteries, acc \\ 0) do
    if n < 1 do
      acc
    else
      max1 = max_prefix(bank, n)
      [_max1 | tail] = Enum.drop_while(bank, fn j -> j < max1 end)
      max_joltage(tail, n - 1, acc * 10 + max1)
    end
  end

  def sum_max_joltages(banks) do
    Enum.reduce(banks, 0, fn bank, sum ->
      sum + max_joltage(bank)
    end)
  end
end
Enum.map(test_banks, &Joltage2.max_joltage/1)
  |> IO.inspect(charlists: :as_lists)
Joltage2.sum_max_joltages(test_banks)
Joltage2.sum_max_joltages(input_banks)

Most Liked

hauleth

hauleth

Parse

batteries =
  puzzle_input
  |> String.split("\n", trim: true)
  |> Enum.map(fn row ->
    row
    |> String.to_charlist()
    |> Enum.map(& &1 - ?0)
  end)

Implementation

defmodule Joltage do
  def make_largest(list, n) do
    to_remove = length(list) - n
    Enum.reduce(1..to_remove, list, fn _, acc -> make_larger(acc) end)
  end
  
  def make_larger([_]), do: []
  def make_larger([a, b | rest]) when a < b, do: [b | rest]
  def make_larger([b | rest]), do: [b | make_larger(rest)]
end

Part 1

Enum.sum_by(batteries, &Integer.undigits(Joltage.make_largest(&1, 2)))

Part 2

Enum.sum_by(batteries, &Integer.undigits(Joltage.make_largest(&1, 12)))

All thanks to simple observation that to make number larger you need to drop first digit that is smaller than the next one or last digit. Now apply it N times to be left with just required amount of digits and you are good to go.

10
Post #7
vkryukov

vkryukov

The algorithm is pretty straightforward: to take k largest batteries from a list of n, take the earliest largest digit among the first (n - k + 1), then iterate for all the digits after that one.

defmodule Y2025.Day03 do
  def digits(s) do
    s
    |> String.graphemes()
    |> Enum.map(&String.to_integer(&1))
  end

  def first_digit(digits, k) do
    n = length(digits)

    digits
    |> Enum.take(n - k + 1)
    |> Enum.with_index()
    |> Enum.max_by(fn {a, i} -> {a, -i} end)
  end

  def max_digits(digits, k) do
    k..1//-1
    |> Enum.reduce({digits, 0}, fn k, {digits, acc} ->
      {d, pos} = first_digit(digits, k)
      {digits |> Enum.drop(pos + 1), acc * 10 + d}
    end)
    |> elem(1)
  end

  def max_joltage(s, k \\ 2) do
    digits(s)
    |> max_digits(k)
  end

  def part1(s, k \\ 2) do
    s
    |> String.split("\n")
    |> Enum.map(&max_joltage(&1, k))
    |> Enum.sum()
  end

  def part2(s) do
    part1(s, 12)
  end
end
BartOtten

BartOtten

Here we go.

Who knows?
How to do a ‘selective capture’?

The reducer passes 2 arguments to the anonymous function. Thought I could simply do (&2) but that is not allowed. &1 has to be used. The dirty trick is to (&2 || &1) when you are certain &2 will never be falsy but it is stretching the limits.

Could have gone with a simple fn but am wondering if someone knows a nice solid trick.

defmodule Aoc2025.Solutions.Y25.Day03 do
  alias AoC.Input

  def parse(input, _part) do
    Input.read!(input)
    |> String.trim()
    |> String.split("\n")
    |> Enum.map(&String.to_charlist/1)
  end

  def part_one(problem) do
    solve(problem, 2)
  end

  def part_two(problem) do
    solve(problem, 12)
  end

  def solve(problem, limit) do
    problem
    |> Stream.map(&keep_highest(&1, limit))
    |> Stream.map(&to_string/1)
    |> Stream.map(&String.to_integer/1)
    |> Enum.sum()
  end

  def keep_highest(bank, limit) do
      discard = length(bank) - limit
      Enum.reduce(1..discard, bank, &maximize/2)
  end

  def maximize(_reduction, bank), do: maximize(bank)
  def maximize([x, s]), do: [max(x, s)]
  def maximize([l, r | rest]) when l < r, do: [r | rest]
  def maximize([l, r | rest]), do: [l | maximize([r | rest])]
end

Edit 1: You can play “spot the differences” with @hauleth solution :slight_smile:

Last Post!

billylanchantin

billylanchantin

defmodule Day03 do
  def part1(file), do: main(file, 2)
  def part2(file), do: main(file, 12)

  def main(file, n) do
    file
    |> Util.file_to_lists_of_ints()
    |> Enum.map(fn [int] -> Integer.digits(int) end)
    |> Enum.sum_by(&max_joltage(&1, n, 0))
  end

  def max_joltage(_digits, 0, max), do: max
  def max_joltage(digits, n, max) do
    x = digits |> Enum.reverse() |> Enum.drop(n - 1) |> Enum.reverse() |> Enum.max()
    {_, [_ | rest]} = Enum.split_while(digits, &(&1 < x))
    max_joltage(rest, n - 1, max + 10 ** (n - 1) * x)
  end
end

Where Next?

Popular in Challenges Top

connorlay
Note by the Moderators: This topic is for general discussion about the Advent of Code 2018. To prevent people from being spoiled about s...
New
bjorng
Note: This topic is to talk about Day 3 of the Advent of Code 2019 . There is a private leaderboard for elixirforum members. You can jo...
New
QuinnWilton
Note: This topic is to talk about Day 7 of the Advent of Code 2019 . There is a private leaderboard for elixirforum members. You can joi...
New
bjorng
Note: This topic is to talk about Day 18 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can joi...
New
maennchen
Ok, that was a rough one today. I haven’t found a way to improve the algorithm further. Part 1 runs in .5 seconds, Part 2 in ~ 5 minutes...
New
shritesh
I mapped both the cards and every possible hand to numeric values and sorted them. In part 2 I could only think of replacing the jokers w...
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

Other popular topics Top

KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36654 110
New
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 49084 226
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New

We're in Beta

About us Mission Statement