276 lines
5.6 KiB
Markdown
276 lines
5.6 KiB
Markdown
# AOC 2022 - Day 8
|
||
|
||
```elixir
|
||
Mix.install(
|
||
[
|
||
{:req, "~> 0.3.3"},
|
||
{:nx, "~> 0.4.1"},
|
||
{:exla, "~> 0.4.1"}
|
||
],
|
||
config: [nx: [default_backend: EXLA.Backend]]
|
||
)
|
||
```
|
||
|
||
## Puzzle description
|
||
|
||
[Day 8: Treetop Tree House](https://adventofcode.com/2022/day/8).
|
||
|
||
## Input
|
||
|
||
```elixir
|
||
defmodule Load do
|
||
def input do
|
||
aoc_session = System.fetch_env!("LB_AOC_SESSION")
|
||
input_url = "https://adventofcode.com/2022/day/8/input"
|
||
Req.get!(input_url, headers: [cookie: "session=#{aoc_session}"]).body
|
||
end
|
||
end
|
||
```
|
||
|
||
## Solution
|
||
|
||
```elixir
|
||
defmodule Util do
|
||
def process(input) do
|
||
input
|
||
|> String.split("\n", trim: true)
|
||
|> Enum.map(fn row ->
|
||
row
|
||
|> String.split("", trim: true)
|
||
|> Enum.map(&String.to_integer/1)
|
||
end)
|
||
end
|
||
|
||
def process2(input) do
|
||
input
|
||
|> String.split()
|
||
|> Enum.with_index()
|
||
|> Enum.flat_map(&parse_line/1)
|
||
|> Map.new(fn {v, k} -> {k, v} end)
|
||
end
|
||
|
||
defp parse_line({string, row}) do
|
||
string
|
||
|> String.graphemes()
|
||
|> Enum.with_index()
|
||
|> Enum.map(fn {char, column} ->
|
||
{String.to_integer(char), {row + 1, column + 1}}
|
||
end)
|
||
end
|
||
|
||
def count_visible(line) do
|
||
line
|
||
|> Enum.with_index()
|
||
|> Enum.map_reduce(0, fn {h, n}, max ->
|
||
cond do
|
||
n == 0 ->
|
||
{true, h}
|
||
|
||
h <= max ->
|
||
{false, max}
|
||
|
||
true ->
|
||
{true, h}
|
||
end
|
||
end)
|
||
|> elem(0)
|
||
end
|
||
|
||
def transpose(v) do
|
||
v
|
||
|> List.zip()
|
||
|> Enum.map(&Tuple.to_list/1)
|
||
end
|
||
|
||
def score(trees, row, column, rows, columns) do
|
||
left_score(trees, row, column, rows, columns) *
|
||
right_score(trees, row, column, rows, columns) *
|
||
up_score(trees, row, column, rows, columns) *
|
||
down_score(trees, row, column, rows, columns)
|
||
end
|
||
|
||
defp left_score(trees, row, column, _rows, _columns) do
|
||
list =
|
||
for c <- (column - 1)..1 do
|
||
trees[{row, c}]
|
||
end
|
||
|> Enum.with_index()
|
||
|
||
len = length(list)
|
||
|
||
list
|
||
|> Enum.reduce_while(1, fn {height, position}, score ->
|
||
if trees[{row, column}] > height and position + 1 != len,
|
||
do: {:cont, score + 1},
|
||
else: {:halt, score}
|
||
end)
|
||
end
|
||
|
||
defp right_score(trees, row, column, _rows, columns) do
|
||
list =
|
||
for c <- (column + 1)..columns do
|
||
trees[{row, c}]
|
||
end
|
||
|> Enum.with_index()
|
||
|
||
len = length(list)
|
||
|
||
list
|
||
|> Enum.reduce_while(1, fn {height, position}, score ->
|
||
if trees[{row, column}] > height and position + 1 != len,
|
||
do: {:cont, score + 1},
|
||
else: {:halt, score}
|
||
end)
|
||
end
|
||
|
||
defp up_score(trees, row, column, _rows, _columns) do
|
||
list =
|
||
for r <- (row - 1)..1 do
|
||
trees[{r, column}]
|
||
end
|
||
|> Enum.with_index()
|
||
|
||
len = length(list)
|
||
|
||
list
|
||
|> Enum.reduce_while(1, fn {height, position}, score ->
|
||
if trees[{row, column}] > height and position + 1 != len,
|
||
do: {:cont, score + 1},
|
||
else: {:halt, score}
|
||
end)
|
||
end
|
||
|
||
defp down_score(trees, row, column, rows, _columns) do
|
||
list =
|
||
for r <- (row + 1)..rows do
|
||
trees[{r, column}]
|
||
end
|
||
|> Enum.with_index()
|
||
|
||
len = length(list)
|
||
|
||
list
|
||
|> Enum.reduce_while(1, fn {height, position}, score ->
|
||
if trees[{row, column}] > height and position + 1 != len,
|
||
do: {:cont, score + 1},
|
||
else: {:halt, score}
|
||
end)
|
||
end
|
||
end
|
||
```
|
||
|
||
```elixir
|
||
defmodule Part1 do
|
||
def run(input) do
|
||
trees =
|
||
input
|
||
|> Util.process()
|
||
|
||
{trees, horizontal} =
|
||
trees
|
||
|> Enum.map_reduce([], fn line, acc ->
|
||
visible =
|
||
[
|
||
Util.count_visible(line),
|
||
line |> Enum.reverse() |> Util.count_visible() |> Enum.reverse()
|
||
]
|
||
|> Enum.zip()
|
||
|> Enum.map(&(elem(&1, 0) or elem(&1, 1)))
|
||
|
||
{line, [visible | acc]}
|
||
end)
|
||
|> then(fn {trees, horizontal} ->
|
||
{trees, Enum.reverse(horizontal)}
|
||
end)
|
||
|
||
{_, vertical} =
|
||
trees
|
||
|> Util.transpose()
|
||
|> Enum.map_reduce([], fn col, acc ->
|
||
visible =
|
||
[
|
||
Util.count_visible(col),
|
||
col |> Enum.reverse() |> Util.count_visible() |> Enum.reverse()
|
||
]
|
||
|> Enum.zip()
|
||
|> Enum.map(&(elem(&1, 0) or elem(&1, 1)))
|
||
|
||
{col, [visible | acc]}
|
||
end)
|
||
|> then(fn {trees, vertical} ->
|
||
{trees, Enum.reverse(vertical) |> Util.transpose()}
|
||
end)
|
||
|
||
horizontal
|
||
|> Enum.zip(vertical)
|
||
|> Enum.map(fn {h, v} ->
|
||
h
|
||
|> Enum.zip(v)
|
||
|> Enum.map(&(elem(&1, 0) or elem(&1, 1)))
|
||
end)
|
||
|> Enum.map(fn line ->
|
||
Enum.count(line, & &1)
|
||
end)
|
||
|> Enum.sum()
|
||
end
|
||
end
|
||
|
||
defmodule Part2 do
|
||
def run(input) do
|
||
trees = Util.process2(input)
|
||
|
||
{rows, columns} =
|
||
trees
|
||
|> Map.keys()
|
||
|> Enum.max()
|
||
|
||
for i <- 2..(rows - 1), j <- 2..(columns - 1) do
|
||
{i, j, Util.score(trees, i, j, rows, columns)}
|
||
end
|
||
|> Enum.max_by(fn {_, _, x} -> x end)
|
||
|> elem(2)
|
||
end
|
||
end
|
||
|
||
ExUnit.start(autorun: false)
|
||
|
||
defmodule Test do
|
||
use ExUnit.Case, async: true
|
||
@example_input ~s(30373
|
||
25512
|
||
65332
|
||
33549
|
||
35390)
|
||
@input Load.input()
|
||
|
||
test "it loads the input" do
|
||
assert String.length(@input) > 0
|
||
end
|
||
|
||
test "part 1 example" do
|
||
assert Part1.run(@example_input) === 21
|
||
end
|
||
|
||
test "part 1" do
|
||
assert Part1.run(@input) === 1816
|
||
end
|
||
|
||
test "part 2 example" do
|
||
assert Part2.run(@example_input) === 8
|
||
end
|
||
|
||
test "part 2" do
|
||
assert Part2.run(@input) === 0
|
||
end
|
||
end
|
||
|
||
ExUnit.run()
|
||
|
||
# [
|
||
# [1, 1, 1, 1, 1],
|
||
# [1, 1, 1, 0, 1],
|
||
# [1, 1, 0, 1, 1],
|
||
# [1, 0, 1, 0, 1],
|
||
# [1, 1, 1, 1, 1]
|
||
# ]
|
||
```
|