# aoc2021-day09b Clojure/JVM program

## source code

; SPDX-License-Identifier: MIT
; Copyright (C) 2021 Tito Sacchi <tito@tilde.team>
; WARNING: These solutions were written while I was still learning Clojure and
; should by no means be taken as examples of good programming practice or fast
; implementations.
(ns aoc.2021.09b
(:require
[clojure.string :as str]))
(defn xor [a b] (and (not (and a b)) (or a b)))
(defn or-else [x default] (if (some? x) x default))
(def input (->> *in*
(slurp)
(str/split-lines)
(mapv (partial mapv #(Integer/parseInt (str %))))))
(defn at [v [x y]] (some-> v (get y) (get x)))
(defn is-minimum [v [x y]]
(let [m (at v [x y])]
(every? #(< m %) (for [dx [-1 0 +1]
dy [-1 0 +1]
; Only shift on X or Y, not both
:when (or (not= dx 0) (not= dy 0))]
(or-else (at v [(+ x dx) (+ y dy)]) 10)))))
(defn all-coords [v]
(for [x (range (count (first v)))
y (range (count v))]
[x y]))
(defn search-basin [v xy]
(loop [latest #{xy}
explored #{}]
(if (empty? latest)
explored
(recur
; Increase L0-norm by 1 and check that the resulting
; coordinates do not have height 9
(set (for [coord latest
dx [-1 0 +1]
dy [-1 0 +1]
:when (xor (= dx 0) (= dy 0))
:let [new-coord (mapv + coord [dx dy])
new-height (at v new-coord)]
:when (some? new-height)
:when (not= new-height 9)
:when (not (explored coord))]
new-coord))
(into explored latest)))))
(println (->> input
(all-coords)
(filter (partial is-minimum input))
(map (partial search-basin input))
(map count)
(sort >)
(take 3)
(apply *)))

## notes, command-line, and program output

NOTES:
Linux
Sun, 23 Jan 2022 16:13:39 GMT
COMMAND LINE:
clojure -cp /usr/share/java/data.priority-map.jar aoc2021_day09b.clj-1.clj 0 < aoc2021_day09b-input4.txt
PROGRAM OUTPUT:
101367520960