Advent of Code 2025 Day 4
Day 4 of solving {{< backlink “aoc” “The Advent of Code” >}} in {{< backlink “clojure” “Clojure” >}}.
The first grid problem of the season! The first part was really suspiciously easy, read in the grid, find all the rolls and look at its 8 neighbors, eliminating it if there are less than 4. The 2nd part was a nice progression on this.
Instead of just saying how many should be eliminated, we do it until there are no eliminations possible. This is highly reminiscent of a Conway Game of Life puzzle. In Clojure this is quite nicely done by reading the grid as a vector, and then ranging over all the coordinates. On each coordinate just take a look at the neighbors and apply the logic. Its pure nature means that the functions are already working in the right way to do this repeatedly.
(ns day4
(:require
[clojure.string :as str]))
(defn get-cell [grid [row col]]
(when (and (>= row 0) (< row (count grid))
(>= col 0) (< col (count (first grid))))
(get-in grid [row col])))
(defn neighbors-8 [grid [row col] & {:keys [filter-fn] :or {filter-fn (constantly true)}}]
(->> (for [r (range (dec row) (+ row 2))
c (range (dec col) (+ col 2))
:when (not= [r c] [row col])]
[r c])
(map #(get-cell grid %))
(remove nil?)
(filter filter-fn)))
(defn all-coords [grid]
(for [row (range (count grid))
col (range (count (first grid)))]
[row col]))
(defn print-grid-with-marks [grid marked-positions]
(doseq [row (range (count grid))]
(doseq [col (range (count (first grid)))]
(let [pos [row col]
cell (get-in grid pos)]
(print (if (some #(= % pos) marked-positions)
"X "
(str cell " ")))))
(println)))
(defn mark-movable [grid]
(let [all-pos (all-coords grid)
marks (remove nil? (map (fn [pos]
(let [n (neighbors-8 grid pos :filter-fn #(not= % \.))]
(when (and (= \@ (get-cell grid pos)) (< (count n) 4))
pos)))
all-pos))]
marks))
(defn remove-marked [grid marked-positions]
(let [rows (count grid)
cols (count (first grid))]
(vec
(for [row (range rows)]
(vec
(for [col (range cols)]
(let [pos [row col]
cell (get-in grid pos)]
(if (and (= cell \@) (some #(= % pos) marked-positions))
\.
cell))))))))
(def play
(fn [grid]
(loop [g grid
moved 0]
(let [marks (mark-movable g)
mark-count (count marks)]
(if (= 0 mark-count)
moved
(recur (remove-marked g marks)
(+ moved mark-count)))))))
(def grid
(->> "resources/4.in"
slurp
str/split-lines
(mapv vec)))
;; part 1
(count (mark-movable grid))
;; part 2
(play grid)
Really enjoyed it and well within my daily commute to solve.