211 lines
4.4 KiB
Elixir
211 lines
4.4 KiB
Elixir
|
|
defmodule Aoc19.Solution.Day06 do
|
||
|
|
@name "Day 6: Universal Orbit Map"
|
||
|
|
@behaviour Solution
|
||
|
|
@you "YOU"
|
||
|
|
@santa "SAN"
|
||
|
|
|
||
|
|
@impl Solution
|
||
|
|
def get_name, do: @name
|
||
|
|
|
||
|
|
@impl Solution
|
||
|
|
@doc """
|
||
|
|
Parse raw input into a list of strings.
|
||
|
|
|
||
|
|
## Examples
|
||
|
|
|
||
|
|
iex> Aoc19.Solution.Day06.parse!(\"\"\"
|
||
|
|
...> X)5
|
||
|
|
...> Y)13
|
||
|
|
...> ZW)1
|
||
|
|
...> \"\"\")
|
||
|
|
[["X", "5"], ["Y", "13"], ["ZW", "1"]]
|
||
|
|
|
||
|
|
"""
|
||
|
|
def parse!(raw) do
|
||
|
|
raw
|
||
|
|
|> String.split()
|
||
|
|
|> Enum.map(fn s -> String.split(s, ")") end)
|
||
|
|
end
|
||
|
|
|
||
|
|
@impl Solution
|
||
|
|
@doc """
|
||
|
|
Solution for the first part of "Day 6: Universal Orbit Map".
|
||
|
|
|
||
|
|
## Examples
|
||
|
|
|
||
|
|
iex> \"\"\"
|
||
|
|
...> COM)B
|
||
|
|
...> B)C
|
||
|
|
...> C)D
|
||
|
|
...> D)E
|
||
|
|
...> E)F
|
||
|
|
...> B)G
|
||
|
|
...> G)H
|
||
|
|
...> D)I
|
||
|
|
...> E)J
|
||
|
|
...> J)K
|
||
|
|
...> K)L
|
||
|
|
...> \"\"\" |> Aoc19.Solution.Day06.parse!() |> Aoc19.Solution.Day06.solve_first_part()
|
||
|
|
42
|
||
|
|
|
||
|
|
"""
|
||
|
|
def solve_first_part(input) do
|
||
|
|
input
|
||
|
|
|> orbit_tree
|
||
|
|
|> Map.values()
|
||
|
|
|> Enum.map(fn l -> length(l) end)
|
||
|
|
|> Enum.sum()
|
||
|
|
end
|
||
|
|
|
||
|
|
@impl Solution
|
||
|
|
@doc """
|
||
|
|
Solution for the second part of "Day 6: Universal Orbit Map".
|
||
|
|
|
||
|
|
## Examples
|
||
|
|
|
||
|
|
iex> \"\"\"
|
||
|
|
...> COM)B
|
||
|
|
...> B)C
|
||
|
|
...> C)D
|
||
|
|
...> D)E
|
||
|
|
...> E)F
|
||
|
|
...> B)G
|
||
|
|
...> G)H
|
||
|
|
...> D)I
|
||
|
|
...> E)J
|
||
|
|
...> J)K
|
||
|
|
...> K)L
|
||
|
|
...> K)YOU
|
||
|
|
...> I)SAN
|
||
|
|
...> \"\"\" |> Aoc19.Solution.Day06.parse!() |> Aoc19.Solution.Day06.solve_second_part()
|
||
|
|
4
|
||
|
|
"""
|
||
|
|
def solve_second_part(input) do
|
||
|
|
santa = parent_branch(input, @santa)
|
||
|
|
you = parent_branch(input, @you)
|
||
|
|
|
||
|
|
common = MapSet.intersection(MapSet.new(santa), MapSet.new(you)) |> Enum.to_list()
|
||
|
|
|
||
|
|
nearest(common, you) + nearest(common, santa)
|
||
|
|
end
|
||
|
|
|
||
|
|
def orbit_tree(input) do
|
||
|
|
orbits =
|
||
|
|
input |> Enum.reduce(%{}, fn [parent, child], acc -> children(acc, parent, child) end)
|
||
|
|
|
||
|
|
orbits
|
||
|
|
|> Map.keys()
|
||
|
|
|> Enum.reduce(orbits, fn k, acc -> Map.get(acc, k, []) |> grandchildren(acc, k) end)
|
||
|
|
end
|
||
|
|
|
||
|
|
@doc """
|
||
|
|
Add grandchildren to branch if parent is present.
|
||
|
|
|
||
|
|
## Examples
|
||
|
|
|
||
|
|
iex> Aoc19.Solution.Day06.children(%{a: [3, 2]}, :a, 5)
|
||
|
|
%{a: [5, 3, 2]}
|
||
|
|
|
||
|
|
"""
|
||
|
|
def children(acc, parent, child) do
|
||
|
|
if Map.has_key?(acc, parent) do
|
||
|
|
Map.put(acc, parent, [child | Map.get(acc, parent, child)])
|
||
|
|
else
|
||
|
|
Map.put(acc, parent, [child])
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
@doc """
|
||
|
|
Add all grandchildren to the orbit tree.
|
||
|
|
|
||
|
|
## Examples
|
||
|
|
|
||
|
|
iex> Aoc19.Solution.Day06.grandchildren([5, 6], %{a: [3, 2], b: [5]}, 2)
|
||
|
|
%{a: [3, 2, 5, 6], b: [5]}
|
||
|
|
|
||
|
|
"""
|
||
|
|
def grandchildren(children, orbits, parent) do
|
||
|
|
values =
|
||
|
|
orbits
|
||
|
|
|> Map.values()
|
||
|
|
|> Enum.map(fn grandchildren -> append_grandchildren(grandchildren, parent, children) end)
|
||
|
|
|
||
|
|
keys = Map.keys(orbits)
|
||
|
|
|
||
|
|
Enum.zip(keys, values)
|
||
|
|
|> Map.new()
|
||
|
|
end
|
||
|
|
|
||
|
|
@doc """
|
||
|
|
add grandchildren to existing branch in orbit tree.
|
||
|
|
|
||
|
|
## Examples
|
||
|
|
|
||
|
|
iex> Aoc19.Solution.Day06.append_grandchildren([0, 1, 2], 1, [3, 4])
|
||
|
|
[0, 1, 2, 3, 4]
|
||
|
|
|
||
|
|
"""
|
||
|
|
def append_grandchildren(children, parent, grandchildren) do
|
||
|
|
if Enum.member?(children, parent) do
|
||
|
|
[children | grandchildren] |> List.flatten()
|
||
|
|
else
|
||
|
|
children
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
@doc """
|
||
|
|
Add all grandchildren to the orbit tree.
|
||
|
|
|
||
|
|
## Examples
|
||
|
|
|
||
|
|
iex> Aoc19.Solution.Day06.parent_branch([
|
||
|
|
...> ["13", "3"],
|
||
|
|
...> ["1", "7"],
|
||
|
|
...> ["7", "13"]
|
||
|
|
...> ], "13")
|
||
|
|
["1", "7"]
|
||
|
|
|
||
|
|
iex> Aoc19.Solution.Day06.parent_branch([
|
||
|
|
...> ["13", "3"],
|
||
|
|
...> ["1", "7"],
|
||
|
|
...> ["7", "13"]
|
||
|
|
...> ], "7")
|
||
|
|
["1"]
|
||
|
|
|
||
|
|
iex> Aoc19.Solution.Day06.parent_branch([
|
||
|
|
...> ["13", "3"],
|
||
|
|
...> ["1", "7"],
|
||
|
|
...> ["7", "13"]
|
||
|
|
...> ], "3")
|
||
|
|
["1", "7", "13"]
|
||
|
|
|
||
|
|
"""
|
||
|
|
def parent_branch(orbits, subject, seen) do
|
||
|
|
case Enum.filter(orbits, fn [_parent, child] -> child == subject end) do
|
||
|
|
[] -> seen |> List.delete_at(-1)
|
||
|
|
[[parent, _child]] -> parent_branch(orbits, parent, [parent | seen])
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def parent_branch(orbits, subject) do
|
||
|
|
parent_branch(orbits, subject, [subject])
|
||
|
|
end
|
||
|
|
|
||
|
|
@doc """
|
||
|
|
Find nearest parent in branch from a set of positions.
|
||
|
|
|
||
|
|
## Examples
|
||
|
|
|
||
|
|
iex> Aoc19.Solution.Day06.nearest(["1", "2"], ["0", "1", "2", "3", "4"])
|
||
|
|
2
|
||
|
|
|
||
|
|
"""
|
||
|
|
def nearest(positions, branch) do
|
||
|
|
len = length(branch)
|
||
|
|
|
||
|
|
positions
|
||
|
|
|> Enum.map(fn p -> len - Enum.find_index(branch, fn x -> x == p end) - 1 end)
|
||
|
|
|> Enum.min()
|
||
|
|
end
|
||
|
|
end
|