bjorng

bjorng

Erlang Core Team
defmodule Day06 do
  def part1(input) do
    input
    |> Enum.map(fn line ->
      line
      |> String.split(" ", trim: true)
      |> Enum.map(fn token ->
        case Integer.parse(token) do
          :error -> String.to_atom(token)
          {n, ""} -> n
        end
      end)
    end)
    |> transpose
    |> Enum.map(fn column ->
      operator = List.last(column)
      Enum.drop(column, -1)
      |> Enum.reduce(&(eval(operator, &1, &2)))
    end)
    |> Enum.sum
  end

  def part2(input) do
    operators = List.last(input)
    spaces = count_spaces(String.to_charlist(operators))
    operators = operators
    |> String.to_charlist
    |> Enum.filter(&(&1 !== ?\s))
    |> Enum.map(&List.to_atom([&1]))

    input
    |> Enum.drop(-1)
    |> Enum.map(fn line ->
      String.to_charlist(line)
      |> split_line(spaces)
    end)
    |> transpose
    |> Enum.zip(operators)
    |> Enum.map(fn {column, operator} ->
      column
      |> transpose
      |> Enum.map(fn column ->
        column
        |> Enum.filter(&(&1 !== ?\s))
        |> List.to_integer
      end)
      |> Enum.reduce(&(eval(operator, &1, &2)))
    end)
    |> Enum.sum
  end

  defp split_line([], []), do: []
  defp split_line(line, [n]) do
    {number, []} = Enum.split(line, n + 1)
    [number]
  end
  defp split_line(line, [n | ns]) do
    {number, rest} = Enum.split(line, n)
    [?\s | rest] = rest
    [number | split_line(rest, ns)]
  end

  defp count_spaces([]), do: []
  defp count_spaces([_op|rest]) do
    {spaces, rest} = Enum.split_while(rest, &(&1 === ?\s))
    [length(spaces) | count_spaces(rest)]
  end

  defp eval(operator, number1, number2) do
    apply(Kernel, operator, [number1, number2])
  end

  defp transpose(list) do
    Enum.zip_with(list, &Function.identity/1)
  end
end

Showing Posts 1 to 10

Aetherus

Aetherus

Yet another easy puzzle.

Part 1

puzzle_input
|> String.split("\n", trim: true)
|> Enum.map(&String.split(&1, " ", trim: true))
|> Enum.map(fn row ->
  Enum.map(row, fn
    "+" -> &+/2
    "*" -> &*/2
    s -> String.to_integer(s)
  end)
end)
|> Enum.zip_with(&Function.identity/1)
|> Enum.map(&Enum.reverse/1)
|> Enum.sum_by(fn [operator | numbers] ->
  Enum.reduce(numbers, operator)
end)

Part 2

Split the puzzle input into [[char()]], then rotate left (= transpose then reverse).

puzzle_input
|> String.split("\n", trim: true)
|> Enum.map(&:binary.bin_to_list/1)
|> Enum.zip_with(&Function.identity/1)
|> Enum.reverse()
|> Enum.map(&to_string/1)
|> Enum.map(&String.trim/1)
|> Enum.map(&Integer.parse/1)
|> Enum.flat_map(fn
  :error ->
    []
  
  {num, ""} ->
    [num]

  {num, op} ->
    op
    |> String.trim()
    |> String.to_atom()
    |> then(fn op -> &apply(Kernel, op, [&1, &2]) end)
    |> then(&[num, &1])
end)
|> Enum.chunk_while([],
  fn
    num, acc when is_integer(num) -> {:cont, [num | acc]}
    op, acc -> {:cont, Enum.reduce(acc, op), []}
  end,
  fn
    [] -> {:cont, []}
  end
)
|> Enum.sum()
vkryukov

vkryukov

I’m using my Array module again: Array.col returns a column as a list, Array.subarray returns a subarray with given indexes, and Array.from_list is a constructor that takes a list of lists.

