(ql:quickload '(fiveam uiop arrows)) (defun coord-x-minor (width height) (lambda (x y) (when (and (< -1 x width) (< -1 y height)) (+ x (* y width))))) (defun forest (data) (let ((width (length (car data))) (height (length data))) (values width height (map 'vector (lambda (x) (- (char-code x) 48)) (apply #'concatenate 'string data))))) (defun line-of-sight (direction x y width height) (let ((location (coord-x-minor width height))) (case direction (right (loop for l from (1+ x) below width collect (funcall location l y))) (bottom (loop for l from (1+ y) below height collect (funcall location x l))) (left (loop for l downfrom (1- x) to 0 collect (funcall location l y))) (top (loop for l downfrom (1- y) to 0 collect (funcall location x l)))))) (defun solver-p1 (filename) (multiple-value-bind (width height forest-arr) (forest (uiop:read-file-lines filename)) (let ((visibility-mask (make-array (* width height) :initial-element nil)) (location-x (coord-x-minor width height))) (flet ((toggle-visibility (major minor location reverse) (let ((range (if reverse (loop for i downfrom (1- minor) to 0 collect i) (loop for i below minor collect i)))) (dotimes (m major) (loop for n in range for maxh = -1 then (max maxh tree-height) for tree-height = (aref forest-arr (funcall location n m)) when (> tree-height maxh) do (setf (aref visibility-mask (funcall location n m)) t))))) (location-y (y x) (funcall location-x x y))) (toggle-visibility height width location-x nil) (toggle-visibility height width location-x t) (toggle-visibility width height #'location-y nil) (toggle-visibility width height #'location-y t)) (loop for v across visibility-mask counting v)))) (defun solver-p2 (filename) (multiple-value-bind (width height forest-arr) (forest (uiop:read-file-lines filename)) (let ((score (make-array (* width height))) (location-x (coord-x-minor width height))) (flet ((score-direction (base dir) (loop for l in dir for tree-height = (aref forest-arr l) count l until (>= tree-height base)))) (dotimes (y height) (dotimes (x width) (let ((base (aref forest-arr (funcall location-x x y)))) (arrows:->> '(right left top bottom) (mapcar (lambda (direction) (score-direction base (line-of-sight direction x y width height)))) (apply #'* ) (setf (aref score (funcall location-x x y))))))) (loop for v across score maximize v))))) (fiveam:test solutions (fiveam:is (= 21 (solver-p1 "eg-in"))) (fiveam:is (= 1672 (solver-p1 "input"))) (fiveam:is (= 8 (solver-p2 "eg-in"))) (fiveam:is (= 327180 (solver-p2 "input")))) (fiveam:run-all-tests)