89 lines
1.8 KiB
Elixir
89 lines
1.8 KiB
Elixir
import AOC
|
|
|
|
aoc 2021, 9 do
|
|
def input_map(),
|
|
do:
|
|
input_string()
|
|
|> String.split("\n")
|
|
|> Enum.with_index()
|
|
|> Enum.flat_map(fn {line, i} ->
|
|
line
|
|
|> String.split("", trim: true)
|
|
|> Enum.map(&String.to_integer/1)
|
|
|> Enum.with_index()
|
|
|> Enum.map(fn {cell, j} -> {{i, j}, cell} end)
|
|
end)
|
|
|> Map.new()
|
|
|
|
def n({i, j}), do: {i - 1, j}
|
|
def e({i, j}), do: {i, j + 1}
|
|
def s({i, j}), do: {i + 1, j}
|
|
def w({i, j}), do: {i, j - 1}
|
|
|
|
def at(map, pos), do: Map.get(map, pos, :infinity)
|
|
|
|
def directions(), do: [&n/1, &e/1, &s/1, &w/1]
|
|
|
|
def neighbors(map, origin) do
|
|
for d <- directions(), pos = d.(origin) do
|
|
{pos, at(map, pos)}
|
|
end
|
|
end
|
|
|
|
def gt({_, a}, b), do: a > b
|
|
|
|
def basin_from(map, pos, seen \\ MapSet.new()) do
|
|
curr = at(map, pos)
|
|
|
|
if min(curr, 9) == 9 do
|
|
MapSet.new()
|
|
else
|
|
seen = MapSet.put(seen, pos)
|
|
|
|
inclines =
|
|
neighbors(map, pos)
|
|
|> Enum.filter(>(&1, curr))
|
|
|> Enum.map(&elem(&1, 0))
|
|
|
|
for inc <- inclines, reduce: seen do
|
|
seen ->
|
|
if MapSet.member?(seen, inc) do
|
|
seen
|
|
else
|
|
MapSet.union(seen, basin_from(map, inc, seen))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def p1 do
|
|
map = input_map()
|
|
|
|
for i <- 0..99, j <- 0..99 do
|
|
pos = {i, j}
|
|
curr = at(map, pos)
|
|
|
|
neighbors =
|
|
directions()
|
|
|> Enum.map(fn d -> at(map, d.(pos)) end)
|
|
|
|
if Enum.all?(neighbors, &(curr < &1)) do
|
|
curr + 1
|
|
else
|
|
0
|
|
end
|
|
end
|
|
|> Enum.sum()
|
|
end
|
|
|
|
def p2 do
|
|
map = input_map()
|
|
|
|
for i <- 0..99, j <- 0..99, pos = {i, j} do
|
|
basin_from(map, pos) |> MapSet.size()
|
|
end
|
|
|> Enum.sort(:desc)
|
|
|> Enum.take(3)
|
|
|> Enum.product()
|
|
end
|
|
end
|