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