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

Advent of Code 2024 - Day 19

2024/19.livemd

Advent of Code 2024 - Day 19

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/19/input", opts).body
[patterns | towels] = String.split(puzzle_input, "\n", trim: true)
patterns = String.split(patterns, ", ")
defmodule LinenLayout do
  def init_cache do
    if :ets.whereis(:towels_cache) != :undefined do
      :ets.delete(:towels_cache)
    end

    :ets.new(:towels_cache, [:set, :public, :named_table])
  end

  def possibilities("" = _towel, _patterns), do: 1

  def possibilities(towel, patterns) do
    case :ets.lookup(:towels_cache, towel) do
      [{_key, result}] ->
        result

      [] ->
        Enum.reduce(patterns, 0, fn pattern, acc ->
          case towel do
            ^pattern <> rest -> possibilities(rest, patterns) + acc
            _other -> acc
          end
        end)
        |> tap(fn result ->
          :ets.insert(:towels_cache, {towel, result})
        end)
    end
  end
end

Puzzle 1

puzzle_1 = fn ->
  LinenLayout.init_cache()

  towels
  |> Task.async_stream(&amp;LinenLayout.possibilities(&amp;1, patterns))
  |> Enum.reduce(0, fn {:ok, result}, acc ->
    if result == 0, do: acc, else: acc + 1
  end)
end

puzzle_1.()
puzzle_2 = fn ->
  LinenLayout.init_cache()

  towels
  |> Task.async_stream(&amp;LinenLayout.possibilities(&amp;1, patterns))
  |> Enum.reduce(0, fn {:ok, result}, acc ->
    result + acc
  end)
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 7.38 135.52 ms ±16.46% 127.98 ms 192.95 ms
puzzle_2 5.88 170.21 ms ±6.69% 172.76 ms 190.97 ms