summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--data/08/example3.txt10
-rw-r--r--day08.lisp99
2 files changed, 109 insertions, 0 deletions
diff --git a/data/08/example3.txt b/data/08/example3.txt
new file mode 100644
index 0000000..5b3fa58
--- /dev/null
+++ b/data/08/example3.txt
@@ -0,0 +1,10 @@
+LR
+
+11A = (11B, XXX)
+11B = (XXX, 11Z)
+11Z = (11B, XXX)
+22A = (22B, XXX)
+22B = (22C, 22C)
+22C = (22Z, 22Z)
+22Z = (22B, 22B)
+XXX = (XXX, XXX)
diff --git a/day08.lisp b/day08.lisp
new file mode 100644
index 0000000..af749e2
--- /dev/null
+++ b/day08.lisp
@@ -0,0 +1,99 @@
+(defstruct mymap left right)
+
+(defstruct problem instructions maps)
+
+(defun remove-spaces (string)
+ (coerce (loop for c across string
+ if (char/= c #\Space)
+ collect c) 'string))
+
+(defun parse-mymap (string)
+ (let* ((string (remove-spaces string))
+ (posequal (position #\= string :test #'char=))
+ (poscomma (position #\, string :start (1+ posequal)))
+ (key (subseq string 0 posequal))
+ (left (subseq string (1+ (1+ posequal)) poscomma))
+ (right (subseq string (1+ poscomma) (1- (length string)))))
+ (values key (make-mymap :left left :right right))))
+
+(defun parse-maps (stream)
+ (let ((ht (make-hash-table :test 'equal)))
+ (loop for line = (read-line stream nil)
+ while line
+ if (< 0 (length line))
+ do (multiple-value-bind (key mymap) (parse-mymap line)
+ (setf (gethash key ht) mymap)))
+ ht))
+
+(defun parse-problem (filespec)
+ (with-open-file (stream filespec)
+ (let* ((instructions (read-line stream) )
+ (maps (parse-maps stream)))
+ (make-problem :instructions instructions :maps maps))))
+
+(defun count-path (problem start end)
+ (let ((instructions (problem-instructions problem))
+ (maps (problem-maps problem)))
+ (labels ((f (&optional (i 0) (current start) (acc 0))
+ (if (equal current end)
+ acc
+ (let* ((instruction (aref instructions i))
+ (currentmap (gethash current maps))
+ (nxt (if (char= #\L instruction)
+ (mymap-left currentmap)
+ (mymap-right currentmap))))
+ (f (mod (1+ i) (array-total-size instructions)) nxt (1+ acc))))))
+ (f))))
+
+(defun solve1 (filespec)
+ (let ((problem (parse-problem filespec)))
+ (count-path problem (subseq "AAA" 0) (subseq "ZZZ" 0))))
+
+(defun ends-with (string char)
+ (char= char (aref string (1- (array-total-size string)))))
+
+(defun find-starts (maps char)
+ (loop for string being each hash-key of maps
+ if (ends-with string char)
+ collect string))
+
+(defun find-possible (maps instructions start endchar)
+ (loop with res = '()
+ and vis = '()
+ and n = (length instructions)
+ for current = start then next
+ for count = 0 then (1+ count)
+ for i = 0 then (mod (1+ i) n)
+ as final = (ends-with current endchar)
+ as instruction = (aref instructions i)
+ as cmap = (gethash current maps)
+ as next = (if (char= instruction #\L) (mymap-left cmap) (mymap-right cmap))
+ as mem = (member (list current i) vis :test #'equal :key #'cdr)
+ if (and final mem)
+ do (push (list (caar mem) (- count (caar mem))) res)
+ if (and final mem (= (length vis) (length res)))
+ return res
+ if (and final (not mem))
+ do (push (list count current i) vis)))
+
+(defun count-multiple-paths (problem startchar endchar)
+ (let* ((instructions (problem-instructions problem))
+ (maps (problem-maps problem))
+ (starts (find-starts maps startchar)))
+ (let ((loops (mapcar (lambda (start) (find-possible maps instructions start endchar))
+ starts)))
+ (when (every (lambda (l) (and (= 1 (length l)) (= (caar l) (cadar l)))) loops)
+ ;; LCM only makes sense if there is exactly 1 loop for each
+ ;; path and that loop has the same start and period (which is
+ ;; the case for the input)
+ (apply #'lcm (mapcar #'caar loops))))))
+
+(defun solve2 (filespec)
+ (let ((problem (parse-problem filespec)))
+ (count-multiple-paths problem #\A #\Z)))
+
+(print (solve1 "data/08/example.txt")) ;; 2
+(print (solve1 "data/08/example2.txt")) ;; 6
+(print (solve1 "data/08/input.txt")) ;; 21883
+(print (solve2 "data/08/example3.txt")) ;; 6 (nil for this code)
+(print (solve2 "data/08/input.txt")) ;; 12833235391111