Day 9: Smoke Basin
Mix.install([:kino])
input = Kino.Input.textarea("Please paste your input:")
Setup
inputs =
input
|> Kino.Input.read()
|> String.split("\n", trim: true)
|> Enum.map(fn line ->
line |> String.split("", trim: true) |> Enum.map(&String.to_integer/1)
end)
Part 1
https://adventofcode.com/2021/day/9
Solve: Part 1
defmodule Day9.Part1 do
def size(heightmap) do
width = heightmap |> List.first() |> Enum.count()
height = heightmap |> Enum.count()
{width, height}
end
def is_low_point?(heightmap, x, y, {width, height}) do
point_value = get_value(heightmap, x, y)
for {dx, dy} <- [{-1, 0}, {1, 0}, {0, -1}, {0, 1}],
x + dx > -1,
x + dx < width,
y + dy > -1,
y + dy < height do
get_value(heightmap, x + dx, y + dy)
end
|> Enum.all?(&(&1 > point_value))
end
def risk_level(heightmap, x, y) do
get_value(heightmap, x, y) + 1
end
defp get_value(heightmap, x, y) do
heightmap |> Enum.at(y) |> Enum.at(x)
end
end
{width, height} = size = Day9.Part1.size(inputs)
for x <- 0..(width - 1), y <- 0..(height - 1), Day9.Part1.is_low_point?(inputs, x, y, size) do
Day9.Part1.risk_level(inputs, x, y)
end
|> Enum.sum()
Part 2
https://adventofcode.com/2021/day/9#part2
Solve: Part 2
defmodule Day9.Part2 do
def size(heightmap) do
width = heightmap |> List.first() |> Enum.count()
height = heightmap |> Enum.count()
{width, height}
end
def is_low_point?(heightmap, {x, y}, size) do
point_value = get_value(heightmap, {x, y})
get_adjacent_points({x, y}, size)
|> Enum.map(&get_value(heightmap, &1))
|> Enum.all?(&(&1 > point_value))
end
def risk_level(heightmap, {x, y}) do
get_value(heightmap, {x, y}) + 1
end
def calc_basin_size(heightmap, point, size) do
finished_set = MapSet.new()
not_finished_list = [point]
do_calc_basin_size(heightmap, finished_set, not_finished_list, size)
end
def do_calc_basin_size(
heightmap,
finished_set,
[current_point | not_finished_list],
size
) do
adjacent_points =
get_adjacent_points(current_point, size)
|> Enum.reject(fn point ->
MapSet.member?(finished_set, point) || get_value(heightmap, point) == 9
end)
do_calc_basin_size(
heightmap,
MapSet.put(finished_set, current_point),
Enum.uniq(not_finished_list ++ adjacent_points),
size
)
end
def do_calc_basin_size(_heightmap, finished_set, [], _size) do
MapSet.size(finished_set)
end
defp get_adjacent_points({x, y}, size) do
for {dx, dy} <- [{-1, 0}, {1, 0}, {0, -1}, {0, 1}], is_valid_point?({x + dx, y + dy}, size) do
{x + dx, y + dy}
end
end
defp is_valid_point?({x, y}, {width, height}) do
x > -1 and x < width and y > -1 and y < height
end
defp get_value(heightmap, {x, y}) do
heightmap |> Enum.at(y) |> Enum.at(x)
end
end
alias Day9.Part2
{width, height} = size = Part2.size(inputs)
for x <- 0..(width - 1), y <- 0..(height - 1), Part2.is_low_point?(inputs, {x, y}, size) do
Part2.calc_basin_size(inputs, {x, y}, size)
end
|> Enum.sort(:desc)
|> Enum.take(3)
|> Enum.reduce(1, &(&1 * &2))