bjorng
Erlang Core Team
Advent of Code 2025 - Day 4
defmodule Day04 do
def part1(input) do
grid = parse(input)
Enum.count(removable(grid))
end
def part2(input) do
grid = parse(input)
remove(grid, 0)
end
defp remove(grid, num_removed) do
case removable(grid) do
[] ->
num_removed
[_|_]=ps ->
grid = Enum.reduce(ps, grid, &(Map.delete(&2, &1)))
remove(grid, num_removed + length(ps))
end
end
defp removable(grid) do
grid
|> Enum.filter(fn {_position, what} -> what === ?@ end)
|> Enum.filter(fn {position, _} ->
adjacent_squares(position)
|> Enum.count(fn position->
get_grid(grid, position) === ?@
end)
|> then(&(&1 < 4))
end)
|> Enum.map(&elem(&1, 0))
end
defp get_grid(grid, position) do
Map.get(grid, position, ?.)
end
defp adjacent_squares({row, col}) do
[{row - 1, col - 1}, {row - 1, col}, {row - 1, col + 1},
{row, col - 1}, {row, col + 1},
{row + 1, col - 1}, {row + 1, col}, {row + 1, col + 1}]
end
defp parse(input) do
parse_grid(input)
end
defp parse_grid(grid) do
grid
|> Enum.with_index
|> Enum.flat_map(fn {line, row} ->
String.to_charlist(line)
|> Enum.with_index
|> Enum.flat_map(fn {char, col} ->
position = {row, col}
[{position, char}]
end)
end)
|> Map.new
end
end
Most Liked
lud
I love immutability because you can just compare a term against its previous version without managing the copy yourself:
case Map.drop(grid, Map.keys(accessibles(grid))) do
^grid -> grid
new_grid -> loop_remove(new_grid)
end
In context:
defmodule AdventOfCode.Solutions.Y25.Day04 do
alias AoC.Input
def parse(input, _part) do
lines = Input.stream!(input, trim: true)
for {row, y} <- Enum.with_index(lines),
{"@", x} <- Enum.with_index(String.graphemes(row)),
reduce: %{},
do: (grid -> Map.put(grid, {x, y}, true))
end
def part_one(grid) do
map_size(accessibles(grid))
end
def part_two(grid) do
clear_grid = loop_remove(grid)
map_size(grid) - map_size(clear_grid)
end
defp loop_remove(grid) do
case Map.drop(grid, Map.keys(accessibles(grid))) do
^grid -> grid
new_grid -> loop_remove(new_grid)
end
end
defp accessibles(grid) do
Map.filter(grid, &less_than_four_neighbors?(&1, grid))
end
defp less_than_four_neighbors?({{x, y}, _}, grid) do
for(nx <- (x - 1)..(x + 1), ny <- (y - 1)..(y + 1), {nx, ny} != {x, y}, do: {nx, ny})
|> Enum.count(&Map.has_key?(grid, &1))
|> Kernel.<(4)
end
end
5
dompdv
This one was pretty straightforward. Using MapSet (and MapSet operations) and the “for” construct, which is more concise than reduce. No optimization at all
defmodule AdventOfCode.Solution.Year2025.Day04 do
import Enum, only: [flat_map: 2, with_index: 1, sum: 1, filter: 2, count: 1]
def parse(input) do
# Create a MapSet of the {row,col} of the rolls
with_index(String.split(input, "\n", trim: true))
|> flat_map(fn {line, row} ->
for {c, col} <- with_index(to_charlist(line)), c == ?@, do: {row, col}
end)
|> MapSet.new()
end
def vec_sum({a, b}, {c, d}), do: {a + c, b + d}
@around for r <- -1..1, c <- -1..1, {r, c} != {0, 0}, do: {r, c}
def liftable(roll, rolls) do
sum(for delta <- @around, vec_sum(roll, delta) in rolls, do: 1) < 4
end
def part1(input) do
rolls = parse(input)
rolls |> filter(&liftable(&1, rolls)) |> count()
end
def remove_all(rolls, counter \\ 0) do
# Identify the removable rolls
removable = for roll <- rolls, liftable(roll, rolls), into: MapSet.new(), do: roll
# If nothing to remove, we're done, else remove the removable rolls and increment the counter
if MapSet.size(removable) == 0,
do: counter,
else: remove_all(MapSet.difference(rolls, removable), counter + MapSet.size(removable))
end
def part2(input), do: input |> parse() |> remove_all()
end
5
mudasobwa
Creator of Cure
I did my best to never construct a matrix, binaries rock. Also, Stream.iterate(&calc/1) is a lovely approach to code reuse.
defmodule Day4 do
@input "day4_1.input" |> File.read!() |> String.trim()
defp read_input(input) do
input
|> String.split(["\s", "\n"], trim: true)
|> then(fn [h | t] ->
stub = "." |> List.duplicate(byte_size(h)) |> Enum.join()
[stub, h | t] ++ [stub]
end)
|> Enum.map(&("." <> &1 <> "."))
end
def calc({input, acc} \\ {@input, 0}) do
input
|> read_input()
|> Enum.chunk_every(3, 1, :discard)
|> Enum.reduce({0, []}, fn [h, l, t], {count, res} ->
res = ["" | res]
Enum.reduce(1..(byte_size(l) - 2), {count, res}, fn idx, {count, [hres | tres]} ->
with <<_::binary-size(idx - 1), r1::binary-size(3), _::binary>> <- h,
<<_::binary-size(idx - 1), r21::binary-size(1), r22::binary-size(1),
r23::binary-size(1), _::binary>> <- l,
<<_::binary-size(idx - 1), r3::binary-size(3), _::binary>> <- t,
false <- r22 == "@" and String.count(r1 <> r21 <> r23 <> r3, "@") < 4,
do: {count, [r22 <> hres | tres]},
else: (_ -> {count + 1, ["x" <> hres | tres]})
end)
end)
|> then(fn {count, data} -> {Enum.join(data, "\n"), acc + count} end)
end
def calc_2(input \\ @input) do
{input, 0}
|> Stream.iterate(&calc/1)
|> Enum.reduce_while({0, -1}, fn
{_, prev}, {acc, prev} -> {:halt, acc}
{_, count}, {acc, _} -> {:cont, {count, acc}}
end)
end
end
3
Popular in Challenges
I said I was on a break, but I took a sneak peak and it looked fun so…
Part 1 completes in half a millisecond with a single pass of the ...
New
Hey there :wave:
No magic or algorithmic finesse today, I just finished the challenge and I my code is quite slow (1sec for part1, 3se...
New
Note by the Moderators: This topic is to talk about Day 5 of the Advent of Code.
For general discussion about the Advent of Code 2018 an...
New
So… that’s it? Everyone is stuck on part 2? :slight_smile: I looked at Reddit hints and thought I probably wouldn’t have come up with the...
New
This topic is about Day 5 of the Advent of Code 2021.
We have a private leaderboard (shared with users of Erlang Forums ):
https://adve...
New
part 1:
https://github.com/rugyoga/aoc2021/blob/main/day8.exs
part 2:
https://github.com/rugyoga/aoc2021/blob/main/day8b.exs
New
Note: This topic is to talk about Day 5 of the Advent of Code 2019.
There is a private leaderboard for elixirforum members. You can join...
New
I’m having so much fun working on the “Protohackers” challenges, I never got into Advent of Code much but this has been amazing. The chal...
New
Note by the Moderators: This topic is for general discussion about the Advent of Code 2018.
To prevent people from being spoiled about s...
New
Note: This topic is to talk about Day 9 of the Advent of Code 2019.
There is a private leaderboard for elixirforum members. You can join...
New
Other popular topics
I have an umbrella app.
Some of the apps inside depend on other apps in the umbrella, unsurprisingly.
I’m writing a test for one of the...
New
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
Hello, how can I check the Phoenix version ?
Thanks !
New
Hi,
I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
Using vs code and installed ElixirLS: support and debugger.
And I got an error popped up on start up says
Failed to run ‘elixir’ comma...
New
Please see the new poll here: Which code editor or IDE do you use? (Poll) (2022 Edition)
It’s been a while since we first asked this, I...
New
Credo is smart enough to check for (something like) this:
assert length(the_list) == 0
with this response:
Checking if an enum is empt...
New
Hello everyone,
Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









