# Day 04 ```elixir Mix.install([:kino_aoc]) ``` ## Parse ```elixir {:ok, puzzle_input} = KinoAOC.download_puzzle("2025", "4", System.fetch_env!("LB_ADVENT_OF_CODE_SESSION")) ``` ```elixir rolls = puzzle_input |> String.split("\n", trim: true) |> Enum.with_index() |> Enum.flat_map(fn {line, row} -> line |> String.to_charlist() |> Enum.with_index() |> Enum.filter(&(elem(&1, 0) == ?@)) |> Enum.map(&{elem(&1, 1), row}) end) |> MapSet.new() ``` ## Implementation ```elixir defmodule PaperRolls do defp adjacent({x, y}) do for dx <- -1..1, dy <- -1..1, {dx, dy} != {0, 0}, do: {x + dx, y + dy} end def movable?(pos, map) do pos |> adjacent() |> Enum.count(&(&1 in map)) |> then(&(&1 < 4)) end end ``` ## Part 1 ```elixir Enum.count(rolls, &PaperRolls.movable?(&1, rolls)) ``` ## Part 2 ```elixir cleaned = Stream.repeatedly(fn -> [] end) |> Enum.reduce_while(rolls, fn _, acc -> removable = Enum.filter(acc, &PaperRolls.movable?(&1, acc)) |> MapSet.new() case MapSet.difference(acc, removable) do ^acc -> {:halt, acc} remaining -> {:cont, remaining} end end) ``` ```elixir MapSet.size(rolls) - MapSet.size(cleaned) ```