defmodule Y2025.Day06 do
  def parse_lines(s), do: s |> String.trim() |> String.split("\n") |> Enum.split(-1)
  def split_by_spaces(s), do: String.trim(s) |> String.split(~r/\s+/)

  def parse1(s) do
    {nums, [ops]} = parse_lines(s)

    nums =
      nums
      |> Enum.map(fn line ->
        line |> split_by_spaces() |> Enum.map(&String.to_integer/1)
      end)
      |> Array.from_list()

    ops = ops |> split_by_spaces()

    {nums, ops}
  end

  def part1(s) do
    {nums, ops} = parse1(s)

    ops
    |> Enum.with_index()
    |> Enum.map(fn {op, i} -> apply_op(op, Array.col(nums, i)) end)
    |> Enum.sum()
  end

  def apply_op("+", nums), do: Enum.sum(nums)
  def apply_op("*", nums), do: Enum.product(nums)

  def parse2(s) do
    {nums, [ops]} = parse_lines(s)

    nums =
      nums
      |> Enum.map(fn line -> String.graphemes(line) |> Enum.map(&maybe_to_digit/1) end)
      |> Array.from_list()

    ops = ops |> split_by_spaces()

    {nums, ops}
  end

  def maybe_to_digit(" "), do: " "
  def maybe_to_digit(d), do: String.to_integer(d)

  def part2(s) do
    {nums, ops} = parse2(s)

    nums =
      nums
      |> array_split_by_spaces()
      |> Enum.map(fn a -> cellophod_digits(a) end)

    Enum.zip(ops, nums)
    |> Enum.map(fn {op, num} -> if op == "*", do: Enum.product(num), else: Enum.sum(num) end)
    |> Enum.sum()
  end

  @doc "Split array of digits and spaces into a list of arrays separated by columns of spaces"
  def array_split_by_spaces(%Array{} = a) do
    do_array_split_by_spaces(a, 0, 0, [])
  end

  def do_array_split_by_spaces(a, i, j, acc) do
    cond do
      i >= a.x_max ->
        Enum.reverse(acc)

      j == a.x_max || all_spaces?(Array.col(a, j)) ->
        do_array_split_by_spaces(a, j + 1, j + 1, [Array.subarray(a, x: i..(j - 1)) | acc])

      true ->
        do_array_split_by_spaces(a, i, j + 1, acc)
    end
  end

  def all_spaces?(list), do: Enum.all?(list, &(&1 == " "))

  def cellophod_digits(%Array{} = a) do
    0..(a.x_max - 1)
    |> Enum.map(fn i -> Array.col(a, i) |> Enum.reject(&(&1 == " ")) end)
    |> Enum.map(&Integer.undigits/1)
  end
end
lkuty

lkuty

#!/usr/bin/env elixir

# Advent of Code 2025. Day 6

defmodule M do
  def i("*"), do: "*"
  def i("+"), do: "+"
  def i(x), do: String.to_integer(x)

  def n(a,b,c,d) do
    [a,b,c,d]
    |> Enum.reject(& &1 == " ")
    |> Enum.map(&String.to_integer/1)
    |> Integer.undigits()
  end
end

# Part 1
# Original solution, before knowing part 2

rows = File.read!("../day06.txt") |> String.trim_trailing() |> String.split("\n")
map = rows |> Enum.reduce(%{}, fn row, map ->
  row
  |> String.trim_trailing()
  |> String.split(~r/\s+/)
  |> Enum.with_index()
  |> Enum.reduce(map, fn {cell, col_idx}, map ->
    Map.update(
      map,
      col_idx,
      [M.i(cell)],
      # We could as well do the operations directly here and only store the result in the map at the given col_idx
      fn lst -> [(case cell do "*" -> &Enum.product/1 ; "+" -> &Enum.sum/1 ; _ -> M.i(cell) end) | lst] end
      # variant 1
      # fn lst -> [(case cell do "*" -> {1, fn a,b -> a*b end} ; "+" -> {0, fn a,b -> a+b end} ; _ -> M.i(cell) end) | lst] end
    )
  end)
end)

