Advent of Code 2015 Day 9 Part 2
Mix.install([
{:kino_aoc, "~> 0.1"}
])
Get Inputs
{:ok, puzzle_input} =
KinoAOC.download_puzzle("2015", "9", System.fetch_env!("LB_SESSION"))
My answer
defmodule TSP do
def cities(distances) do
distances
|> Map.keys()
|> Enum.map(&Tuple.to_list(&1))
|> Enum.concat()
|> Enum.uniq()
end
def all_routes([]), do: [[]]
def all_routes(list) do
for elem <- list,
rest <- all_routes(list--[elem]) do
[elem | rest]
end
end
def drop_reversed(list) do
list
|> Enum.reduce([], fn new, acc ->
if Enum.member?(acc, Enum.reverse(new)) do
acc
else
[new | acc]
end
end)
end
def reverse_distances(distances) do
distances
|> Enum.into(%{}, fn {{from, to}, value} ->
{{to, from}, value}
end)
end
def get_max_distance(permutations, full_distances) do
permutations
|> Enum.map(fn permutation ->
next_permutation = tl(permutation) ++ [nil]
Enum.zip(permutation, next_permutation)
|> Enum.map(fn {from, to} ->
if is_nil(to), do: 0, else: Map.get(full_distances, {from, to})
end)
|> Enum.sum()
end)
|> Enum.max()
end
def solve(distances) do
permutations =
distances
|> cities()
|> all_routes()
|> drop_reversed()
full_distances = Map.merge(distances, reverse_distances(distances))
get_max_distance(permutations, full_distances)
end
end
puzzle_input
|> String.split("\n")
|> Enum.into(%{}, fn row ->
Regex.named_captures(
~r/(?[a-zA-Z]+) to (?[a-zA-Z]+) = (?[0-9]+)/,
row
)
|> then(fn %{"from" => from, "to" => to, "distance" => distance} ->
{{from, to}, String.to_integer(distance)}
end)
end)
|> TSP.solve()