Powered by AppSignal & Oban Pro

Day 9: All in a Single Night

day_9_all_in_a_single_night.livemd

Day 9: All in a Single Night

Section

defmodule P1 do
  def parse_lines(lines) do
    lines
    |> Enum.reduce(%{}, &parse_line/2)
  end

  defp parse_line(line, acc) do
    Regex.run(~r/^(\w+) to (\w+) = (\d+)$/, line, capture: :all_but_first)
    |> case do
      [city1, city2, dist_str] ->
        dist = String.to_integer(dist_str)

        acc
        |> Map.update(city1, %{city2 => dist}, &Map.put(&1, city2, dist))
        |> Map.update(city2, %{city1 => dist}, &Map.put(&1, city1, dist))
    end
  end

  def dfs_all(graph) do
    Enum.map(Map.keys(graph), fn start_node ->
      explore_from_node(graph, start_node)
    end)
    |> List.flatten()
  end

  defp explore_from_node(graph, start_node) do
    dfs(graph, start_node, [start_node], 0, [])
  end

  defp dfs(graph, current, path, distance, all_paths) do
    neighbors = Map.get(graph, current, %{})
    nodes = Map.keys(graph)

    if length(path) == length(nodes) do
      [{Enum.reverse(path), distance} | all_paths]
    else
      Enum.reduce(neighbors, all_paths, fn {neighbor, d}, acc ->
        if Enum.member?(path, neighbor) do
          acc
        else
          dfs(graph, neighbor, [neighbor | path], distance + d, acc)
        end
      end)
    end
  end
end

paths =
  "/Users/eli/Desktop/input.txt"
  |> File.read!()
  |> String.split("\n", trim: true)
  |> P1.parse_lines()
  |> P1.dfs_all()

min_path = Enum.min_by(paths, &elem(&1, 1))
max_path = Enum.max_by(paths, &elem(&1, 1))
{min_path, max_path}
{{["Faerun", "AlphaCentauri", "Tambi", "Snowdin", "Norrath", "Tristram", "Arbre", "Straylight"],
  117},
 {["Snowdin", "Tristram", "Straylight", "AlphaCentauri", "Norrath", "Faerun", "Arbre", "Tambi"],
  909}}