day22.lisp (5527B)
1 (defpackage #:adventofcode2022/day22 2 (:use #:cl #:adventofcode2022)) 3 (in-package #:adventofcode2022/day22) 4 5 (defun turn (facing direction) 6 (mod 7 (+ facing 8 (case direction 9 (:left -1) 10 (:right 1) 11 (otherwise 0))) 12 4)) 13 14 (defun walk (coords facing) 15 (let ((x (car coords)) 16 (y (cadr coords))) 17 (case facing 18 (0 (list (1+ x) y)) 19 (1 (list x (1+ y))) 20 (2 (list (1- x) y)) 21 (3 (list x (1- y)))))) 22 23 (defun parse-input (inputs) 24 (loop with map = (make-hash-table :test 'equal) 25 with last-line? = nil 26 with min-x = nil 27 for line in inputs 28 for length = (length line) 29 for y from 0 30 when last-line? 31 return (values 32 map 33 (loop with start = 0 34 with results = nil 35 for i from 0 36 while (< i length) 37 for char = (aref line i) 38 if (not (digit-char-p char)) 39 do (push (parse-integer (subseq line start i)) results) 40 and do (push (case char (#\R :right) (#\L :left)) results) 41 and do (setf start (1+ i)) 42 finally (push (parse-integer (subseq line start i)) results) 43 finally (return (nreverse results))) 44 (list min-x 0)) 45 when (= length 0) 46 do (setf last-line? t) 47 do (loop for x from 0 below length 48 for char = (aref line x) 49 for type = (case (aref line x) 50 (#\# :wall) 51 (#\. :empty) 52 (otherwise nil)) 53 when (and (= y 0) 54 (eq type :empty) 55 (or (null min-x) 56 (< x min-x))) 57 do (setf min-x x) 58 when type 59 do (setf (gethash (list x y) map) type)))) 60 61 (defun wrap-around (map pos last-pos dir) 62 (declare (ignore dir)) 63 (let ((x (car pos)) 64 (y (cadr pos))) 65 (if (= y (cadr last-pos)) 66 (list 67 (if (> x (car last-pos)) 68 (loop for i from 0 69 for tile = (gethash (list i y) map) 70 when tile 71 return i) 72 (loop for i from (car last-pos) 73 for (tile exists) = (multiple-value-list (gethash (list i y) map)) 74 while exists 75 maximize i)) 76 y) 77 (list 78 x 79 (if (> y (cadr last-pos)) 80 (loop for i from 0 81 for tile = (gethash (list x i) map) 82 when tile 83 return i) 84 (loop for i from (1+ y) 85 for tile = (gethash (list x i) map) 86 while tile 87 maximize i)))))) 88 89 (defparameter *wrap-map* (make-hash-table :test 'equal)) 90 91 (loop for i below 50 92 do (flet ((m (x y dir nx ny ndir) 93 (setf (gethash (list x y dir) *wrap-map*) (list nx ny ndir)))) 94 (m 50 i 2 0 (- 149 i) 0) 95 (m (+ 50 i) 0 3 0 (+ 150 i) 0) 96 97 (m (+ 100 i) 0 3 i 199 3) 98 (m 149 i 0 99 (- 149 i) 2) 99 (m (+ 100 i) 49 1 99 (+ 50 i) 2) 100 101 (m 50 (+ 50 i) 2 i 100 1) 102 (m 99 (+ 50 i) 0 (+ 100 i) 49 3) 103 104 (m 99 (+ 100 i) 0 149 (- 49 i) 2) 105 (m (+ 50 i) 149 1 49 (+ 150 i) 2) 106 107 (m 0 (+ 100 i) 2 50 (- 49 i) 0) 108 (m i 100 3 50 (+ 50 i) 0) 109 110 (m 0 (+ 150 i) 2 (+ 50 i) 0 1) 111 (m 49 (+ 150 i) 0 (+ 50 i) 149 3) 112 (m i 199 1 (+ 100 i) 0 1))) 113 114 (defun wrap-cube (map pos last-pos dir) 115 (declare (ignore map pos)) 116 (or (gethash (append last-pos (list dir)) 117 *wrap-map*) 118 (error "Wrap result is null!"))) 119 120 (defun walk-map (map directions start-pos wrap-function) 121 (loop with facing = 0 122 with current-pos = start-pos 123 for direction in directions 124 if (numberp direction) 125 do (loop repeat direction 126 for next-coord = (walk current-pos facing) 127 for next-facing = facing 128 for next-tile = (gethash next-coord map) 129 when (null next-tile) 130 do (let ((wrap (funcall wrap-function map next-coord current-pos facing))) 131 (setf next-coord (subseq wrap 0 2)) 132 (when (> (length wrap) 2) 133 (setf next-facing (caddr wrap)))) 134 and do (setf next-tile (gethash next-coord map)) 135 never (eq next-tile :wall) 136 do (setf current-pos next-coord 137 facing next-facing)) 138 else 139 do (setf facing (turn facing direction)) 140 finally (return (progn 141 (list (* 4 (1+ (car current-pos))) (* 1000 (1+ (cadr current-pos))) facing) 142 (+ (* 1000 (1+ (cadr current-pos))) 143 (* 4 (1+ (car current-pos))) 144 facing))))) 145 146 (defun task1 (inputs) 147 (multiple-value-bind (map directions start-pos) 148 (parse-input inputs) 149 (walk-map map directions start-pos #'wrap-around))) 150 151 (defun task2 (inputs) 152 (multiple-value-bind (map directions start-pos) 153 (parse-input inputs) 154 (walk-map map directions start-pos #'wrap-cube))) 155 156 (define-day 22 157 () 158 #'task1 159 #'task2)