Day 8: Treetop Tree House
Mix.install([:kino])
input = Kino.Input.textarea("Please paste your input:")
Part 1
https://adventofcode.com/2022/day/8
horizontal =
input
|> Kino.Input.read()
|> String.split("\n", trim: true)
|> Enum.map(fn line ->
line |> String.split("", trim: true) |> Enum.map(&(&1 |> String.to_integer()))
end)
vertical =
horizontal
|> Enum.zip_with(& &1)
right =
horizontal
|> Enum.with_index()
|> Enum.map(fn {row, row_index} ->
{visibles, _} =
row
|> Enum.with_index()
|> Enum.reduce(
{_visibles = [], _max_height = nil},
fn {height, column_index}, {visibles, max_height} ->
case is_nil(max_height) or height > max_height do
true -> {[{row_index, column_index} | visibles], height}
false -> {visibles, max_height}
end
end
)
visibles
end)
left =
horizontal
|> Enum.with_index()
|> Enum.map(fn {row, row_index} ->
{visibles, _} =
row
|> Enum.with_index()
|> Enum.reverse()
|> Enum.reduce(
{_visibles = [], _max_height = nil},
fn {height, column_index}, {visibles, max_height} ->
case is_nil(max_height) or height > max_height do
true -> {[{row_index, column_index} | visibles], height}
false -> {visibles, max_height}
end
end
)
visibles
end)
top =
vertical
|> Enum.with_index()
|> Enum.map(fn {column, column_index} ->
{visibles, _} =
column
|> Enum.with_index()
|> Enum.reduce(
{_visibles = [], _max_height = nil},
fn {height, row_index}, {visibles, max_height} ->
case is_nil(max_height) or height > max_height do
true -> {[{row_index, column_index} | visibles], height}
false -> {visibles, max_height}
end
end
)
visibles
end)
bottom =
vertical
|> Enum.with_index()
|> Enum.map(fn {column, column_index} ->
{visibles, _} =
column
|> Enum.with_index()
|> Enum.reverse()
|> Enum.reduce(
{_visibles = [], _max_height = nil},
fn {height, row_index}, {visibles, max_height} ->
case is_nil(max_height) or height > max_height do
true -> {[{row_index, column_index} | visibles], height}
false -> {visibles, max_height}
end
end
)
visibles
end)
(right ++ left ++ top ++ bottom)
|> List.flatten()
|> Enum.uniq()
|> Enum.count()
Part 2
https://adventofcode.com/2022/day/8#part2
row_count = horizontal |> Enum.count()
column_count = horizontal |> List.first() |> Enum.count()
trees =
horizontal
|> Enum.with_index()
|> Enum.map(fn {row, row_index} ->
row
|> Enum.with_index()
|> Enum.map(fn {height, column_index} ->
{{row_index, column_index}, height}
end)
end)
|> List.flatten()
|> Map.new()
defmodule Solver do
@directions [{1, 0}, {-1, 0}, {0, 1}, {0, -1}]
def run(trees) do
trees
|> Enum.map(fn {index, _height} -> {index, calc_scenic_score(trees, index)} end)
|> Enum.max_by(fn {_index, scenic_score} -> scenic_score end)
end
def calc_scenic_score(trees, index) do
@directions
|> Enum.map(fn direction -> calc_viewing_distance(trees, index, direction) end)
|> Enum.reduce(&(&1 * &2))
end
def calc_viewing_distance(trees, index, direction) do
origin_height = trees |> Map.get(index)
do_calc_viewing_distance(
trees,
next_index(index, direction),
direction,
origin_height,
0
)
end
defp do_calc_viewing_distance(trees, index, direction, origin_height, viewing_distance) do
height = trees |> Map.get(index)
case is_nil(height) do
true ->
viewing_distance
false ->
case height < origin_height do
true ->
do_calc_viewing_distance(
trees,
next_index(index, direction),
direction,
origin_height,
viewing_distance + 1
)
false ->
viewing_distance + 1
end
end
end
defp next_index({row_index, column_index}, {row_direction, column_direction}) do
{row_index + row_direction, column_index + column_direction}
end
end
Solver.run(trees)