Powered by AppSignal & Oban Pro

AoC - Day8

2022/elixir/day8.livemd

AoC - Day8

Mix.install([
  {:kino_aoc, git: "https://github.com/ljgago/kino_aoc"}
])

Setup

{:ok, data} = KinoAOC.download_puzzle("2022", "8", System.fetch_env!("LB_AOC_SECRET"))

Solve

tdata = """
30373
25512
65332
33549
35390
"""

defmodule Day8 do
  def solve(data) do
    {dd, rows, cols} = format_inp(data)

    dd_cw = rotate_cw(dd)

    acc_r = task1(dd, cols, false)
    acc_c = task1(dd_cw, rows, true)
    MapSet.union(acc_r, acc_c)
  end

  def task1(dd, size, rot) do
    dd
    |> Enum.with_index()
    |> Enum.reduce(MapSet.new(), fn {row, ind}, acc ->
      {_, lacc} = visible_from_edge(row, ind, acc, size, &l2r_ind/2, false, rot)
      {_, acc} = visible_from_edge(row, ind, lacc, size, &r2l_ind/2, true, rot)
      acc
    end)
  end

  # calculation for rotated indexes need to be simplified
  def visible_from_edge(row, i, acc, size, ind_fun, reverse, rot) do
    row
    |> then(fn row ->
      if reverse, do: Enum.reverse(row), else: row
    end)
    |> Enum.with_index(fn el, ind -> {el, ind_fun.(ind, size)} end)
    |> Enum.reduce({-1, acc}, &count_visible(&1, &2, i, rot, size))
  end

  def l2r_ind(ind, _size), do: ind
  def r2l_ind(ind, size), do: size - 1 - ind

  def count_visible({num, j}, {prev, acc}, i, rot, size) when num > prev do
    k = if rot, do: {size - 1 - j, i}, else: {i, j}
    {num, MapSet.put(acc, k)}
  end

  def count_visible(_el, {prev, acc}, _i, _rot, _size), do: {prev, acc}

  def rotate_cw(matrix) do
    matrix |> Enum.zip_with(fn row -> Enum.reverse(row) end)
  end

  def format_inp(data) do
    dd =
      data
      |> String.trim()
      |> String.split("\n", trim: true)
      |> Enum.map(&String.graphemes/1)
      |> Enum.map(fn row ->
        Enum.map(row, &String.to_integer/1)
      end)

    rows = length(dd)
    cols = dd |> List.first() |> length()
    {dd, rows, cols}
  end
end

# 21, 1662
res = Day8.solve(tdata)
res |> MapSet.size()