Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 8: Treetop Tree House

2022/elixir/day-08.livemd

Day 8: Treetop Tree House

Mix.install([:kino])

Input

input = Kino.Input.textarea("Input")

Part 1

defmodule Parser do
  def parse(str) do
    str
    |> String.split("\n")
    |> Enum.map(fn s ->
      s
      |> String.graphemes()
      |> Enum.map(fn c -> String.to_integer(c) end)
    end)
    |> then(fn rows ->
      for {row, r} <- Enum.with_index(rows), {val, c} <- Enum.with_index(row), into: %{} do
        {{r, c}, val}
      end
    end)
  end
end
defmodule Trees do
  def visible?(map, row, col) do
    Enum.map(
      [
        grids(map, row, col, 0, 1),
        grids(map, row, col, 1, 0),
        grids(map, row, col, 0, -1),
        grids(map, row, col, -1, 0)
      ],
      fn grids -> max?(map, grids, map[{row, col}]) end
    )
    |> Enum.any?()
  end

  defp max?(_, [], _) do
    true
  end

  defp max?(map, grids, val) do
    Enum.all?(grids, fn {row, col} -> map[{row, col}] < val end)
  end

  defp grids(_, _, _, 0, 0) do
    []
  end

  defp grids(map, row, col, offset_row, offset_col) do
    {r, c} = {row + offset_row, col + offset_col}

    if map[{r, c}] do
      [{r, c} | grids(map, r, c, offset_row, offset_col)]
    else
      []
    end
  end
end
map =
  input
  |> Kino.Input.read()
  |> Parser.parse()

map
|> Map.keys()
|> Enum.map(fn {row, col} -> Trees.visible?(map, row, col) end)
|> Enum.count(&amp; &amp;1)

Part 2

defmodule Trees.Part2 do
  def score(map, row, col) do
    Enum.map(
      [
        grids(map, row, col, 0, 1),
        grids(map, row, col, 1, 0),
        grids(map, row, col, 0, -1),
        grids(map, row, col, -1, 0)
      ],
      fn grids -> viewing_trees(map, grids, map[{row, col}]) end
    )
    |> Enum.reduce(fn x, acc -> x * acc end)
  end

  defp viewing_trees(_, [], _) do
    0
  end

  defp viewing_trees(map, grids, val) do
    grids
    |> Enum.take_while(fn {row, col} -> map[{row, col}] < val end)
    |> then(fn shorter_grids ->
      Enum.min([length(shorter_grids) + 1, length(grids)])
    end)
  end

  defp grids(_, _, _, 0, 0) do
    []
  end

  defp grids(map, row, col, offset_row, offset_col) do
    {r, c} = {row + offset_row, col + offset_col}

    if map[{r, c}] do
      [{r, c} | grids(map, r, c, offset_row, offset_col)]
    else
      []
    end
  end
end
map =
  input
  |> Kino.Input.read()
  |> Parser.parse()

map
|> Map.keys()
|> Enum.map(fn {row, col} -> Trees.Part2.score(map, row, col) end)
|> Enum.max()