defmodule Aoc2023.Day3 do use Aoc2023 @symbols [ ?$, ?%, ?&, ?*, ?+, ?-, ?/, ?=, ?@, ?# ] defp adjacents({y, x, len}, grid) do [ {y, x - 1}, {y, x + len} | for(y <- [y - 1, y + 1], x <- (x - 1)..(x + len), do: {y, x}) ] |> Enum.filter(fn {y, x} -> 0 <= y and y < length(grid) and 0 <= x and x < length(Enum.at(grid, y)) end) end defp adjacents({y, x}, grid), do: adjacents({y, x, 1}, grid) defp get({y, x}, grid) do grid |> Enum.at(y) |> Enum.at(x) end defp get({y, x, len}, grid) do x..(x + len - 1) |> Enum.map(&get({y, &1}, grid)) end defp contains({y, x}, numbers) do numbers |> Enum.filter(fn {ny, nx, nlen} -> ny == y and nx <= x and x < nx + nlen end) end defp number(num, grid) do num |> get(grid) |> Enum.map(&(&1 - ?0)) |> Integer.undigits() end def parse(input) do grid = input |> String.split("\n") |> Enum.map(&String.to_charlist/1) { grid, grid |> Enum.with_index() |> Enum.flat_map(fn {row, y} -> row |> runs(&(?0 <= &1 && &1 <= ?9)) |> Enum.map(fn {x, len} -> {y, x, len} end) end) } end def part1({grid, numbers}) do numbers |> Enum.map(id &&& adjacents(grid)) |> Enum.filter( pipe( elem(1) |> Enum.any?(&(get(&1, grid) in @symbols)) ) ) |> Enum.map( pipe( elem(0) |> number(grid) ) ) |> Enum.sum() end def part2({grid, numbers}) do grid |> Enum.with_index() |> Enum.flat_map(fn {row, y} -> row |> Enum.with_index() |> Enum.filter(&(elem(&1, 0) == ?*)) |> Enum.map(&{y, elem(&1, 1)}) |> Enum.map(id &&& adjacents(grid)) |> Enum.map(pipe( elem(1) |> Enum.flat_map(pipe(contains(numbers))) |> Enum.uniq() )) |> Enum.filter(pipe( length() |> then(&(&1 == 2))) ) |> Enum.map(pipe( Enum.map(pipe(number(grid))) |> Enum.product )) end) |> Enum.sum end end