5.6 KiB
5.6 KiB
AOC 2022 - Day 8
Mix.install(
[
{:req, "~> 0.3.3"},
{:nx, "~> 0.4.1"},
{:exla, "~> 0.4.1"}
],
config: [nx: [default_backend: EXLA.Backend]]
)
Puzzle description
Input
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
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
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]
# ]