Powered by AppSignal & Oban Pro

Advent of code day 11

2025/livebooks/day-11.livemd

Advent of code day 11

Mix.install([
  {:kino, "~> 0.5.0"}
])

Setup input

example = Kino.Input.textarea("Please paste your input example:")
input = Kino.Input.textarea("Please paste your real input:")
graph = example
|> Kino.Input.read()
|> String.split("\n", trim: true)
|> Enum.reduce(%{}, fn line, graph ->
  [key | neigh] = String.split(line, " ", trim: true)
  key = String.trim_trailing(key, ":")

  Enum.reduce(neigh, graph, fn n, acc ->
    neigh = Map.get(acc, key, MapSet.new())
    Map.put(acc, key, MapSet.put(neigh, n))
  end)
end)
defmodule DFS do
  def count_paths(graph, start, part \\ :one) do
    {count, _memo} = dfs(start, graph, %{}, MapSet.new(), part)
    count
  end

  defp dfs(node, graph, memo, visited, part) do
    # Check memoization
    has_dac = MapSet.member?(visited, "dac")
    has_fft = MapSet.member?(visited, "fft")
    memo_key = if part == :two, do: {node, has_dac, has_fft}, else: node

    case memo[memo_key] do
      nil -> :continue
      cached -> {cached, memo}
    end
    |> case do
      {cached, memo} ->
        {cached, memo}

      :continue ->
        if MapSet.member?(visited, node) do
          {0, memo}
        else
          neighbors = Map.get(graph, node, [])

          cond do
            neighbors == [] ->
              result = case part do
                :one ->
                  1
                :two ->
                  if has_dac and has_fft, do: 1, else: 0
              end
              {result, Map.put(memo, memo_key, result)}

            true ->
              # Recursive case: explore neighbors
              visited = MapSet.put(visited, node)
              {total, memo} =
                Enum.reduce(neighbors, {0, memo}, fn n, {acc, memo} ->
                  {val, memo} = dfs(n, graph, memo, visited, part)
                  {acc + val, memo}
                end)
              {total, Map.put(memo, memo_key, total)}
          end
        end
    end
  end
end

Part 01

DFS.count_paths(graph, "you")

Part 02

DFS.count_paths(graph, "svr", :two)