— Day 4: Printing Department —
Mix.install([{:kino_aoc, "~> 0.1"}])
Setup
{:ok, puzzle_input} =
KinoAOC.download_puzzle("2025", "4", System.fetch_env!("LB_AOC_SESSION_COOKIE"))
test_input = Kino.Input.textarea("test_input")
test_input = Kino.Input.read(test_input)
defmodule PrintingDeptartment do
@cardinal_directions [:n, :ne, :e, :se, :s, :sw, :w, :nw]
def parse_to_grid(input) do
input
|> String.split()
|> Enum.with_index(&{&2, &1})
|> Enum.map(fn {index, rolls} ->
{index, rolls |> String.graphemes() |> Enum.with_index(&{&2, &1}) |> Map.new()}
end)
|> Map.new()
end
def max_iterations(input) do
~r(@)
|> Regex.scan(input)
|> List.flatten()
|> length()
end
def cardinal_directions(), do: @cardinal_directions
@spec check_adjacent(map(), integer(), integer(), atom()) :: String.t() | nil
def check_adjacent(grid, col, row, :n), do: grid[col][row - 1]
def check_adjacent(grid, col, row, :ne), do: grid[col + 1][row - 1]
def check_adjacent(grid, col, row, :e), do: grid[col + 1][row]
def check_adjacent(grid, col, row, :se), do: grid[col + 1][row + 1]
def check_adjacent(grid, col, row, :s), do: grid[col][row + 1]
def check_adjacent(grid, col, row, :sw), do: grid[col - 1][row + 1]
def check_adjacent(grid, col, row, :w), do: grid[col - 1][row]
def check_adjacent(grid, col, row, :nw), do: grid[col - 1][row - 1]
end
Part 1
import PrintingDeptartment
grid = parse_to_grid(puzzle_input)
for {col_index, row} <- grid, {row_index, "@"} <- row, reduce: 0 do
count ->
adjacent_rolls =
cardinal_directions()
|> Enum.map(&check_adjacent(grid, col_index, row_index, &1))
|> Enum.sum_by(&if(&1 == "@", do: 1, else: 0))
if adjacent_rolls < 4, do: count + 1, else: count
end
Part 2
import PrintingDeptartment
grid = parse_to_grid(puzzle_input)
max_iterations = max_iterations(puzzle_input)
Enum.reduce_while(0..max_iterations, {0, grid}, fn _, {starting_count, grid} ->
{finishing_count, remove_rolls} =
for {col_index, row} <- grid, {row_index, "@"} <- row, reduce: {starting_count, []} do
{count, remove_rolls} ->
adjacent_rolls =
cardinal_directions()
|> Enum.map(&check_adjacent(grid, col_index, row_index, &1))
|> Enum.sum_by(&if(&1 == "@", do: 1, else: 0))
if adjacent_rolls < 4 do
{count + 1, [[col_index, row_index] | remove_rolls]}
else
{count, remove_rolls}
end
end
grid = Enum.reduce(remove_rolls, grid, &put_in(&2, &1, "."))
if finishing_count == starting_count do
{:halt, finishing_count}
else
{:cont, {finishing_count, grid}}
end
end)