map
|> Enum.map(fn {_idx, [fun | numbers]} -> fun.(numbers) end)
# variant 1
# |> Enum.map(fn {_idx, [{id, fun} | numbers]} -> Enum.reduce(numbers, id, fun) end)
|> Enum.sum()
|> IO.inspect(label: "Day 6. Part 1")

# Part 2

File.read!("../day06.txt")
|> String.trim_trailing("\n")
|> String.split("\n")
|> Enum.map(&String.codepoints/1)
|> Enum.zip() # transpose the file
|> Enum.reduce({0, [], nil}, fn
     {" ", " ", " ", " ", " "}, {total, numbers, "+"} -> {total+Enum.sum(numbers), [], nil}
     {" ", " ", " ", " ", " "}, {total, numbers, "*"} -> {total+Enum.product(numbers), [], nil}
     {a,b,c,d," "}, {total, numbers, op} -> {total, [M.n(a,b,c,d) | numbers], op}
     {a,b,c,d,op}, {total, numbers, nil} -> {total, [M.n(a,b,c,d) | numbers], op}
   end)
|> then(fn
     {total,numbers,"+"} -> total+Enum.sum(numbers)
     {total,numbers,"*"} -> total+Enum.product(numbers)
   end)
|> IO.inspect(label: "Day 6. Part 2")

# Part 1 revisited

File.read!("../day06.txt")
|> String.trim_trailing("\n")
|> String.split("\n")
|> Enum.map(fn row -> row |> String.trim_trailing() |> String.split(~r/\s+/) |> Enum.map(&M.i/1) end)
|> Enum.zip() # transpose the file
|> Enum.reduce(0, fn
     {a,b,c,d,"+"}, total -> total+a+b+c+d
     {a,b,c,d,"*"}, total -> total+a*b*c*d
   end)
|> IO.inspect(label: "Day 6. Part 1")
hauleth

hauleth

Setup

{tasks, [ops]} =
  puzzle_input
  |> String.split("\n", trim: true)
  |> Enum.split(-1)

ops =
  ops
  |> String.split()
  |> Enum.map(&String.to_atom/1)

Part 1

tasks
|> Enum.map(&String.split/1)
|> Enum.zip_with(fn numbers -> Enum.map(numbers, &String.to_integer/1) end)
|> Enum.zip(ops)
|> Enum.sum_by(fn
  {nums, :+} -> Enum.sum(nums)
  {nums, :*} -> Enum.product(nums)
end)

Part 2

tasks
|> Enum.map(&String.to_charlist/1)
|> Enum.zip_with(&(&1 |> List.to_string() |> String.trim()))
|> Enum.chunk_while(
  [],
  fn
    "", acc -> {:cont, acc, []}
    num, acc -> {:cont, [String.to_integer(num) | acc]}
  end,
  &{:cont, &1, []}
)
|> Enum.zip(ops)
|> Enum.sum_by(fn
  {nums, :+} -> Enum.sum(nums)
  {nums, :*} -> Enum.product(nums)
end)
lud

lud

Arf I wish I had thought to use String.to_integer before chunking!

defmodule AdventOfCode.Solutions.Y25.Day06 do
  alias AoC.Input

  # No separate parsing today :)
  def parse(input, _), do: input

  def part_one(input) do
    input
    |> Input.stream!(trim: true)
    |> Stream.map(&String.split(&1, " ", trim: true))
    |> Enum.zip_with(& &1)
    |> Enum.sum_by(fn list ->
      {op, args} = List.pop_at(list, -1)
      apply_op(op, Enum.map(args, &String.to_integer/1))
    end)
  end

  def part_two(input) do
    rows = Input.read!(input) |> String.split("\n", trim: true)
    {ops, rows} = List.pop_at(rows, -1)
    rows = Enum.map(rows, &String.graphemes/1)
    ops = String.split(ops, " ", trim: true)

    rows
    |> Stream.unfold(fn
      [[] | _] ->
        nil

      rows ->
        {rows, col} = Enum.map_reduce(rows, [], fn [h | t], acc -> {t, [h | acc]} end)
        {:lists.reverse(col), rows}
    end)
    |> Stream.map(fn str_digits ->
      case str_digits |> Enum.join("") |> String.trim() do
        "" -> nil
        str_num -> String.to_integer(str_num)
      end
    end)
    |> Stream.chunk_by(&is_integer/1)
    |> Stream.filter(&(&1 != [nil]))
    |> Stream.zip(ops)
    |> Enum.sum_by(fn {numbers, op} -> apply_op(op, numbers) end)
  end

  defp apply_op("+", args), do: Enum.sum(args)
  defp apply_op("*", args), do: Enum.product(args)
