(ql:quickload "split-sequence")
(defun read-data (path)
(with-open-file (stream path)
(loop for line = (read-line stream nil)
while line collect line)))
(defparameter *input-source*
(cadr *posix-argv*))
;; Useful in testing
;; (defparameter *input-source* "example_input")
;; (defparameter *input-source* "input")
(defun parse-digit (d)
(- (char-code d) (char-code #\0)))
(defun parse-line (line)
(map 'list #'parse-digit line))
(defun parse-input (lines)
(let ((numbers (map 'list #'parse-line lines)))
(make-array (list (length numbers) (length (car numbers)))
:initial-contents numbers)))
(defparameter *data* (parse-input (read-data *input-source*)))
(defun within-grid-p (max-x max-y x y)
(and (>= x 0)
(< x max-x)
(>= y 0)
(< y max-y)))
(defun neighbours (grid x y i j)
(loop for (a b) in '((1 0) (-1 0) (0 1) (0 -1))
when (within-grid-p x y (+ i a) (+ j b))
collect (aref grid (+ i a) (+ j b))))
(defun local-minimum-p (grid x y i j)
(every (lambda (neighbour) (< (aref grid i j) neighbour)) (neighbours grid x y i j)))
(defun low-points (grid)
(destructuring-bind (x y) (array-dimensions grid)
(loop for i from 0 below x
nconcing
(loop for j from 0 below y
when (local-minimum-p grid x y i j)
collect (list i j)))))
(defun part1 (grid)
(reduce #'+ (mapcar (lambda (coordinate) (1+ (apply #'aref grid coordinate))) (low-points grid))))
(defun flood (hash grid x y)
(destructuring-bind (max-x max-y) (array-dimensions grid)
(if (and (not (gethash (list x y) hash))
(within-grid-p max-x max-y x y)
(/= 9 (aref grid x y)))
(progn
(setf (gethash (list x y) hash) T)
(+ 1
(flood hash grid (1+ x) y)
(flood hash grid (1- x) y)
(flood hash grid x (1+ y))
(flood hash grid x (1- y))))
0)))
(defun basin (grid low-point)
(let ((hash (make-hash-table :test #'equal)))
(flood hash grid (car low-point) (cadr low-point))
(hash-table-count hash)))
(defun part2 (grid)
(reduce #'*
(subseq (sort (mapcar (lambda (point) (basin grid point))
(low-points grid))
#'>)
0 3)))
(format t "===== Part 1 =====")
(format t "Result: ~A~%~%" (time (part1 *data*)))
(format t "===== Part 2 =====")
(format t "Result: ~A~%" (time (part2 *data*)))