Does anybody miss old-school Lisp mutable data structures where you're kinda a functional language but you still have to worry about the difference between values and object identity? I'm not sure anybody does, but this is a retrocomputing house.
(ql:quickload :str)
(defun parse-line (line)
(let* ((id-and-nums (str:split ":" line))
(id (parse-integer (car id-and-nums)))
(nums (mapcar #'parse-integer (str:split "," (cadr id-and-nums)))))
(cons id nums)))
(defun read-inputs (filename)
(mapcar #'parse-line (uiop:read-file-lines filename)))
(defun fishbone-push (fb n)
(if (null fb)
(list (vector nil n nil))
(let ((rib (car fb)))
(cond ((and (null (aref rib 0))
(< n (aref rib 1)))
(setf (aref rib 0) n)
fb)
((and (null (aref rib 2))
(> n (aref rib 1)))
(setf (aref rib 2) n)
fb)
((null (cdr fb))
(nconc fb (fishbone-push (cdr fb) n)))
(t
(fishbone-push (cdr fb) n)
fb)))))
(defun make-fishbone (ns)
(reduce #'fishbone-push ns :initial-value nil))
(defun quality (ns)
(let ((fb (make-fishbone ns)))
(parse-integer (apply #'str:concat
(mapcar #'(lambda (rib) (write-to-string (aref rib 1)))
fb)))))
(defun main-1 (filename)
(quality (cdar (read-inputs filename))))
(defun main-2 (filename)
(let ((qualities (mapcar #'quality (mapcar #'cdr (read-inputs filename)))))
(- (apply #'max qualities) (apply #'min qualities))))
(defun complex-quality (idx ns)
(let* ((fb (make-fishbone ns))
(quality
(parse-integer (apply #'str:concat
(mapcar #'(lambda (rib) (write-to-string (aref rib 1)))
fb))))
(rib-qualities
(mapcar #'(lambda (rib)
(parse-integer
(apply #'str:concat
(mapcar #'write-to-string
(remove-if #'null (coerce rib 'list))))))
fb)))
(list (cons :quality quality)
(cons :rib-qualities rib-qualities)
(cons :index idx))))
(defun list> (ns1 ns2)
(cond ((null ns1) nil)
((null ns2) t)
((> (car ns1) (car ns2)) t)
((< (car ns1) (car ns2)) nil)
(t (list> (cdr ns1) (cdr ns2)))))
(defun cq> (cq1 cq2)
(let ((q1 (cdr (assoc :quality cq1)))
(q2 (cdr (assoc :quality cq2))))
(cond ((> q1 q2) t)
((< q1 q2) nil)
(t
(let ((rq1 (cdr (assoc :rib-qualities cq1)))
(rq2 (cdr (assoc :rib-qualities cq2))))
(cond ((list> rq1 rq2) t)
((list> rq2 rq1) nil)
(t
(> (cdr (assoc :index cq1))
(cdr (assoc :index cq2))))))))))
(defun checksum (idxs)
(loop for idx in idxs
for n from 1 to (length idxs)
sum (* idx n)))
(defun main-3 (filename)
(let ((inputs (read-inputs filename))
(sword-qualities (make-hash-table)))
(loop for idx-ns in inputs
do (setf (gethash (car idx-ns) sword-qualities)
(complex-quality (car idx-ns) (cdr idx-ns))))
(let ((sorted-idxs
(sort (mapcar #'car inputs)
#'(lambda (idx1 idx2)
(cq> (gethash idx1 sword-qualities)
(gethash idx2 sword-qualities))))))
(checksum sorted-idxs))))