Kevo

Kevo

I don’t use elixir much, mainly just learning for some hobby projects I have in mind, but I like doing these puzzles as I learn about functions I don’t use or know much and it’s just a different way of thinking stringing things together in pipelines. I kind of enjoyed the parsing challenge in the second part. Zip and chunk_by are ones I rarely use that helped me out today.

defmodule Puzzle do
  def parse_first(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&String.split(&1, " ", trim: true))
    |> Enum.zip()
    |> Enum.map(&Tuple.to_list/1)
    |> Enum.map(&Enum.reverse/1)
  end

  def parse_second(input) do
    rows = String.split(input, "\n")

    operators = 
      List.last(rows)
      |> String.split(" ", trim: true)
        
    rows =
      rows
      |> List.delete_at(length(rows) - 1)
      |> Enum.map(&String.graphemes/1)
      |> Enum.zip()
      |> Enum.map(&Tuple.to_list/1)
      |> Enum.map(&Enum.join/1)
      |> Enum.map(&String.trim/1)
      |> Enum.chunk_by(&(&1 == ""))
      |> Enum.filter(fn chunk -> chunk != [""] end)
      
    Enum.zip(operators, rows)
    |> Enum.map(fn {operator, values} -> [operator | values] end)  
  end

  def compute_column([operator | values]) do
    case operator do
      "*" ->
        Enum.reduce(values, 1, fn value_str, acc ->
          String.to_integer(value_str) * acc
        end)

      "+" ->
        Enum.reduce(values, 0, fn value_str, acc ->
          String.to_integer(value_str) + acc
        end)
    end
  end
end

#Part 1
Puzzle.parse_first(input)
|> Enum.reduce(0, fn col, acc ->
  acc + Puzzle.compute_column(col)
end)

#Part 2
Puzzle.parse_second(input)
|> Enum.reduce(0, fn col, acc ->
  acc + Puzzle.compute_column(col)
end)
0
Post #11
zwippie

zwippie

That one is going straight into my Aoc.Utils, thank you!

4
Post #13
KeithFrost

KeithFrost

2025 Dec 06

Trash Compactor

defmodule Cephalopod do
  def parse_line(line, columns) do
    col_strs = String.split(line)
    columns = if columns == nil do
      Enum.map(col_strs, fn _ -> [] end)
    else
      columns
    end
    Enum.zip_with(col_strs, columns, fn str, column ->
      case Integer.parse(str) do
        :error ->
          [String.to_atom(str) | column]
        {n, ""} ->
          [n | column]
      end
    end)
  end

  def parse(lines) do
    Enum.reduce(lines, nil, &parse_line/2)
  end

  def sum_work(columns) do
    Enum.reduce(columns, 0, fn column, sum -> 
      case column do
        [:+ | ns] ->
          sum + Enum.sum(ns)
        [:* | ns] ->
          sum + Enum.product(ns)
      end
    end)
  end
end

test_input = """
123 328  51 64 
 45 64  387 23 
  6 98  215 314
*   +   *   +  
""" |> String.split("\n", trim: true)
  |> IO.inspect()
test1_work = Cephalopod.parse(test_input)

Cephalopod.sum_work(test1_work)

input_work = File.stream!(__DIR__ <> "/dec-06-input.txt")
  |> Cephalopod.parse()
Cephalopod.sum_work(input_work)

Part Two

defmodule Cephalopod2 do
  def parse(lines) do
    flipped = Enum.map(lines, fn line -> 
      to_charlist(String.trim_trailing(line, "\n"))
    end)
      |> Enum.zip_with(&Function.identity/1)
      |> Enum.map(&to_string/1)
    Enum.flat_map(flipped, fn s ->
      case Regex.run(~r"^\s*(\d*)\s*([*+])?$", s) do
        [_match, ""] ->
          []
        [_match, digits] ->
          [String.to_integer(digits)]
        [_match, digits, op] ->
          [String.to_atom(op), String.to_integer(digits)]
      end
    end)
  end

  def sum_work(formulae, op \\ nil, acc \\ nil, sum \\ 0) do
    case formulae do
      [:+ | rest] ->
        sum = if acc != nil do
          sum + acc
        else
          sum
        end
        sum_work(rest, :+, 0, sum)
      [:* | rest] ->
        sum = if acc != nil do
          sum + acc
        else
          sum
        end
        sum_work(rest, :*, 1, sum)
      [n | rest] ->
        acc = case op do
          :* ->
            n * acc
          :+ ->
            n + acc
        end
        sum_work(rest, op, acc, sum)
      [] ->
        sum + acc
    end
  end
end

test2_work = Cephalopod2.parse(test_input)
  |> IO.inspect()
Cephalopod2.sum_work(test2_work)

input_work2 = File.stream!(__DIR__ <> "/dec-06-input.txt")
  |> Cephalopod2.parse()
  |> IO.inspect()
Cephalopod2.sum_work(input_work2)
0
Post #14
DavidB

DavidB

Didn’t know about that one, would have made my life easier

