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

Day 3

Day3.livemd

Day 3

Mix.install([
  {:kino_aoc, "~> 0.1.5"}
])

Input

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2023", "3", System.fetch_env!("LB_AOC_SESSION"))
lines = puzzle_input |> String.split("\n")

Part 1

symbols =
  lines
  |> Enum.with_index()
  |> Enum.flat_map(fn {l, y} ->
    Regex.scan(~r/[^\d.]/, l, return: :index)
    |> Enum.map(fn [{x, _}] -> {{y, x}, String.at(l, x)} end)
  end)
  |> Map.new()

number_positions =
  lines
  |> Enum.with_index()
  |> Enum.flat_map(fn {l, y} ->
    Regex.scan(~r/\d+/, l, return: :index)
    |> Enum.map(fn [{x, l}] -> {{y, x}, l} end)
  end)
  |> Map.new()

number_positions
|> Enum.filter(fn {{y, x}, l} ->
  Enum.any?(for dy <- -1..1, dx <- -1..l, do: symbols[{y + dy, x + dx}])
end)
|> Enum.map(fn {{y, x}, l} ->
  Enum.at(lines, y) |> String.slice(x, l) |> String.to_integer()
end)
|> Enum.sum()

Part 2

adjacent_symbol_positions =
  number_positions
  |> Enum.flat_map(fn {{y, x}, l} ->
    for dy <- -1..1,
        dx <- -1..l,
        pos = {y + dy, x + dx},
        symbols[pos] == "*",
        do: {pos, Enum.at(lines, y) |> String.slice(x, l) |> String.to_integer()}
  end)
  |> Enum.group_by(&amp;elem(&amp;1, 0), &amp;elem(&amp;1, 1))
  |> Enum.filter(fn {_, n} -> Enum.count(n) == 2 end)
  |> Enum.map(fn {_, [a, b]} -> a * b end)
  |> Enum.sum()