From c864b267707fb91fa8f1c9c93194c51e43b90122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Ytterstr=C3=B6m?= Date: Fri, 9 Dec 2022 13:14:56 +0100 Subject: [PATCH] Add solutions for 2022:8 "Treetop Tree House" --- 2022-elixir/lib/solutions/day_08.ex | 178 +++++++++++++++++++++ 2022-elixir/test/solutions/day_08_test.exs | 29 ++++ 2 files changed, 207 insertions(+) create mode 100644 2022-elixir/lib/solutions/day_08.ex create mode 100644 2022-elixir/test/solutions/day_08_test.exs diff --git a/2022-elixir/lib/solutions/day_08.ex b/2022-elixir/lib/solutions/day_08.ex new file mode 100644 index 0000000..943af9c --- /dev/null +++ b/2022-elixir/lib/solutions/day_08.ex @@ -0,0 +1,178 @@ +defmodule Aoc.Solution.Day08 do + import Aoc.Utils + + @name "Day 8: Treetop Tree House" + @behaviour Solution + + @impl Solution + def get_name, do: @name + + @impl Solution + def present(solution), do: "#{solution} trees are visible" + + @impl Solution + def present_again(solution), do: "Highest scenic score possible is #{solution}" + + @impl Solution + def parse!(raw) do + raw + |> split_lines() + |> Enum.map(fn line -> String.codepoints(line) |> Enum.map(&String.to_integer/1) end) + end + + @impl Solution + def solve(map) do + find_visible(map) + end + + @impl Solution + def solve_again(map) do + scenic_scores(map) + |> MapSet.to_list() + |> Enum.max() + end + + def find_visible(map) do + {h, w} = _map_size(map) + count_visibles(map, 0, 0, h, w, 0) + end + + def count_visibles(_map, y, _x, h, _w, count) when y == h do + count + end + + def count_visibles(map, y, x, h, w, count) when x == w do + count_visibles(map, y + 1, 0, h, w, count) + end + + def count_visibles(map, y, x, h, w, count) do + if visible?(map, y, x, h, w) do + count_visibles(map, y, x + 1, h, w, count + 1) + else + if Enum.zip(map) |> Enum.map(&Tuple.to_list/1) |> visible?(x, y, w, h) do + count_visibles(map, y, x + 1, h, w, count + 1) + else + count_visibles(map, y, x + 1, h, w, count) + end + end + end + + def visible?(_map, y, _x, _h, _w) when y == 0 do + true + end + + def visible?(_map, y, _x, h, _w) when y == h - 1 do + true + end + + def visible?(_map, _y, x, _h, _w) when x == 0 do + true + end + + def visible?(_map, _y, x, _h, w) when x == w - 1 do + true + end + + def visible?(map, y, x, _h, _w) do + value = Enum.at(map, y) |> Enum.at(x) + {l, r} = Enum.at(map, y) |> Enum.split(x) + r = Enum.drop(r, 1) + + Enum.max(l) < value or Enum.max(r) < value + end + + def scenic_scores(map) do + {h, w} = _map_size(map) + + scenic_score(map, Enum.zip(map) |> Enum.map(&Tuple.to_list/1), 0, 0, h, w, MapSet.new()) + end + + def scenic_score(_lr_map, _tb_map, y, _x, h, _w, scores) when y == h do + scores + end + + def scenic_score(lr_map, tb_map, y, x, h, w, scores) when x == w do + scenic_score(lr_map, tb_map, y + 1, 0, h, w, scores) + end + + def scenic_score(lr_map, tb_map, y = 0, x, h, w, scores) do + zero_score(lr_map, tb_map, y, x, h, w, scores) + end + + def scenic_score(lr_map, tb_map, y, x = 0, h, w, scores) do + zero_score(lr_map, tb_map, y, x, h, w, scores) + end + + def scenic_score(lr_map, tb_map, y, x, h, w, scores) when x == w - 1 do + zero_score(lr_map, tb_map, y, x, h, w, scores) + end + + def scenic_score(lr_map, tb_map, y, x, h, w, scores) when y == h - 1 do + zero_score(lr_map, tb_map, y, x, h, w, scores) + end + + def scenic_score(lr_map, tb_map, y, x, h, w, scores) do + value = Enum.at(lr_map, y) |> Enum.at(x) + + {l, r} = lr(lr_map, y, x) + {t, b} = lr(tb_map, x, y) + + %{pos: {y, x}, value: value} + + score = + [t, r, b, l] + |> Enum.map(fn trbl -> view_distance(trbl, value) end) + |> Enum.product() + + scenic_score(lr_map, tb_map, y, x + 1, h, w, MapSet.put(scores, score)) + end + + def zero_score(lr_map, tb_map, y, x, h, w, scores) do + scenic_score(lr_map, tb_map, y, x + 1, h, w, MapSet.put(scores, 0)) + end + + def lr(map, y, x) do + {l, r} = Enum.at(map, y) |> Enum.split(x) + {Enum.reverse(l), Enum.drop(r, 1)} + end + + def _map_size(map) do + h = Enum.count(map) + w = List.first(map) |> Enum.count() + + {h, w} + end + + @doc """ + Calculate the trees that can be seen in a given direction. + + ## Examples + + iex> Aoc.Solution.Day08.view_distance([3], 5) + 1 + iex> Aoc.Solution.Day08.view_distance([5, 2], 5) + 1 + iex> Aoc.Solution.Day08.view_distance([1, 2], 5) + 2 + iex> Aoc.Solution.Day08.view_distance([3, 5, 3], 5) + 2 + iex> Aoc.Solution.Day08.view_distance([3, 3], 5) + 2 + iex> Aoc.Solution.Day08.view_distance([4, 9], 5) + 2 + + """ + def view_distance(list, value, count \\ 0) + + def view_distance([], _value, count) do + count + end + + def view_distance([nearest | _beyond], value, count) when value <= nearest do + count + 1 + end + + def view_distance([_nearest | beyond], value, count) do + view_distance(beyond, value, count + 1) + end +end diff --git a/2022-elixir/test/solutions/day_08_test.exs b/2022-elixir/test/solutions/day_08_test.exs new file mode 100644 index 0000000..5114159 --- /dev/null +++ b/2022-elixir/test/solutions/day_08_test.exs @@ -0,0 +1,29 @@ +defmodule Day08Test do + use ExUnit.Case + doctest Aoc.Solution.Day08 + import Aoc.Solution.Day08 + + @input ~s( + 30373 + 25512 + 65332 + 33549 + 35390 + ) + + test "08: Treetop Tree House, part 1" do + expected = 21 + + result = @input |> parse!() |> solve() + + assert result == expected + end + + test "08: Treetop Tree House, part 2" do + expected = 8 + + result = @input |> parse!() |> solve_again() + + assert result == expected + end +end