aoc2022/day8.livemd
2023-01-02 18:36:35 +01:00

276 lines
5.6 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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]
#  ]
```