(defpackage #:advent.2024.08 (:use #:cl #:advent.2024.00) (:import-from #:alexandria #:maphash-values #:map-permutations)) (in-package #:advent.2024.08) ;;; Part 1. ;; How many locations in the map contain "antinodes" ? ;; Hmmmm let's do a hash-table ;; COORDINATES are stored in (row . col) (defparameter *input* "08.ex") (defun radio-map () "Return a hash map of frequency => (list station-coordinates)." (let ((res (make-hash-table))) (loop for row from 0 for line in (uiop:read-file-lines *input*) do (loop for col from 0 for char across line do (incf col) do (unless (eq char #\.) (push (cons row col) (gethash char res)))) finally (return res)))) (defun find-pair-antinodes (pair) "Given a COORD-PAIR, find its antinodes." (destructuring-bind ((row1 . col1) (row2 . col2)) pair (let ((rise (- row2 row1)) (run (- col2 col1))) ;; m a t h h h h h h h h h h h h h (list (cons (- row1 rise) (- col1 run)) (cons (+ row2 rise) (+ col2 run)))))) ;; (defun find-antinodes* (coordinates) ;; "Find all the antinodes of each pair of stations in COORDINATES." ;; (let (res) ;; (alexandria:map-permutations (lambda (pair) ;; (setf res ;; (append (find-pair-antinodes pair) ;; res))) ;; coordinates ;; :length 2) ;; res)) (defun unique-values (list) (let (unique) (loop for coord in list unless (member coord unique :test #'equal) do (push coord unique) finally (return unique)))) ;; (defun solve1* () ;; ;;; ACTUALLY this returns the list of antinodes... do (length (solve1)) to get ;; ;;; the answer. ;; (let* ((lines (uiop:read-file-lines *input*)) ;; (rows (length lines)) ;; (cols (length (car lines))) ;; res) ;; (alexandria:maphash-values (lambda (v) ;; (setf res (append ;; (remove-if-not ;; (lambda (p) ;; (and (< -1 (car p) rows) ;; (< -1 (cdr p) cols))) ;; (find-antinodes v)) ;; res))) ;; (radio-map)) ;; res)) ;;; Part 2 ;; Extend the antinode idea to the edge of the map (defun insidep (point rows cols) "Is POINT inside the map?" (and (< -1 (car point) rows) (< -1 (cdr point) cols))) (defun find-pair-resonants (pair rows cols) "Find the resonants along the line of P1-P2." (destructuring-bind ((row1 . col1) (row2 . col2)) pair (let ((rise (- row2 row1)) (run (- col2 col1))) ;; MATH! (append pair (loop for r = (- row1 rise) then (- r rise) for c = (- col1 run) then (- c run) while (insidep (cons r c) rows cols) collect (cons r c) ) (loop for r = (+ row2 rise) then (+ r rise) for c = (+ col2 run) then (+ c run) while (insidep (cons r c) rows cols) collect (cons r c) ))))) ;; (defun solve2* () ;; (let* ((lines (uiop:read-file-lines *input*)) ;; (rows (length lines)) ;; (cols (length (car lines))) ;; res) ;; (alexandria:maphash-values ;; (lambda (v) ;; (setf res (append (find-all #'find-pair-resonants v rows cols) res))) ;; (radio-map)) ;; (unique-values res))) ;;; 435 too low --- I forgot to add the 2 originals ;;; 585 too low ;;; 628 too low ---- 435 + 193 (number of original radios) ;;; Doing this again.... ;; I was trying to figure out part 2, and then part 1 isn't giving the right ;; solution either. So let's do it again. ;;; Part 1. (defvar *rows* nil) (defvar *cols* nil) (defun field () (let ((res (make-hash-table)) (lines (uiop:read-file-lines *input*))) (setf *rows* (length lines)) (setf *cols* (length (car lines))) (loop for line in lines for row from 0 do (loop for char across line for col from 0 do (unless (eq char #\.) (push (cons row col) (gethash char res)))) finally (return res)))) (defun find-antinodes (pair) (destructuring-bind ((row1 . col1) (row2 . col2)) pair (let ((drow (- row2 row1)) (dcol (- col2 col1))) (list (cons (- row1 drow) (- col1 dcol)) (cons (+ row2 drow) (+ col2 dcol)))))) (defun outsidep (point) (unless (and *rows* *cols*) (field)) (or (> 0 (car point)) (> 0 (cdr point)) (<= *rows* (car point)) (<= *cols* (cdr point)))) (defun find-group-antinodes (list &optional (fn #'find-antinodes)) "For every pair in LIST, find every pair of antinodes." (let (res) (map-permutations (lambda (pair) ;; (print pair) (loop for pt in (funcall fn pair) ;; do (print pt) do (pushnew pt res :test #'equal))) list :length 2) (remove-if #'outsidep res))) (defun solve1 () (let (res) (maphash-values (lambda (coordinate-group) (loop for g in (find-group-antinodes coordinate-group) do (pushnew g res :test #'equal))) (field)) (length res))) ;;; OKAY THIS IS CORRECT! DOPE. ;;; Part 2. (defun find-resonant-antinodes (pair) (destructuring-bind ((row1 . col1) (row2 . col2)) pair (let ((drow (- row2 row1)) (dcol (- col2 col1))) (append pair (loop for r = (- row1 drow) then (- r drow) for c = (- col1 dcol) then (- c dcol) until (outsidep (cons r c)) collect (cons r c)) (loop for r = (+ row2 drow) then (+ r drow) for c = (+ col2 dcol) then (+ c dcol) until (outsidep (cons r c)) collect (cons r c)))))) (defun solve2 () (let (res) (maphash-values (lambda (group) (loop for g in (find-group-antinodes group #'find-resonant-antinodes) do (pushnew g res :test #'equal))) (field)) (length (remove-if #'outsidep res)))) ;;; YAY!