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
This topic is about Day 9 of the Advent of Code 2020 .
Thanks to @egze, we have a private leaderboard:
https://adventofcode.com/2020/le...
New
This topic is about Day 3 of the Advent of Code 2020 .
Thanks to @egze, we have a private leaderboard:
https://adventofcode.com/2020/le...
New
Took me a minute to remember my binary math :smile: :grimacing:..
import Bitwise
__DIR__
|> Path.join("puzzle.txt")
|> File.strea...
New
Note: This topic is to talk about Day 7 of the Advent of Code 2019 .
There is a private leaderboard for elixirforum members. You can joi...
New
Monkeys fitted squarely as GenServers in my head. My initial problem was using cast instead of call; I imagine impolite monkeys slinging...
New
This topic is about Day 9 of the Advent of Code 2021 .
We have a private leaderboard (shared with users of Erlang Forums):
https://adve...
New
Phew, this one took a while to get right. My naive attempts was way to slow so I reached for Dijkstras shortest path algorithm.. and that...
New
Here is my solution for day 1 of Advent of Code:
defmodule Day01 do
def part1(input) do
all = parse(input)
{first, second} = E...
New
I mapped both the cards and every possible hand to numeric values and sorted them. In part 2 I could only think of replacing the jokers w...
New
Note: This topic is to talk about Day 4 of the Advent of Code 2019.
There is a private leaderboard for elixirforum members. You can join...
New
Other popular topics
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
New
I tried installing
elixir 1.11.2
erlang 23.3.4
via asdf in my zsh shell. Enabled the versions locally and globally.
When I list them ...
New
Hi!
In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir?
Searched the docs for ip address and the web, no good results.
Thanks!
New
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!)
This post collects co...
New
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
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








