Powered by AppSignal & Oban Pro

Day 07

2025/day07.livemd

Day 07

Mix.install([:kino_aoc, :image])

Parse

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2025", "7", System.fetch_env!("LB_ADVENT_OF_CODE_SESSION"))
[start | rest] = String.split(puzzle_input)

start_col = byte_size(start) - byte_size(String.trim_leading(start, "."))
splitters =
  Enum.map(rest, fn row ->
    row
    |> String.to_charlist()
    |> Enum.with_index()
    |> Enum.filter(&(elem(&1, 0) == ?^))
    |> MapSet.new(&elem(&1, 1))
  end)

Part 1

Enum.reduce(splitters, {MapSet.new([start_col]), 0}, fn splits, {beams, count} ->
  import MapSet, only: [intersection: 2, difference: 2, union: 2]
  
  hits = intersection(beams, splits)
  new_beams = for hit <- hits, dx <- [-1, 1], into: MapSet.new(), do: hit + dx
  beams = beams |> difference(hits) |> union(new_beams)

  {beams, MapSet.size(hits) + count}
end)

Part 2

Enum.reduce(splitters, %{start_col => 1}, fn splits, beams ->
  Enum.reduce(splits, beams, fn s, acc ->
    case Map.pop(acc, s) do
      {nil, map} ->
        map

      {count, map} ->
        Map.merge(
          map,
          %{
            (s + 1) => count,
            (s - 1) => count
          },
          fn _k, a, b -> a + b end
        )
    end
  end)
end)
|> Enum.sum_by(&amp;elem(&amp;1, 1))

Image

height = length([start | rest])
width = byte_size(start)

{height, width}
Image.new!(width, height)
|> Image.mutate(fn img ->
  Image.Draw.point!(img, start_col, 0, color: :yellow)

  splitters
  |> Enum.with_index(1)
  |> Enum.reduce(MapSet.new([start_col]), fn {splits, y}, beams ->
    import MapSet, only: [intersection: 2, difference: 2, union: 2]

    for x <- beams do
      Image.Draw.point(img, x, y, color: :green)
    end

    for x <- splits do
      Image.Draw.point(img, x, y, color: :red)
    end
    
    hits = intersection(beams, splits)
    new_beams = for hit <- hits, dx <- [-1, 1], into: MapSet.new(), do: hit + dx
    beams = beams |> difference(hits) |> union(new_beams)

    beams
  end)

  img
end)
|> then(fn {:ok, img} -> img end)
|> Image.resize!(5, interpolate: :nearest)