;; https://adventofcode.com/2022/day/15 ;; ;; oh, wow. i can already imagine the second part of the task ;; so. for arrangements of (sensor closest-beacon) ;; i need to figure out which points are out of rangle for all sensors? ;; where "range" is distance between sensor and the closest-beacon ;; ;; so for each sensor also store distance, and each sensor should be able to answer query for point ;; whether it disproves existence of a beacone there ;; then for ( POINTS x SENSORS ) computations i'll be able to mark all points that aren't covered. ;; ;; doesn't seem like too much (ql:quickload 'cl-ppcre) ;; poor man's parsing (rest (mapcar (lambda (str) (parse-integer str :junk-allowed t)) (cl-ppcre:split "=" "Sensor at x=2, y=18: closest beacon is at x=-2, y=15"))) ;; manhattan distance : https://en.wikipedia.org/wiki/Taxicab_geometry ;; sum of abs of coord-diffs (defclass point () ((x :initarg :x :reader x) (y :initarg :y :reader y))) (defmethod print-object ((obj point) stream) (print-unreadable-object (obj stream :type t) (with-slots (x y) obj (format stream "x:~a y:~a" x y)))) (defparameter *test-point-1* (make-instance 'point :x 1 :y 19)) (defparameter *test-point-2* (make-instance 'point :x -2 :y -20)) (defmethod manh-dist ((one point) (two point)) (+ (abs (- (x one) (x two))) (abs (- (y one) (y two))))) (manh-dist *test-point-1* *test-point-2*) ;; i guess this is right (defclass sensor () ((self-coord :initarg :self :reader self-coord) (beacon-coord :initarg :beacon :reader beacon-coord) (covered-dist :initarg :dist :reader covered-dist))) (defun make-sensor (sens-x sens-y beac-x beac-y) (let* ((sensor (make-instance 'point :x sens-x :y sens-y)) (beacon (make-instance 'point :x beac-x :y beac-y)) (dist (manh-dist sensor beacon))) (make-instance 'sensor :self sensor :beacon beacon :dist dist))) (defmethod print-object ((obj sensor) stream) (print-unreadable-object (obj stream :type t) (with-slots (self-coord beacon-coord covered-dist) obj (format stream "at: ~a, linked to: ~a, covering dist: ~a" self-coord beacon-coord covered-dist)))) (defparameter *test-sensor* (make-sensor 2 18 -2 15)) (defmethod can-have-unknown-beacon-p ((p point) (s sensor)) (> (manh-dist p (self-coord s)) (covered-dist s))) (manh-dist *test-point-1* (self-coord *test-sensor*)) (can-have-unknown-beacon-p *test-point-1* *test-sensor*) (manh-dist *test-point-2* (self-coord *test-sensor*)) (can-have-unknown-beacon-p *test-point-2* *test-sensor*) ;; ok. now read in all sensors? ;; and then for line with specified 'y' ;; and from leftmost to rightmost S or B for each point ask each sensor if possible ;; to have an unknown beacon, if any says "no" - then no ;; otherwise - count (defparameter *day15-input-file* "day15-test.txt") (defun line-to-coords (line) (rest (mapcar (lambda (str) (parse-integer str :junk-allowed t)) (cl-ppcre:split "=" line)))) (defparameter *day15-sensors-list* nil) (setq *day15-sensors-list* (mapcar (lambda (coords-list) (apply #'make-sensor coords-list)) (mapcar #'line-to-coords (uiop:read-file-lines *day15-input-file*)))) ;; next - find lovest x and highest x ;; but then i guess i'd also want lovest and highest y overall ;; that's neat (loop for sensor in *day15-sensors-list* minimize (x (self-coord sensor)) into xs minimize (x (beacon-coord sensor)) into xs maximize (x (self-coord sensor)) into xm maximize (x (beacon-coord sensor)) into xm minimize (y (self-coord sensor)) into ys minimize (y (beacon-coord sensor)) into ys maximize (y (self-coord sensor)) into ym maximize (y (beacon-coord sensor)) into ym finally (return (list xs xm ys ym))) ;; (-2 25 0 22) ;; now for line y=10 check all x and count how many -for-all- sensors allow new point (defun all-sensors-allow-for-hidden-beacon (point sensors) (macroexpand `(and ,@(mapcar (lambda (sensor) (can-have-unknown-beacon-p point sensor)) sensors)))) (defun all-sensors-allow-for-hidden-beacon (point sensors) (not (position nil (mapcar (lambda (sensor) (can-have-unknown-beacon-p point sensor)) sensors)))) ;; well, do i have to write my own function for AND ? (when (all-sensors-allow-for-hidden-beacon *test-point-2* *day15-sensors-list*) 1) (when (all-sensors-allow-for-hidden-beacon *test-point-1* *day15-sensors-list*) 1) ;; count how many ARE covered (loop for x from -2 to 25 count (not (all-sensors-allow-for-hidden-beacon (make-instance 'point :x x :y 10) *day15-sensors-list*))) ;; on the image it's from -2 and till 24, so should be 27, if counting 0 ;; well. we're counting posistions "wher beacon can't possibly exist" ;; so removing points which _are_ beacons? ;; ;; and - range needs to be extended significantly, no? ;; what would be enough? ;; doubling into each direction? (defmethod points-equal ((left point) (right point)) (and (= (x left) (x right)) (= (y left) (y right)))) (points-equal (make-instance 'point :x 1 :y 1) (make-instance 'point :x 1 :y 1)) (defun possible-to-have-beacon (point sensors) (let ((all-checks (mapcar (lambda (sensor) (if (points-equal point (beacon-coord sensor)) 'known-sensor (can-have-unknown-beacon-p point sensor) ; single NIL means - not possible to have unknown )) sensors))) (or (not (position nil all-checks)) ; nil if all sensors allow (said T) presense of unknown beacons (position 'known-sensor all-checks) ; exists known sensor ))) ;; beacon is possible : either sensor has beacon at that point ;; or position is out of the sensor range ;; but here's the thing. if sencor-beacon is at this point - need to short-circuit T (possible-to-have-beacon *test-point-2* *day15-sensors-list*) (possible-to-have-beacon *test-point-1* *day15-sensors-list*) (possible-to-have-beacon (make-instance 'point :x -2 :y 15) *day15-sensors-list*) ;; i guess that works ;; count how many ARE covered (loop for x from -2 to 25 count (not (possible-to-have-beacon (make-instance 'point :x x :y 10) *day15-sensors-list*))) ;; ok.