I already had a module for working with 2D arrays defined for prior AOC runs, so today’s was straightforward:
defmodule Y2025.Day04 do
def parse(s) do
s
|> String.split("\n")
|> Enum.map(&String.graphemes/1)
|> Array.from_list()
end
def part1(s) do
a = parse(s)
a
|> Array.map_all(fn x, y, v -> removable?(a, x, y, v) end)
|> Array.to_list()
|> Enum.count(& &1)
end
def removable?(a, x, y, v) do
v == "@" &&
Array.neighbors(a, {x, y}) # gives the indexes of all valid neighbors
|> Enum.map(&Array.get(a, &1))
|> Enum.count(&(&1 == "@")) < 4
end
def part2(s) do
a = parse(s)
b = fixed_point(&remove/1, a)
count(a) - count(b)
end
def fixed_point(f, x) do
do_fixed_point(f, count(x), f.(x))
end
def do_fixed_point(f, old_count, a) do
new_count = count(a)
if new_count == old_count do
a
else
do_fixed_point(f, new_count, f.(a))
end
end
def remove(a) do
Array.map_all(a, fn x, y, v -> if removable?(a, x, y, v), do: ".", else: v end)
end
def count(a) do
Array.to_list(a) |> Enum.count(&(&1 == "@"))
end
end






















