Day 4
Mix.install([
{:kino_aoc, "~> 0.1"}
])
Setup
{:ok, input} = KinoAOC.download_puzzle("2025", "4", System.fetch_env!("LB_AOC_SESSION"))
{:ok,
"@..@@.@.@@@....@@..@@@.@@@@@@..@..@@.@.@@.@@...@.@.@...@@.....@..@@.@@@@.@@@.@@@..@.@@@@@@.@@@@@@@@@@...@@..@..@@.@@@@@@..@@@...@.@@@@@@\n@.@@.@@@@@@..@.@@@@.@@@@@@.@.@@@@..@@@@@@@@.@.@@@...@.@@@@@@..@..@.@@@.@@@@.@@@@.@@@@@@@@@@@..@@@@@@.@@.@@.@.@@@@.@@@@@@@@@@...@.@@.....\n@@.@@@@@.@.@@.@@@@@@@@..@@@@@.@@.@@.@@@@.@..@@@.@.@@@@@@@@.@.@@@@@@@@@@@...@@@@@@.@@@@@@@.@@@@.@@.@@.@@.@@.@@.@@@..@@..@@.@@@@@@.....@.@\n@@@@.@..@@@.@@.@@.@@.@@@@.@.@.@@@@@@@.@@.@@@...@.@@.@@@@@@.@@@@@.@@.@@@@@.@@..@.@@@@@@@.@@@@.@.@@@.@...@@.@@@@.@.@@@@.@.@.@@@@.@@.@@@@@.\n.@@@@.@@@.@@.@@@.@@.@.@.@@..@.@@..@.@@.@@@@@@@@.@.@.@.@@.@@@@..@@.@..@..@.@@@@@@@.@@@.@.@@@@@..@@@@@@@.@@@.@@.@@..@@...@@..@.@@@.@@@@@.@\n@@@@@@@.@@.@.@.@@@.@@.@@@@@@@@..@@@@.@@.@@@..@@..@@@@@.@@@.@.@.@..@...@@.@..@@@.@.@@.@.@.@@.@@@@@@.@..@.@@@.@..@..@@..@@@@.@@.@@@@@@@.@.\n..@@@.@@.@...@.@@@.@@.@.@@@@@@@@@.@@.@..@.@@@.@@@@.@.@.@@@.@@@.@.@@@@@@.@@.@...@@@..@@.@.@.@@@.@@.@.@@@..@.@@@@@..@.@@.@..@.@@.@...@@@..\n@@.@.@.@@..@@@@.@.@@.@@@@@@@@.@.@@.@.@@@..@..@@@@@@@...@@@@@@.@@@@@@.@.@@@@@@@@...@.@@@@@@@@@@@.@@@..@.@.@@@.@@@.@..@@@.@..@@.@.....@@.@\n@@.@.@@@@.@@@@..@..@.@@.@@.@@..@.@@@...@@@.@@.@.@@@@..@@.@.@@@.@@@@..@@@.@.@@@..@@...@@.@....@@@.@@@@@@@..@@.....@@.@@@@..@.@@@.@@@.@@@@\n@.@@@@..@@@@..@..@@@@@@.@@..@.@.@@@@@..@@@.@@@.@.@@@@..@@@@.@.@@.....@@@@@@@@@@.@..@@.@@@@@@@@.@@@..@..@.@@@@....@.@.@@@@.@@.@.@.@.@@@@.\n@.@@....@.@@@@@..@@@@..@@.@@@.@@@.@.@..@@@.@@.@@.@@@@@@@@.@@@.@@.@..@@@@..@@@@@@@@@@.@@@...@@@@.@@.@@@@@.@@@..@@..@@.@@@.@@@@@@@@.@@.@@@\n@@@.@@.@.@.@.@@.@@@..@@.@..@@@..@@@@..@@@@..@.@@@@@@@.@@@@..@.@@..@@.@..@@....@@@@@@@@@@@@@.@..@@@@..@@@@@.@@@.@...@@.@..@.@@@@@@@.@@.@@\n.@...@.@@@.@@@.@.@@.@...@@.@@.@@.@.@@..@@.@.@@.@.@@@@@@@.@.@@@@.@@@@@@@.@@@..@@@@@@..@@@.@@.@.@..@@@@@..@.@@@@...@..@@@@@@@.@@@@@@@.@.@@\n@@@@.@.@.@.@.@.@@@@@@.@@@@@@@..@@@@..@@.@....@..@.@@..@@@@@@@.@@@@@@@@...@@@..@@@@..@@@@@.@@@@.@@..@@.@@.@@@@.@@..@@@@@@@@.@@.@@@@@...@@\n@@.@.@@@..@....@@@@@.@@..@@@...@...@@@.@@@@@@..@@@@@@@@.@@@@@@..@.@@...@.@@.@..@@..@@@@.@..@..@@@.@.@@@.@.@@@@@.@@@@@.@.@@..@@@@..@.@.@.\n@.@@@@.@..@@.@..@.@@.@.@@@@..@.@.@..@@@@.@@@.@@.@@.@@@@@@.@......@..@@.@@@...@...@@@.@@@.@@.@@@.@@..@.@.@@@@..@@@@@.@@@@@@.@@..@@@@@@@@@\n@.@@@@@@@@.@.@@.@.@.@...@@@@@..@@.@.@@@@@@@.@@...@@....@@@..@@.@@@@.@.@@@@@.@@@@.@@..@.@@@....@@.@.@@@.@@@@@.@@@@@@.@@@@@..@...@....@@@.\n@.@@@@.@..@@@@@@@@@@@@@@..@@@@...@@@@@@@@..@@@@@.@@@@...@@.@@@@.@@@@@@@.@@..@.@@..@@@.@......@@@@@@@@@@.@@@@@@@..@@@@@..@..@@@@@@.@@@@@.\n@@..@@..@@@@@@@@@@.@...@@.@@@@@@@@..@..@@@.@.@.@.@@.@@@@@@@@@@@..@.@..@@@.@....@@@.@@@@.@.@@@.@@@.@@..@@@.@.@@@@@..@@.@...@@@@@@@.@@@...\n@..@@.@@.@@@@.@..@@.@@@@@@@@.@@.@.@@@@@..@.@@.@..@.@@.@@@@@@@@@@@...@.@@@.@@@.@.@@@@@@@@@@..@.@@@@@@..@@@@@.@.@@.@@@@..@@@@@....@@..@@.@\n..@@.@..@@..@@.@.@.@@@.@..@.@@@@@@@@@..@@@@@.@@@@@@.@@.@@.@..@@@@@@@..@...@..@@..@@.@@@.@..@@@@.@..@@@.@@@.@@@.@.@@@@@@@.@.@.@@@.@@@@...\n...@@....@.@@@@@@@@...@@@@@@@.@.@@@@.@@@...@@@@@@@@@@@@@@@@@@@@@@@@.@..@@@@@@.@@@@..@@.@@...@@@@..@..@@@@@@@...@.@@.@@.@@..@.@.@.@.@@@@.\n.@@@.@@@.@..@@.@.@..@.@@...@@.@@.@.@@..@@.@...@..@@@@@@..@@@@.@@.@..@@@.@@.@.@.@.@@.@@@.@@@.@..@.@@@.@.@@@..@@.@.@@@.@.@@@@@.@@@@@...@@@\n@@.@..@@.@@@@@@@@@@@@...@@.@@@@.@@@@..@..@..@@@@@@@.@.@@..@@@.@@@.@@@@@@@@@@@.@@@@...@@.@.@.@@..@.@@.@....@..@@.@.@@..@@@@@@...@@.@@@.@@\n@.@@.@@.@@@@@..@..@@@@@@@@..@..@@.....@@@@.@.@@@@@.@@@.@@@@@@.@@@@.@.@.@.@@.@.@@.@@.@@@@@.@@@.@..@.@@...@.@@@@@@@@@..@@@...@.@.@.@.@@@@.\n.....@@@@@@@.@@.@.@...@@@..@..@@.@@@@.....@..@.@@@@@.@@.@@...@@.@.@@.@@@.@@@.@@@@@@.@.@@@@@@..@@@@@@@@...@..@.@..@@@..@..@@.@....@@@@@@@\n@@@@.@@@...@@.@.@@.@@.@.@@.@@@@.@@@@@@@@@@.@@@.@..@@.@@@.@@.@..@..@@@@.@..@@@@.@@@@...@@.@@.@..@@@@@@@.@@..@@@.@@@@@@.@@@.@@@...@.@@@@.@\n.@.@@.....@.@@@@@@@...@..@@.@@..@.@.@@..@..@@..@@@@@@@...@@..@@@.@.@@@..@@.@@...@@..@@@@@@.@...@..@.@@@@@@@@@.@@.@@.@...@@@@@@@.@@@@@..@\n@.@@@@@...@.@@@@@@@...@@.@.@@@@@@.@@@@@@..@..@.@.@...@@....@@@.@...@@@@@@....@@@..@@@@.@@..@.@@@@....@@@@@@@@@@@@@.@.@@.@@@@@@.@.@..@.@@\n@@@@.@@@@@.@..@@@@@@@.@@@@@@@@@@@@@.@.@@.@@@@.@@@@@..@@@@@@.@....@@@@@@@@.@@@@@@.@.@..@@@@.@.....@.@.@@@@@@@@@@@@@@@@@@@@@." <> ...}
ex = """
..@@.@@@@.
@@@.@.@.@@
@@@@@.@.@@
@.@@@@..@.
@@.@@@@.@@
.@@@@@@@.@
.@.@.@.@@@
@.@@@.@@@@
.@@@@@@@@.
@.@.@@@.@.
"""
"..@@.@@@@.\n@@@.@.@.@@\n@@@@@.@.@@\n@.@@@@..@.\n@@.@@@@.@@\n.@@@@@@@.@\n.@.@.@.@@@\n@.@@@.@@@@\n.@@@@@@@@.\n@.@.@@@.@.\n"
Solution
defmodule Day4 do
@neighbors [
{-1, 0}, # left
{ 1, 0}, # right
{-1, -1}, # top left
{ 1, -1}, # top right
{-1, 1}, # bottom left
{ 1, 1}, # bottom right
{ 0, -1}, # top
{ 0, 1} # bottom
]
def parse(str) do
String.split(str)
|> Enum.with_index()
|> Enum.flat_map(fn {row, y} ->
String.split(row, "", trim: true)
|> Enum.with_index()
|> Enum.map(fn {v, x} ->
{v, {x, y}}
end)
end)
|> Enum.reduce(%{}, fn {v, pos}, map ->
Map.put(map, pos, {v, pos})
end)
end
def adj(grid, {x, y}) do
@neighbors
|> Enum.map(fn {dx, dy} ->
Map.get(grid, {x + dx, y + dy}, nil)
end)
|> Enum.filter(& &1) # truthy values only please
end
def rollcount(grid, pos, max) do
grid
|> adj(pos)
|> Enum.count_until(fn {v, _} -> v == "@" || v == "x" end, max)
end
def print(grid) do
{max_x, _} = grid |> Map.keys() |> Enum.max_by(fn {x, _} -> x end)
{_, max_y} = grid |> Map.keys() |> Enum.max_by(fn {_, y} -> y end)
for y <- 0..max_y do
for x <- 0..max_x do
{v, _pos } = Map.get(grid, {x, y}, {".", {x, y}})
IO.write(v)
end
IO.puts("")
end
end
def forklift_accessible_rolls(grid) do
grid |> Map.values |> Enum.filter(fn
{"@", pos } -> rollcount(grid, pos, 5) < 4
{ _v, _pos } -> false
end)
end
def solve(str) do
parse(str)
|> forklift_accessible_rolls()
|> Enum.count()
end
def remove_rolls(grid), do: remove_rolls(grid, 0)
def remove_rolls(grid, removed_count_acc) do
to_remove = grid |> forklift_accessible_rolls()
removed_count = length(to_remove)
if removed_count > 0 do
Enum.reduce(to_remove, grid, fn {_v, pos}, acc -> Map.delete(acc, pos) end)
|> remove_rolls(removed_count_acc + removed_count)
else
{ grid, removed_count_acc }
end
end
def solve2(str) do
grid = parse(str)
{_, total_removed } = remove_rolls(grid)
total_removed
end
end
{:module, Day4, <<70, 79, 82, 49, 0, 0, 25, ...>>, {:solve2, 1}}
Part 1
Day4.parse(ex)
%{
{1, 3} => {".", {1, 3}},
{9, 9} => {".", {9, 9}},
{1, 1} => {"@", {1, 1}},
{1, 5} => {"@", {1, 5}},
{8, 9} => {"@", {8, 9}},
{5, 0} => {"@", {5, 0}},
{5, 7} => {".", {5, 7}},
{1, 8} => {"@", {1, 8}},
{5, 4} => {"@", {5, 4}},
{4, 5} => {"@", {4, 5}},
{6, 1} => {"@", {6, 1}},
{6, 0} => {"@", {6, 0}},
{9, 4} => {"@", {9, 4}},
{9, 1} => {"@", {9, 1}},
{3, 4} => {"@", {3, 4}},
{8, 4} => {"@", {8, 4}},
{4, 9} => {"@", {4, 9}},
{7, 7} => {"@", {7, 7}},
{6, 9} => {"@", {6, 9}},
{5, 8} => {"@", {5, 8}},
{8, 7} => {"@", {8, 7}},
{9, 2} => {"@", {9, 2}},
{5, 3} => {"@", {5, 3}},
{0, 5} => {".", {0, 5}},
{3, 3} => {"@", {3, 3}},
{4, 6} => {".", {4, 6}},
{6, 2} => {"@", {6, 2}},
{3, 7} => {"@", {3, 7}},
{9, 8} => {".", {9, 8}},
{7, 0} => {"@", {7, 0}},
{1, 6} => {"@", {1, 6}},
{7, 4} => {".", {7, 4}},
{3, 1} => {".", {3, 1}},
{2, 5} => {"@", {2, 5}},
{7, 6} => {"@", {7, 6}},
{4, 4} => {"@", {4, 4}},
{7, 2} => {".", {7, 2}},
{3, 9} => {".", {3, 9}},
{9, 6} => {"@", {9, 6}},
{6, 5} => {"@", {6, 5}},
{1, 0} => {".", {1, 0}},
{2, 2} => {"@", {2, 2}},
{8, 3} => {"@", {8, 3}},
{8, 8} => {"@", {8, 8}},
{0, 1} => {"@", {0, 1}},
{7, 1} => {".", {7, 1}},
{5, 1} => {".", {5, ...}},
{7, 5} => {"@", {...}},
{0, ...} => {"@", ...},
{...} => {...},
...
}
Day4.parse(ex) |> Day4.adj({0,0})
[{".", {1, 0}}, {"@", {1, 1}}, {"@", {0, 1}}]
Day4.parse(ex) |> Day4.rollcount({2,0}, 10)
3
Day4.parse(ex) |> Day4.rollcount({0, 0}, 20)
2
Day4.parse(ex) |> Day4.rollcount({7, 0}, 5)
4
Day4.parse(ex) |> Day4.print()
..@@.@@@@.
@@@.@.@.@@
@@@@@.@.@@
@.@@@@..@.
@@.@@@@.@@
.@@@@@@@.@
.@.@.@.@@@
@.@@@.@@@@
.@@@@@@@@.
@.@.@@@.@.
[:ok, :ok, :ok, :ok, :ok, :ok, :ok, :ok, :ok, :ok]
Day4.solve(ex)
13
Day4.solve(input)
1395
Part 2
Day4.solve(ex)
13
Day4.solve2(input)
8451