(ql:quickload '(fiveam cl-ppcre)) (defparameter eg-input "467..114.. ...*...... ..35..633. ......#... 617*...... .....+.58. ..592..... ......755. ...$.*.... .664.598..") (defun inbounds (row-max col-max) (lambda (cell) (destructuring-bind (row . col) cell (and (<= 0 row row-max) (<= 0 col col-max))))) (defun in-line-numbers (line symbol-pos) (loop for (num-start num-end) on (cl-ppcre:all-matches "\\d+" line) by #'cddr when (<= (1- num-start) symbol-pos num-end) collect (parse-integer (subseq line num-start num-end)))) (defun adjacent-numbers (rows row limits symbol-pos) (loop for y in (list (1- row) row (1+ row)) when (funcall limits (cons y symbol-pos)) nconc (in-line-numbers (aref rows y) symbol-pos))) (defun solver (lines symbol-regex reducer) (let* ((rows (make-array (length lines) :initial-contents lines :adjustable t)) (limits (inbounds (1- (length lines)) (1- (length (car lines)))))) (loop for row below (length lines) sum (loop for (start _end) on (cl-ppcre:all-matches symbol-regex (aref rows row)) by #'cddr sum (funcall reducer (adjacent-numbers rows row limits start)))))) (defun solver1 (lines) (solver lines "[^.\\d]" (lambda (numbers) (apply #'+ numbers)))) (defun solver2 (lines) (solver lines "\\*" (lambda (ratios) (if (= 2 (length ratios)) (* (car ratios) (cadr ratios)) 0)))) (fiveam:test solutions (fiveam:is (= 4361 (solver1 (uiop:split-string eg-input :separator '(#\Newline))))) (fiveam:is (= 467835 (solver2 (uiop:split-string eg-input :separator '(#\Newline))))) (fiveam:is (= 531561 (solver1 (uiop:read-file-lines "input")))) (fiveam:is (= 83279367(solver2 (uiop:read-file-lines "input")))))