152 lines
5.5 KiB
Common Lisp
152 lines
5.5 KiB
Common Lisp
;; https://adventofcode.com/2022/day/15
|
|
|
|
(ql:quickload 'cl-ppcre)
|
|
|
|
(defparameter *day15-input-file* "day15-test.txt")
|
|
|
|
(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))))
|
|
|
|
(defmethod points-equal ((left point) (right point))
|
|
(and (= (x left) (x right))
|
|
(= (y left) (y right))))
|
|
|
|
(defmethod manh-dist ((one point) (two point))
|
|
(+ (abs (- (x one) (x two)))
|
|
(abs (- (y one) (y two)))))
|
|
|
|
|
|
(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))))
|
|
|
|
|
|
(defmethod can-have-unknown-beacon-p ((p point) (s sensor))
|
|
(> (manh-dist p (self-coord s))
|
|
(covered-dist s)))
|
|
|
|
(defun line-to-coords (line)
|
|
(rest (mapcar (lambda (str)
|
|
(parse-integer str :junk-allowed t))
|
|
(cl-ppcre:split "=" line))))
|
|
|
|
(defun get-sensors-list (input-file-name)
|
|
(mapcar (lambda (coords-list)
|
|
(apply #'make-sensor coords-list))
|
|
(mapcar #'line-to-coords (uiop:read-file-lines input-file-name))))
|
|
|
|
(defun get-limits (sensors-list)
|
|
(loop
|
|
for sensor in 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))))
|
|
|
|
(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
|
|
)))
|
|
|
|
(defun count-certainly-not-beacons (input-file-name)
|
|
(let ((sensors (get-sensors-list input-file-name)))
|
|
(destructuring-bind (min-x max-x min-y max-y)
|
|
(get-limits sensors)
|
|
(let ((to-add-x (abs (- max-x min-x)))
|
|
;; (to-check-y 10)
|
|
(to-check-y 2000000)
|
|
)
|
|
(loop
|
|
for x from (- min-x to-add-x) to (+ max-x to-add-x)
|
|
count (not (possible-to-have-beacon
|
|
(make-instance 'point :x x :y to-check-y)
|
|
sensors))
|
|
do (format t "iterating for x:~a y:~a~%" x to-check-y))))))
|
|
|
|
(count-certainly-not-beacons "day15-test.txt")
|
|
;; (count-certainly-not-beacons "day15-input.txt")
|
|
|
|
;; well, that's just too slow
|
|
;; how do i rewrite it to make it faster?
|
|
;; i guess i could exclude the sensors which are too far away from the list?
|
|
;;
|
|
;; well, optimization here that we move by 1 point toward or away from sensor
|
|
;; so, we can kind of calculate when we'll be in it's range?
|
|
;; * in what amount of steps
|
|
;; ** whoa, org colors
|
|
;;
|
|
;; so.
|
|
|
|
;; PART 2 - just start search overnight?
|
|
;; for 0 to 4.000.000 by x and y?
|
|
;; and collect all such possible x and y?
|
|
;; nope, even if it were 5 minutes for 16mil,
|
|
;; so 2 minutes per 4 mil, then multiply by 4M - more than a day.
|
|
;; think better
|
|
|
|
(defun subtract-interval (minuend subtrahend)
|
|
(if (not subtrahend)
|
|
(list minuend) ; list of one interval
|
|
(destructuring-bind ((m-left m-right) (s-left s-right)) (list minuend subtrahend)
|
|
(cond
|
|
((< m-right s-left)
|
|
(list (list m-left m-right))) ; minuend fully to the left
|
|
((> m-left s-right)
|
|
(list (list m-left m-right))) ; minuend fully to the right
|
|
((and (< m-left s-left)
|
|
(> m-right s-right)) ; minuend is around subtrahend
|
|
(list (list m-left (1- s-left))
|
|
(list (1+ s-right) m-right))) ; part before and after subtrahend
|
|
((and (>= m-left s-left)
|
|
(<= m-right s-right)) ; subtrahend consumes minuend
|
|
nil)
|
|
((< m-left s-left) ; minuend start to the left, but not subtrahend consumes all right part
|
|
(list (list m-left (1- s-left)))) ; list of one interval
|
|
((> m-right s-right) ; minuend has part to the right of subtrahend
|
|
(list (list (1+ s-right) m-right)))))))
|
|
|
|
|
|
(defun get-no-unknown-beacons-x-interval (line-y scanner)
|
|
(let* ((y-dist (abs (- line-y (y (self-coord scanner)))))
|
|
(x-slack (- (covered-dist scanner) y-dist))
|
|
(x-sc (x (self-coord scanner))))
|
|
(when (>= x-slack 0)
|
|
(list (- x-sc x-slack) (+ x-sc x-slack)))))
|
|
|
|
(defun subtract-from-all (intervals subtrahend)
|
|
(mapcan (lambda (interval) (subtract-interval interval subtrahend))
|
|
intervals))
|