main.lisp (4379B)
1 (defpackage #:aoc/main 2 (:use #:cl) 3 (:import-from #:alexandria) 4 (:import-from #:local-time) 5 (:import-from #:dexador) 6 (:nicknames #:aoc) 7 (:export 8 #:today 9 #:run-day 10 #:new-day 11 #:main)) 12 (in-package #:aoc/main) 13 14 (defconstant +year+ 2023) 15 16 (defvar *system* (asdf:find-system '#:aoc)) 17 18 (defvar *cookie* nil) 19 20 (defun system-pathname (file) 21 (asdf:system-relative-pathname *system* file)) 22 23 (defun input-pathname (day) 24 (system-pathname (format nil "input/~A.txt" day))) 25 26 (defun source-pathname (day) 27 (system-pathname (format nil "src/day-~A.lisp" day))) 28 29 (defun test-pathname (day) 30 (system-pathname (format nil "t/day-~A.lisp" day))) 31 32 (defun transform-input (input day) 33 (typecase input 34 (pathname 35 (values 36 (open input) 37 #'close)) 38 (string 39 (values 40 (make-string-input-stream input) 41 #'close)) 42 (stream 43 (values 44 input 45 nil)) 46 (null 47 (values 48 (open (input-pathname day)) 49 #'close)))) 50 51 (defun day-fun (day) 52 (let* ((fun (format nil "DAY-~A" day)) 53 (package (format nil "AOC/~A" fun)) 54 (package (or (find-package package) 55 (progn 56 (asdf:load-system (string-downcase package)) 57 (find-package package)))) 58 (fun (find-symbol fun package))) 59 (symbol-function fun))) 60 61 (defun today () 62 (local-time:timestamp-day (local-time:now))) 63 64 (defun run-day (&optional (day (today)) input) 65 (multiple-value-bind (input cleanup) 66 (transform-input input day) 67 (unwind-protect 68 (funcall (day-fun day) input) 69 (when cleanup 70 (funcall cleanup input))))) 71 72 (defun new-day (&optional (day (today))) 73 (let ((input-pathname (input-pathname day)) 74 (source-pathname (source-pathname day)) 75 (test-pathname (test-pathname day))) 76 (ensure-directories-exist input-pathname) 77 (ensure-directories-exist test-pathname) 78 (unless (probe-file input-pathname) 79 (restart-case 80 (unless *cookie* 81 (error "Advent Of Code cookie is unset")) 82 (use-cookie (cookie) 83 :interactive (lambda () 84 (format t "Cookie: ") 85 (list (read-line))) 86 (setf *cookie* cookie))) 87 (alexandria:write-string-into-file 88 (dex:get (format nil "https://adventofcode.com/~A/day/~A/input" +year+ day) 89 :cookie-jar (cookie:make-cookie-jar 90 :cookies (list (cookie:make-cookie :name "session" 91 :value *cookie* 92 :domain ".adventofcode.com")))) 93 input-pathname)) 94 (values 95 (unless (probe-file source-pathname) 96 (with-open-file (stream source-pathname :direction :output) 97 (let ((fun (make-symbol (format nil "DAY-~A" day))) 98 (package (make-symbol (format nil "AOC/DAY-~A" day))) 99 (*print-case* :downcase)) 100 (format stream "~S~%~S~%~%(defun ~A (input)~% )" 101 `(defpackage ,package 102 (:use #:cl #:aoc/utils) 103 (:export 104 ,fun)) 105 `(in-package ,package) 106 fun))) 107 ;; return function, makes it easy to jump into the file from Emacs 108 (day-fun day)) 109 (unless (probe-file test-pathname) 110 (let ((package (make-symbol (format nil "AOC-TEST/DAY-~A" day))) 111 (*print-case* :downcase)) 112 (with-open-file (stream test-pathname :direction :output) 113 (format stream "~S~%~S~%~%(define-test test-day-~A~% ()~% )" 114 `(defpackage ,package 115 (:use #:cl #:lisp-unit2)) 116 `(in-package ,package) 117 day)) 118 (asdf:load-system (format nil "aoc-test/day-~A" day)) 119 (symbol-function 120 (find-symbol (format nil "TEST-DAY-~A" day) 121 (find-package package)))))))) 122 123 (defun main () 124 (let* ((args (uiop:command-line-arguments)) 125 (today (or (first args) 126 (today))) 127 (input (or (and #1=(second args) 128 (parse-namestring #1#)) 129 (input-pathname today)))) 130 (dolist (task (multiple-value-list (time (run-day today input)))) 131 (format t "~A~%" task))))