Enum.zip_with(list, &Function.identity/1)

My original code, didn’t refactor, so not very readable :

defmodule Day6 do
  def file, do: Parser.read_file(6)
  def test, do: Parser.read_file("test")

  def parse(input) do
    input
  end

  def solve(input \\ file()) do
    input
    |> Enum.reduce(%{}, fn line, acc ->
      line
      |> String.split()
      |> Stream.with_index()
      |> Enum.reduce(acc, fn {value, index}, acc ->
        Map.update(acc, index, [value], fn list -> [value | list] end)
      end)
    end)
    |> Map.values()
    |> Enum.map(&solve_line/1)
    |> Enum.sum()
  end

  def solve_line(list) do
    [operation | rest] = list
    new_list = Enum.reverse(rest) |> Enum.map(&String.to_integer/1)
    calculate(new_list, operation)
  end

  def calculate(list, "+"), do: Enum.sum(list)
  def calculate(list, "*"), do: Enum.product(list)

  def solve_two(input \\ file()) do
    {operator_line, number_lines} = Enum.map(input, &String.graphemes/1) |> List.pop_at(-1)

    operator_line
    |> get_operation_length
    |> Enum.reduce({[], number_lines}, fn {operator, operation_length},
                                          {new_stuf, lines_to_cut} ->
      {problem, rest} =
        Enum.reduce(lines_to_cut, {[], []}, fn line, {new, old} ->
          {one, two} = line |> Enum.split(operation_length)
          {new ++ [one], old ++ [two]}
        end)

      {[{operator, problem} | new_stuf], rest}
    end)
    |> elem(0)
    |> Enum.map(fn {operator, future_number} ->
      future_number |> build_number() |> Enum.reverse() |> calculate(operator)
    end)
    |> Enum.sum()
  end

  def build_number(list) do
    list
    |> Enum.reduce(%{}, fn list, acc ->
      list
      |> Stream.with_index()
      |> Map.new(fn {k, v} -> {v, k} end)
      |> Map.merge(acc, fn _key, v1, v2 -> [v1 | List.wrap(v2)] end)
    end)
    |> Map.values()
    |> Enum.map(fn list ->
      list
      |> Enum.join()
      |> String.trim()
      |> String.reverse()
    end)
    |> Enum.reject(&(&1 == ""))
    |> Enum.map(&String.to_integer/1)
  end

  def get_operation_length(operator_line) do
    operator_line
    |> Enum.reduce([], fn symbol, acc ->
      case symbol do
        " " ->
          [{operator, count} | rest] = acc
          [{operator, count + 1} | rest]

        operator ->
          [{operator, 1} | acc]
      end
    end)
    |> Enum.map(fn {op, count} -> {op, count} end)
    |> Enum.reverse()
  end
end
0
Post #15
davewil

davewil

Apologies for those who recurse.

I love Elixir but I’m solving the problems in Go this year. I also felt day06 was a fussy problem not challenging. I parsed part 1 quickly and solved it. Then it was parse again with a rotated input. Ok, how do you rotate a grid, then apply use in the puzzle. That was about the only interesting bit.

My Go solution is here if anyone wants to look.

https://github.com/davewil/aoc2025/blob/trunk/day06/day06.go

0
Post #16
aloisdg

aloisdg

Part 1

transpose = fn list -> Enum.zip_with(list, &Function.identity/1) end

values =
  File.read!("ids.txt")
  |> String.split("\n")
  |> Enum.map(&(Regex.split(~r{\s+}, &1, trim: true)))

transpose.(values)
|> Enum.map(&Enum.reverse/1)
|> Enum.map(fn [head | tail] ->
  case head do
    "*" -> tail |> Enum.map(&String.to_integer/1) |> Enum.product
    "+" -> tail |> Enum.map(&String.to_integer/1) |> Enum.sum
  end
end)
|> Enum.sum
|> IO.inspect

Part 2

transpose = fn list -> Enum.zip_with(list, &Function.identity/1) end

items =
  File.read!("ids.txt")
  |> String.split("\n")
  |> Enum.map(&(String.split(&1, "", trim: true)))

transpose.(items)
|> Enum.map(&Enum.reverse/1)
|> Enum.map(fn [head | tail] -> {head, tail |> Enum.reverse |> Enum.join("") |> String.trim} end)
|> Enum.chunk_by(fn {_, value} -> value == "" end)
|> Enum.reject(&(&1 == [{" ", ""}]))
|> Enum.map(fn [{operand, value} | tail] ->
  Enum.reduce(tail, String.to_integer(value), fn {_, x}, acc ->
    case operand do
      "*" -> acc * String.to_integer(x)
      "+" -> acc + String.to_integer(x)
    end
  end)
end)
|> Enum.sum
|> IO.inspect
0
Post #17
aloisdg

aloisdg

You can replace

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

with

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

shorter and easier to read :slight_smile:

1
Post #18
hauleth

hauleth

Enum.product(list)

3
Post #19
aloisdg

aloisdg

Even better. Thank you!

0
Post #20