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)