(ql:quickload '(fiveam cl-ppcre uiop arrows)) (defstruct valve name flow connections) (defun data (filename) (with-open-file (in filename) (loop for line = (read-line in nil nil) while line collect (cl-ppcre:register-groups-bind (val (#'parse-integer flow) others) ("Valve (\\w+) has flow rate=(\\d+); tunnels? leads? to valves? (.+)" line) `(,(intern val) ,flow ,@(mapcar #'intern (cl-ppcre:split ", " others))))))) (let ((gra (list (make-valve :name 'aa :connections '(bb cc)) (make-valve :name 'bb :connections '(ll cc))))) (valve-connections (find 'll gra :key #'valve-name))) (defun next-steps (current graph paths) (flet ((appropriate (next) (and (not (gethash next paths)) ;; not visited (setf (gethash next paths) (cons next (gethash current paths)))))) (remove-if-not #'appropriate ;; name flow connections -> cddr (cddr (assoc current graph :test #'eq))))) (defun shortest-path (graph from to) (let ((paths (make-hash-table :test #'eq))) (setf (gethash from paths) (list from)) (labels ((traverse (queue paths) (unless (null queue) (let ((next (next-steps (car queue) graph paths))) (unless (member to next :test #'eq) (traverse (append (cdr queue) next) paths)))))) (traverse (list from) paths)) ;; (loop for k being the hash-keys in paths using (hash-value v) ;; do (format t "~a=>~a~%" k v)) (gethash to paths))) (defun path-release (paths) (loop for (name flow distance) in paths sum (1+ distance) into runtime sum (* flow (- 30 runtime)) into release finally (return release))) (= 340 (path-release '((a 5 1) (b 8 2)))) (shortest-path (data "eg-in") 'AA 'CC) (defun worthwhile-graph (graph) (let ((interesting (remove-if #'zerop graph :key #'cadr))) (loop for (from flow) in (cons '(aa 0) interesting) collect (nconc (list from flow) (loop for (to) in interesting when (not (eq from to)) collect (cons to (1- (length (shortest-path graph from to))))))))) (defun travel-time (paths) (loop for (name flow distance) in paths sum (1+ distance))) (defun next-options (current graph open time-left) (flet ((appropriate (next) (destructuring-bind (next-node-name . time-there) next (and (not (member next-node-name open :test #'eq)) (< (1+ time-there) time-left))))) (remove-if-not #'appropriate (cddr (assoc current graph :test #'eq))))) (let* ((open '(aa)) (action-graph (worthwhile-graph (data "input")))) ;; (path-release) ;; (loop for k being the hash-keys in open using (hash-value v) ;; do (format t "~a=>~a~%" k v)) (labels ((traverse (graph node open time-left current-flow) (let ((next (next-options node graph open time-left))) (if (null next) current-flow (arrows:-<> (lambda (next-node) (destructuring-bind (name . time-there) next-node (let ((flow (cadr (assoc name graph :test #'eq))) (time-left (- time-left time-there 1))) (traverse graph name (cons name open) time-left (+ current-flow (* flow time-left)))))) (mapcar <> next) (reduce #'max <>)))))) (traverse action-graph 'aa open 30 0) ) )