advent-of-code-2023

My solutions to AoC 2023
git clone git://git.entf.net/advent-of-code-2023
Log | Files | Refs

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))))