end

Edit:

yes, that makes is much simpler:

  def part_two(input) do
    rows = Input.read!(input) |> String.split("\n", trim: true)
    {ops, rows} = List.pop_at(rows, -1)
    rows = Enum.map(rows, &String.graphemes/1)
    ops = String.split(ops, " ", trim: true)

    rows
    |> Enum.zip_with(fn digits -> String.trim(Enum.join(digits)) end)
    |> Stream.map(fn
      "" -> nil
      str_num -> String.to_integer(str_num)
    end)
    |> Stream.chunk_by(&is_integer/1)
    |> Stream.filter(&(&1 != [nil]))
    |> Stream.zip(ops)
    |> Enum.sum_by(fn {numbers, op} -> apply_op(op, numbers) end)
  end

I had a version of my first solution with chunk_while as well but I feel it makes everything harder to read.

sevenseacat

sevenseacat

Author of Ash Framework

I don’t love the code I wrote today - all the work was in parsing the input into the shape I wanted. Lots of string manipulations, list transposing, and messy stuff.

https://github.com/sevenseacat/advent_of_code/blob/main/lib/y2025/day06.ex

Also, this is the first time this year I’ve been confused at the speed of my solution, did some profiling, and found out that all the time was totally not where I expected it to be!

Name                     ips        average  deviation         median         99th %
day 06, part 1        363.34        2.75 ms     ±1.88%        2.76 ms        2.82 ms
day 06, part 2        258.39        3.87 ms     ±5.48%        3.91 ms        4.97 ms
dompdv

dompdv

Same comment. Most of the job is done in the parsing, accumulate data in Map, to get a data structure that makes the main algorithm I simple.
But I still wonder how to make it nice.


defmodule AdventOfCode.Solution.Year2025.Day06 do
  import Enum, only: [with_index: 1, reduce: 3, sum: 1, take: 2]

  def mul(l), do: reduce(l, 1, &(&1 * &2))

  def parse_part1(input) do
    # Returns a list of groups like {:mul, [12,1453,23]} (operator, operands)
    input
    |> String.split("\n", trim: true)
    |> reduce(
      %{},
      fn line, acc ->
        indexed_groups = line |> String.split(" ", trim: true) |> with_index()

        reduce(indexed_groups, acc, fn {chunk, chunk_number}, local_acc ->
          {operator, operands} = Map.get(local_acc, chunk_number, {nil, []})

          case chunk do
            "*" ->
              Map.put(local_acc, chunk_number, {:mul, operands})

            "+" ->
              Map.put(local_acc, chunk_number, {:add, operands})

            number ->
              Map.put(local_acc, chunk_number, {operator, [String.to_integer(number) | operands]})
          end
        end)
      end
    )
    |> Map.values()
  end

  def part1(input) do
    parse_part1(input)
    |> reduce(0, fn
      {op, l}, acc ->
        acc + if op == :mul, do: mul(l), else: sum(l)
    end)
  end

  def parse2(input) do
    lines = input |> String.split("\n", trim: true)

    {
      # map of %{column => number}
      reduce(take(lines, length(lines) - 1), %{}, fn line, acc ->
        line
        |> String.codepoints()
        |> with_index()
        |> reduce(acc, fn {c, col}, local_acc ->
          Map.update(local_acc, col, c, &(&1 <> c))
        end)
      end)
      |> reduce(%{}, fn {col, s}, acc ->
        s = String.trim(s)
        if s == "", do: acc, else: Map.put(acc, col, String.to_integer(s))
      end)
      |> Map.new(),
      # map of %{column => operator}
      List.last(lines)
      |> to_charlist()
      |> with_index()
      |> reduce(%{}, fn {c, col}, acc ->
        if c == 32, do: acc, else: Map.put(acc, col, c)
      end)
    }
  end

  # Find the next element in a list. returns max_length if reaching the end of the list
  def next([val], val, max_length), do: max_length
  def next([val, n | _], val, _max_length), do: n
  def next([_n | r], val, max_length), do: next(r, val, max_length)

  # Gather numbers starting from col till an empty column
  def gather(numbers, col, acc) do
    if Map.has_key?(numbers, col), do: gather(numbers, col + 1, [numbers[col] | acc]), else: acc
  end

  def part2(input) do
    {numbers, operators} = parse2(input)

    reduce(operators, 0, fn {col, op}, acc ->
      l = gather(numbers, col, [])
      acc + if op == ?*, do: mul(l), else: sum(l)
    end)
  end
