~schnouki/advent-of-code

ef7244d07ce7785d9ac7381c008a71bbe02a30a5 — Thomas Jost 1 year, 9 months ago e9a05c7
2022 day 12
5 files changed, 186 insertions(+), 3 deletions(-)

M 2022/aoc-2022.asd
A 2022/day12.lisp
A 2022/inputs/12
M utils/grid.lisp
M utils/packages.lisp
M 2022/aoc-2022.asd => 2022/aoc-2022.asd +2 -1
@@ 10,4 10,5 @@
               (:file "day08")
               (:file "day09")
               (:file "day10")
               (:file "day11")))
               (:file "day11")
               (:file "day12")))

A 2022/day12.lisp => 2022/day12.lisp +85 -0
@@ 0,0 1,85 @@
(defpackage :aoc-2022-day12
  (:use :cl))
(in-package :aoc-2022-day12)

(defparameter *test-input* "Sabqponm
abcryxxl
accszExk
acctuvwj
abdefghi")

(defun read-input (input)
  (let* ((g (aoc:dense-grid-from-string input))
         (start (aoc:grid-find #\S g :test #'char=))
         (end (aoc:grid-find #\E g :test #'char=)))
    (setf (aoc:grid-xy g (car start) (cdr start)) #\a)
    (setf (aoc:grid-xy g (car end) (cdr end)) #\z)
    (list :grid g
          :start start
          :end end)))

(defun part1 (data)
  (let* ((g (getf data :grid))
         (start (getf data :start))
         (end (getf data :end))
         (bb (aoc:grid-bounding-box g))
         (width (1+ (nth 2 bb)))
         (height (1+ (nth 3 bb)))
         (moves (aoc:make-dense-grid width height)))
    (setf (aoc:grid-xy moves (car end) (cdr end)) 0)
    (loop with next = (list end)
          while next
          while (null (aoc:grid-xy moves (car start) (cdr start)))
          for m from 1
          do (loop with changed = nil
                   for (px . py) in next
                   for ph = (- (char-code (aoc:grid-xy g px py))
                               (char-code #\a))
                   do (loop for (nx . ny) in (aoc:grid-neighbors g px py)
                            for nh = (- (char-code (aoc:grid-xy g nx ny))
                                        (char-code #\a))
                            when (and (null (aoc:grid-xy moves nx ny))
                                      (>= nh (1- ph)))
                              do (setf (aoc:grid-xy moves nx ny) m)
                                 (pushnew (cons nx ny) changed :test #'equal))
                   finally (setf next changed)))
    (aoc:grid-xy moves (car start) (cdr start))))

(defun part2 (data)
  (let* ((g (getf data :grid))
         (end (getf data :end))
         (bb (aoc:grid-bounding-box g))
         (width (1+ (nth 2 bb)))
         (height (1+ (nth 3 bb)))
         (moves (aoc:make-dense-grid width height)))
    (setf (aoc:grid-xy moves (car end) (cdr end)) 0)
    (block outer
      (loop with next = (list end)
            while next
            for m from 1
            do (loop with changed = nil
                     for (px . py) in next
                     for ph = (- (char-code (aoc:grid-xy g px py))
                                 (char-code #\a))
                     do (loop for (nx . ny) in (aoc:grid-neighbors g px py)
                              for nv = (aoc:grid-xy g nx ny)
                              for nh = (- (char-code nv)
                                          (char-code #\a))
                              when (and (null (aoc:grid-xy moves nx ny))
                                        (>= nh (1- ph)))
                                do (setf (aoc:grid-xy moves nx ny) m)
                                   (pushnew (cons nx ny) changed :test #'equal)
                                   (when (char= nv #\a) (return-from outer m)))
                     finally (setf next changed))))))


(aoc:defpuzzle day12
  :test-data (list *test-input*)
  :test-results-part1 '(31)
  :test-results-part2 '(29)
  :read-input #'read-input
  :part1 #'part1
  :part2 #'part2)

;; (day12 :test)
;; (day12)

A 2022/inputs/12 => 2022/inputs/12 +41 -0
@@ 0,0 1,41 @@
abccccccccccccccccccaaaaaaaaacccccccccccccccccccccccccccccccccccccaaaa
abcccccccccccccccaaaaaaaaaaacccccccccccccccccccccccccccccccccccccaaaaa
abcaaccaacccccccccaaaaaaaaaacccccccccccccccccccccaaacccccccccccccaaaaa
abcaaaaaaccccccccaaaaaaaaaaaaacccccccccccccccccccaacccccccccccccaaaaaa
abcaaaaaacccaaacccccaaaaaaaaaaaccccccccccccccccccaaaccccccccccccccccaa
abaaaaaaacccaaaaccccaaaaaacaaaacccccccccccaaaacjjjacccccccccccccccccca
abaaaaaaaaccaaaaccccaaaaaaccccccaccccccccccaajjjjjkkcccccccccccccccccc
abaaaaaaaaccaaacccccccaaaccccccaaccccccccccajjjjjjkkkaaacccaaaccaccccc
abccaaacccccccccccccccaaccccaaaaaaaacccccccjjjjoookkkkaacccaaaaaaccccc
abcccaacccccccccccccccccccccaaaaaaaaccccccjjjjoooookkkkcccccaaaaaccccc
abcccccccaacccccccccccccccccccaaaacccccccijjjoooooookkkkccaaaaaaaccccc
abccaaccaaaccccccccccccccccccaaaaacccccciijjooouuuoppkkkkkaaaaaaaacccc
abccaaaaaaaccccccccccaaaaacccaacaaaccciiiiiooouuuuupppkkklllaaaaaacccc
abccaaaaaacccccccccccaaaaacccacccaaciiiiiiqooouuuuuupppkllllllacaccccc
abcccaaaaaaaacccccccaaaaaaccccaacaiiiiiqqqqoouuuxuuupppppplllllccccccc
abccaaaaaaaaaccaaaccaaaaaaccccaaaaiiiiqqqqqqttuxxxuuuppppppplllccccccc
abccaaaaaaaacccaaaaaaaaaaacccaaaahiiiqqqttttttuxxxxuuuvvpppplllccccccc
abcaaaaaaacccaaaaaaaaaaacccccaaaahhhqqqqtttttttxxxxuuvvvvvqqlllccccccc
abcccccaaaccaaaaaaaaaccccccccacaahhhqqqttttxxxxxxxyyyyyvvvqqlllccccccc
abcccccaaaccaaaaaaaacccccccccccaahhhqqqtttxxxxxxxyyyyyyvvqqqlllccccccc
SbcccccccccccaaaaaaaaaccccccccccchhhqqqtttxxxxEzzzyyyyvvvqqqmmlccccccc
abcccccccccccaaaaaaaacccaacccccccchhhppptttxxxxyyyyyvvvvqqqmmmcccccccc
abccccccccccaaaaaaaaaaccaacccccccchhhpppptttsxxyyyyyvvvqqqmmmccccccccc
abcaacccccccaaaaaaacaaaaaaccccccccchhhppppsswwyyyyyyyvvqqmmmmccccccccc
abaaaacccccccaccaaaccaaaaaaacccccccchhhpppsswwyywwyyyvvqqmmmddcccccccc
abaaaaccccccccccaaaccaaaaaaacccccccchhhpppsswwwwwwwwwvvqqqmmdddccccccc
abaaaacccccccccaaaccaaaaaaccccccccccgggpppsswwwwrrwwwwvrqqmmdddccccccc
abccccccaaaaaccaaaacaaaaaaccccccaacccggpppssswwsrrrwwwvrrqmmdddacccccc
abccccccaaaaaccaaaacccccaaccccaaaaaacggpppssssssrrrrrrrrrnmmdddaaccccc
abcccccaaaaaaccaaaccccccccccccaaaaaacggppossssssoorrrrrrrnnmdddacccccc
abcccccaaaaaaccccccccaaaaccccccaaaaacgggoooossoooonnnrrnnnnmddaaaacccc
abccccccaaaaaccccccccaaaacccccaaaaaccgggoooooooooonnnnnnnnndddaaaacccc
abccccccaaaccccccccccaaaacccccaaaaacccgggoooooooffennnnnnnedddaaaacccc
abcccccccccccccccccccaaacccccccaacccccggggffffffffeeeeeeeeeedaaacccccc
abccccccccccccccccccaaacccccaccaaccccccggfffffffffeeeeeeeeeecaaacccccc
abccccccccccccccccccaaaacccaaaaaaaaaccccfffffffaaaaaeeeeeecccccccccccc
abccccccccaacaaccccaaaaaacaaaaaaaaaaccccccccccaaaccaaaaccccccccccccccc
abccccccccaaaaacccaaaaaaaaaaacaaaaccccccccccccaaaccccaaccccccccccaaaca
abcccccccaaaaaccccaaaaaaaaaaacaaaaacccccccccccaaaccccccccccccccccaaaaa
abcccccccaaaaaacccaaaaaaaaaacaaaaaacccccccccccaaccccccccccccccccccaaaa
abcccccccccaaaaccaaaaaaaaaaaaaaccaaccccccccccccccccccccccccccccccaaaaa

M utils/grid.lisp => utils/grid.lisp +54 -2
@@ 61,7 61,8 @@
           (width (- end start -1)))
      (make-array width
                  :displaced-to data
                  :displaced-index-offset (array-row-major-index data y start)))))
                  :displaced-index-offset (array-row-major-index data y start)
                  :element-type (array-element-type data)))))

(defgeneric grid-column (grid x &key up-from down-from)
  (:documentation "Return a column from the grid.")


@@ 101,7 102,7 @@
      (list 0 0 (1- width) (1- height)))))

(defgeneric grid-size (grid)
  (:documentation "Reutnr the number of set points in the grid.")
  (:documentation "Return the number of set points in the grid.")
  (:method ((g sparse-grid))
    (hash-table-count (sparse-grid-data g))))



@@ 118,3 119,54 @@
                            (otherwise grid-value))
              do (format out "~a~a" value h-space)
              finally (format out "~%"))))))

(defun dense-grid-from-string (s)
  "Build a dense grid from a string."
  (let ((lines (str:lines s)))
    (loop with g = (make-dense-grid (length (car lines)) (length lines))
          for line in lines
          for y from 0
          do (loop for char across line
                   for x from 0
                   do (setf (grid-xy g x y) char))
          finally (return g))))

(defgeneric grid-find-if (predicate grid)
  (:documentation "Return the coords of the first value in GRID that satisfies PREDICATE.")
  (:method (predicate (g sparse-grid))
    (loop for coords being the hash-keys in (sparse-grid-data g) using (hash-value value)
          when (funcall predicate value) do
            (return (cons (realpart value)
                          (imagpart value)))
          finally (return nil)))
  (:method (predicate (g dense-grid))
    (alexandria:when-let* ((data (dense-grid-data g))
                           (idx (position-if predicate
                                             (make-array (array-total-size data)
                                                         :displaced-to data
                                                         :element-type (array-element-type data)))))
      (multiple-value-bind (y x) (floor idx (array-dimension data 1))
        (cons x y)))))

(defun grid-find (item grid &key (test #'eql))
  "Return the coords of the first instance of ITEM in GRID."
  (grid-find-if (lambda (it) (funcall test it item)) grid))

(defgeneric grid-neighbors (grid x y)
  (:documentation "Return the coords of all neighbors.")
  (:method ((g sparse-grid) x y)
    (list (cons (1+ x) y)
          (cons (1- x) y)
          (cons x (1+ y))
          (cons x (1- y))))
  (:method ((g dense-grid) x y)
    (loop with dim = (array-dimensions (dense-grid-data g))
          for (xx . yy) in (list (cons (1+ x) y)
                                 (cons (1- x) y)
                                 (cons x (1+ y))
                                 (cons x (1- y)))
          when (and (>= xx 0)
                    (>= yy 0)
                    (< xx (cadr dim))
                    (< yy (car dim)))
            collect (cons xx yy))))

M utils/packages.lisp => utils/packages.lisp +4 -0
@@ 24,6 24,10 @@
   #:grid-bounding-box
   #:grid-size
   #:grid-print
   #:dense-grid-from-string
   #:grid-find-if
   #:grid-find
   #:grid-neighbors

   ;; sequence
   #:chunks