(defvar %cave-hash% (hash)) (defstruct cave () name neighbors (:postinit (cv) (assert cv.name) (set [%cave-hash% cv.name] cv))) (defstruct small-cave cave (:method visit (cv path fn) (unless (member cv path) (push cv path) (each ((n cv.neighbors)) n.(visit path fn))))) (defstruct big-cave cave (:method visit (cv path fn) (push cv path) (each ((n cv.neighbors)) n.(visit path fn)))) (defstruct end-cave cave (:method visit (cv path fn) (push cv path) [fn path])) (defun ensure-cave (name) (or [%cave-hash% name] (set [%cave-hash% name] (match-case name ("end" (new end-cave name name)) (@[all @str chr-islower] (new small-cave name name)) (@else (new big-cave name name)))))) (defun read-caves (: (name "input")) (with-stream (s (open-file name)) (whilet ((line (get-line s))) (match `@a-@b` line (let ((ca (ensure-cave a)) (cb (ensure-cave b))) (pushnew cb ca.neighbors) (pushnew ca cb.neighbors)))))) (defun find-paths (fn) (let ((start [%cave-hash% "start"])) (assert start) start.(visit nil fn)))