end
rvnash

rvnash

Anyone else feel there was little “fun” in todays problem? I enjoy problems when there is a challenging data structure or algorithmic obstacle to surmount. This seemed like a lot of persnickety busywork.

defmodule RAoc.Solutions.Y25.Day06 do
  alias AoC.Input

  # Parts 1 and 2 have their own parsers
  def parse(input, _part) do
    input
  end

  def part_one(input) do
    Input.stream!(input, trim: true)
    |> Stream.map(fn line ->
      String.split(line, " ", trim: true)
    end)
    |> Enum.zip()
    |> Enum.map(fn tuple ->
      [operator | operands] = Tuple.to_list(tuple) |> Enum.reverse()
      {operator, Enum.map(operands, &String.to_integer/1)}
    end)
    |> Enum.map(&solve/1)
    |> Enum.sum()
  end

  def part_two(input) do
    Input.read!(input)
    |> String.split("\n", trim: true)
    |> Enum.map(&String.graphemes/1)
    |> move_last_to_first()
    |> then(fn [operators | operands] ->
      operators =
        operators
        |> Enum.reject(&(&1 == " "))
        |> Enum.reverse()

      operands =
        operands
        |> Enum.zip()
        |> Enum.reverse()
        |> Enum.map(fn tuple ->
          tuple
          |> Tuple.to_list()
          |> Enum.join()
          |> String.trim()
        end)
        |> Enum.chunk_by(&(&1 == ""))
        |> Enum.reject(&(&1 == [""]))
        |> Enum.map(fn list -> Enum.map(list, &String.to_integer/1) end)

      Enum.zip(operators, operands)
    end)
    |> Enum.map(&solve/1)
    |> Enum.sum()
  end

  defp solve({"*", operands}), do: Enum.product(operands)
  defp solve({"+", operands}), do: Enum.sum(operands)
  defp move_last_to_first([]), do: []

  defp move_last_to_first(list) do
    [last | rest_reversed] = Enum.reverse(list)
    [last | Enum.reverse(rest_reversed)]
  end
end
BartOtten

BartOtten

Yeah, as I have a time limit I only made Part 1, Part 2 I only drafted in pseudo code: taking largest number in a row (A), then chunk the 'row string’ with number A to have the numbers including whitespace. From there on it’s just ‘grid’ing again.

Part 1

  def parse(input, _part) do
    Input.read!(input)
    |> String.trim()
    |> String.split("\n")
  end

  def part_one(problem) do
    problem
    |> Enum.flat_map(&String.split(&1) |> Enum.with_index )
    |> Enum.reverse()
    |> Enum.group_by(fn {x,y} -> y end, fn {x,y} -> x end)
    |> Enum.map(fn {_, [op | rest]} -> 
        Enum.intersperse(rest, op) 
        |> List.to_string() 
        |> Code.eval_string() 
        |> elem(0) 
       end)
    |> Enum.sum()
  end

BartOtten

BartOtten

Reading the second problem on the iPhone makes it more challenging to understand how their math works… (see the alignment of numbers)

Where Next? Top

Trending in Challenges Top

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews