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

Advent of Code 2024 - Day 13

2024/13.livemd

Advent of Code 2024 - Day 13

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/13/input", opts).body
puzzles =
  puzzle_input
  |> String.split("\n", trim: true)
  |> Enum.chunk_every(3)
  |> Enum.map(fn machine ->
    Enum.map(machine, fn row ->
      [[_patternx, x], [_patterny, y]] = Regex.scan(~r/[XY][+=](\d+)/, row)

      {String.to_integer(x), String.to_integer(y)}
    end)
  end)

Helpers

defmodule ClawContraption do
  # ax * x + bx * y = px
  # ay * x + by * y = py
  def solve([{ax, ay}, {bx, by}, {px, py}]) do
    case ax * by - bx * ay do
      0 ->
        0

      determinant ->
        num_x = px * by - bx * py
        num_y = ax * py - px * ay

        if rem(num_x, determinant) == 0 and rem(num_y, determinant) == 0 do
          x = div(num_x, determinant)
          y = div(num_y, determinant)

          {x, y}
        else
          0
        end
    end
  end

  def calculate_cost({x, y}), do: x * 3 + y
  def calculate_cost(_result), do: 0
end

Puzzle 1

puzzle_1 = fn ->
  Enum.map(puzzles, fn puzzle ->
    puzzle
    |> ClawContraption.solve()
    |> ClawContraption.calculate_cost()
  end)
  |> Enum.sum()
end

puzzle_1.()

Puzzle 2

puzzle_2 = fn ->
  Enum.map(puzzles, fn puzzle ->
    [{ax, ay}, {bx, by}, {px, py}] = puzzle
    
    [{ax, ay}, {bx, by}, {px + 10_000_000_000_000, py + 10_000_000_000_000}]
    |> ClawContraption.solve()
    |> ClawContraption.calculate_cost()
  end)
  |> Enum.sum()
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 9.53 K 104.96 μs ±12.40% 104 μs 117.92 μs
puzzle_2 1.15 K 869.68 μs ±16.67% 858.52 μs 996.20 μs