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(&elem(&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)