Advent of Code 2024 - Day 7

This was much easier than yesterday!

And I’m always proud of myself when I manage to write recursive code (mostly because I don’t need recursive code very often in my daily job)

Part1

defmodule Advent.Y2024.Day07.Part1 do
  def run(puzzle) do
    puzzle
    |> parse()
    |> Enum.filter(fn {result, nums} -> eval(result, nums) end)
    |> Enum.map(&elem(&1, 0))
    |> Enum.sum()
  end

  def parse(puzzle) do
    puzzle
    |> String.split("\n")
    |> Enum.map(fn equation ->
      [result, numbers] = String.split(equation, ": ")
      numbers = numbers |> String.split(" ") |> Enum.map(&String.to_integer/1)
      {String.to_integer(result), numbers}
    end)
  end

  def eval(result, numbers, total \\ nil)
  def eval(result, [], total), do: total == result
  def eval(result, [number | tail], nil), do: eval(result, tail, number)
  def eval(result, _numbers, total) when total > result, do: false

  def eval(result, [number | tail], total) do
    eval(result, tail, total + number) or eval(result, tail, total * number)
  end
end

Part2

defmodule Advent.Y2024.Day07.Part2 do
  alias Advent.Y2024.Day07.Part1

  def run(puzzle) do
    puzzle
    |> Part1.parse()
    |> Task.async_stream(fn {result, nums} -> {eval(result, nums), result} end)
    |> Stream.map(fn {:ok, output} -> output end)
    |> Stream.filter(fn {eval, _result} -> eval end)
    |> Stream.map(fn {_eval, result} -> result end)
    |> Enum.sum()
  end

  def eval(result, numbers, acc \\ nil)
  def eval(result, [], acc), do: acc == result
  def eval(result, [number | tail], nil), do: eval(result, tail, number)
  def eval(result, _numbers, acc) when acc > result, do: false

  def eval(result, [number | tail], acc) do
    eval(result, tail, acc + number) or
      eval(result, tail, acc * number) or
      eval(result, tail, concat(acc, number))
  end

  defp concat(a, b) when b < 10, do: a * 10 + b
  defp concat(a, b) when b < 100, do: a * 100 + b
  defp concat(a, b) when b < 1000, do: a * 1000 + b
end

It takes about 10ms for part1 and 40ms for part2
edit: using Task.async_stream and part 2 is now running in 20ms

2 Likes