Skip to main content Arjen Wiersma

Advent of Code 2025 Day 5

Day 5 of solving The Advent of Code in Clojure .

It was a hectic day, and it is not even over yet. I had multiple trips to the vet while trying to get some work done. To relax a bit I worked on the puzzle of the day. In hindsight I was way too cautious with my solution, but then again, it is to solve a puzzle :D

The first thing to figure out is that a list of numbers is in a collection of ranges. Reading in the file and parsing it to numbers is something that has been done the entire week already, then it is a matter of taking the list of ingredients and mapping over the inventory, filtering out the ones that are not in range.

For part 2 the question becomes how many numbers are in the ranges, and this is a dead give away for something that is so huge that you can not just brute force it. The ranges can overlap, so first thing is to merge adjacent or overlapping ranges. My solution is a bit convoluted, but it works. Then it is a matter of getting the size of each range and adding it up.

clojure code snippet start

(ns day5
  (:require
   [clojure.string :as str]))

(defn parse-input [input]
  (let [[available ingredients]
        (-> input
            slurp
            (str/split #"\n\n"))
        inventory (mapv parse-long (str/split-lines ingredients))
        cleaned (->> available
                     str/split-lines
                     (map #(str/split % #"-"))
                     (mapv (partial mapv #(-> % parse-long))))]
    [cleaned inventory]))

(defn part1 []
  (let [[inventory ingredients] (parse-input "resources/5.in")]
    (->> ingredients
         (map
          #(filter (fn [[min max]]
                     (if (and (>= % min) (<= % max))
                       %
                       nil)) inventory))
         (filter seq)
         count)))

(defn should-merge? [[_ end] [start _]]
  (<= start (inc end)))

(defn merge-two-ranges [[start1 end1] [start2 end2]]
  [(min start1 start2) (max end1 end2)])

(defn merge-overlapping [first-range remaining]
  (let [overlapping (filter (partial should-merge? first-range) remaining)
        merged (reduce merge-two-ranges first-range overlapping)
        unmerged (remove (set overlapping) remaining)]
    [merged unmerged]))

(defn merge-pass [ranges]
  (if (empty? ranges)
    []
    (let [[first-range & remaining] ranges
          [merged remaining-unmerged] (merge-overlapping first-range remaining)]
      (cons merged (merge-pass remaining-unmerged)))))

(defn merge-ranges [ranges]
  (loop [current ranges
         previous nil]
    (let [next-merge (merge-pass current)]
      (if (= next-merge previous)
        next-merge
        (recur next-merge current)))))

(->> "resources/5.in"
     parse-input
     first
     merge-ranges
     (map (fn [[start end]] (- (inc end) start)))
     (reduce +))

clojure code snippet end