AoC 2023 - Day 05
Mix.install([
{:kino, "~> 0.11.3"}
])
Section
input_e1 =
"day05-e1.txt"
|> Kino.FS.file_path()
|> File.read!()
|> String.trim()
input =
"day05.txt"
|> Kino.FS.file_path()
|> File.read!()
|> String.trim()
[input_e1, input]
defmodule Solution do
def part_1(input) do
[seeds, maps] = parse(input)
Enum.map(seeds, fn seed ->
Enum.reduce(maps, seed, &match/2)
end)
|> Enum.min()
end
def part_2(input) do
[seeds, steps] = parse(input)
seeds
|> Enum.chunk_every(2)
|> Enum.map(fn [s1, s2] -> s1..(s1 + s2 - 1) end)
|> steps(steps)
|> Enum.map(fn s1.._ -> s1 end)
|> Enum.min()
end
def parse(input) do
[seeds | maps] = input |> String.split("\n\n")
seeds =
seeds
|> String.split(":")
|> List.last()
|> String.split(" ", trim: true)
|> Enum.map(&String.to_integer/1)
maps =
Enum.map(maps, fn map ->
map
|> String.split("\n")
|> Enum.drop(1)
|> Enum.map(&build_range/1)
|> Enum.sort_by(&elem(&1, 0))
end)
[seeds, maps]
end
defp match(step, seed) do
Enum.find_value(step, seed, fn {r1, r2, offset} ->
if seed in r1..r2 do
seed - offset
end
end)
end
defp steps(seed, []), do: seed
defp steps(seed, [step | rest]) do
seed
|> Enum.flat_map(&step(&1, step))
|> steps(rest)
end
defp step(s1..s2, []), do: [s1..s2]
defp step(s1..s2, [{r1, r2, offset} | rest]) do
cond do
# within
r1 <= s1 and s2 <= r2 ->
[(s1 - offset)..(s2 - offset)]
# partial @ start
s1 < r1 and s2 in r1..r2 ->
[
s1..(r1 - 1),
(r1 - offset)..(r2 - offset)
]
# partial @ end?
s1 in r1..r2 ->
[
(s1 - offset)..(r2 - offset)
| step((r2 + 1)..s2, rest)
]
true ->
step(s1..s2, rest)
end
end
defp build_range(string) do
[dst, src, len] = string |> String.split(" ") |> Enum.map(&String.to_integer/1)
{src, src + len - 1, src - dst}
end
end
Solution.parse(input)
Solution.part_1(input_e1)
Solution.part_2(input)