Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Advent of Code 2024 - Day 07

2024/07.livemd

Advent of Code 2024 - Day 07

Mix.install([
  {:req, "~> 0.5"},
  {:benchee, "~> 1.3"}
])

Input

opts = [headers: [{"cookie", "session=#{System.fetch_env!("LB_AOC_SESSION")}"}]]
puzzle_input = Req.get!("https://adventofcode.com/2024/day/7/input", opts).body
puzzle_input =
  String.split(puzzle_input, "\n", trim: true)
  |> Enum.map(fn row ->
    [sum, nums] = String.split(row, ":", trim: true)

    sum = String.to_integer(sum)
    nums = String.split(nums, " ", trim: true) |> Enum.map(&String.to_integer(&1))

    {sum, nums}
  end)
defmodule BridgeRepair do
  def sum_of_sums(rows, operators) do
    Task.async_stream(rows, fn {sum, nums} ->
      [head | tail] = nums

      if valid?(tail, sum, head, operators), do: sum, else: 0
    end)
    |> Enum.reduce(0, fn {:ok, x}, acc -> x + acc end)
  end

  def valid?(_nums, expected, acc, _operators) when acc > expected, do: false
  def valid?(_nums, expected, acc, _operators) when expected == acc, do: true
  def valid?([], _expected, _acc, _operators), do: false

  def valid?([x | tail], expected, acc, operators) do
    for operator <- operators do
      case operator do
        :multiply -> valid?(tail, expected, x * acc, operators)
        :add -> valid?(tail, expected, x + acc, operators)
        :concatenation -> valid?(tail, expected, concat_nums(acc, x), operators)
      end
    end
    |> Enum.any?()
  end

  def concat_nums(a, b) when b < 10, do: a * 10 + b
  def concat_nums(a, b) when b < 100, do: a * 100 + b
  def concat_nums(a, b) when b < 1_000, do: a * 1000 + b

  def concat_nums(a, b) do
    a * Integer.pow(10, Integer.digits(b) |> length) + b
  end
end

Puzzle 1

puzzle_1 = fn ->
  operators = [:multiply, :add]
  BridgeRepair.sum_of_sums(puzzle_input, operators)
end

puzzle_1.()

Puzzle 2

puzzle_2 = fn ->
  operators = [:multiply, :add, :concatenation]
  BridgeRepair.sum_of_sums(puzzle_input, operators)
end

puzzle_2.()

Benchmarks

Benchee.run(
  %{
    "puzzle_1" => fn -> puzzle_1.() end,
    "puzzle_2" => fn -> puzzle_2.() end
  })
Name ips average deviation median 99th %
puzzle_1 119.32 8.38 ms ±5.76% 8.32 ms 9.90 ms
puzzle_2 22.29 44.86 ms ±5.46% 44.67 ms 62.88 ms