From a327aef2bf09df83ca2712693e935bc81e682a27 Mon Sep 17 00:00:00 2001 From: Peter Tillemans Date: Sun, 22 Dec 2024 07:08:37 +0100 Subject: [PATCH] starting day 22 --- aoc.asd | 2 + src/2024/day21.lisp | 219 +++++++++++++++++++++++++++++++++++-- src/2024/day22.lisp | 42 +++++++ src/maze.lisp | 17 +++ tests/2024/day16-test.lisp | 2 +- tests/2024/day18-test.lisp | 2 +- tests/2024/day21-test.lisp | 50 +++++++-- tests/2024/day22-test.lisp | 27 +++++ tests/2024/main.lisp | 4 +- 9 files changed, 344 insertions(+), 21 deletions(-) create mode 100644 src/2024/day22.lisp create mode 100644 tests/2024/day22-test.lisp diff --git a/aoc.asd b/aoc.asd index 8d17487..b234ac5 100644 --- a/aoc.asd +++ b/aoc.asd @@ -51,6 +51,7 @@ (:file "2024/day19") (:file "2024/day20") (:file "2024/day21") + (:file "2024/day22") ))) :description "Advent of Code challenges and solutions." :long-description "Solutions for the AOC challenges." @@ -88,6 +89,7 @@ (:file "2024/day19-test") (:file "2024/day20-test") (:file "2024/day21-test") + (:file "2024/day22-test") ))) :description "Test system for aoc" :perform (test-op (op c) (symbol-call :parachute :test :aoc/tests))) diff --git a/src/2024/day21.lisp b/src/2024/day21.lisp index 41e6488..fcff4bf 100644 --- a/src/2024/day21.lisp +++ b/src/2024/day21.lisp @@ -1,35 +1,240 @@ (defpackage :aoc/2024/21 - (:use :cl :aoc :alexandria :trivia :lla) + (:use :cl :aoc + :aoc/maze :alexandria :lla :arrow-macros) (:export #:sample-data #:sample-data2 #:part1 #:part2 + #:key-to-pos + #:press-key + #:type-code + #:type-manual + #:complexity + #:combinations + #:press-key-combinations + #:path-invalid-p + #:easiest-to-type + #:easy-key-combinations + #:easy-type-code )) (in-package :aoc/2024/21) - (defun parse-line (line) line) - (defun parse-input (lines) (mapcar #'parse-line lines)) (defparameter input-text (test-input 2024 21)) (defparameter input-data (parse-input input-text)) -(defparameter sample-text (aoc:split-lines "")) +(defparameter sample-text (aoc:split-lines "029A +980A +179A +456A +379A")) (defparameter sample-data - (parse-input sample-text)) + (parse-input sample-text)) + +;; numerical pad: +;; +---+---+---+ +;; | 7 | 8 | 9 | +;; +---+---+---+ +;; | 4 | 5 | 6 | +;; +---+---+---+ +;; | 1 | 2 | 3 | +;; +---+---+---+ +;; | 0 | A | +;; +---+---+ +;; directional pad: +;; +---+---+ +;; | ^ | A | +;; +---+---+---+ +;; | < | v | > | +;; +---+---+---+ +;; +;; complexity = #moves/actions * numeric part +;; +;; observations: +;; - directional pad looks like an upside down truncated num pad +;; - no questions about the actual keystrokes, only amount +;; - keystrokes = manhattan distance (moves) + action press +;; +;; approach: +;; - ^ = 0; < = 1; v = 2; > = 3 --> dirpad to numpad +;; - 0-9A --> pos +;; - pos differences +;; - manhattan + 1 +;; - sum it. + + +(defun key-to-pos (c) + (cond + ((eq c #\0) (make-pos :x 1 :y 0)) + ((eq c #\A) (make-pos :x 2 :y 0)) + ((eq c #\^) (make-pos :x 1 :y 0)) + ((eq c #\<) (make-pos :x 0 :y 1)) + ((eq c #\v) (make-pos :x 1 :y 1)) + ((eq c #\>) (make-pos :x 2 :y 1)) + ((digit-char-p c) (make-pos + :x (mod (1- (digit-char-p c)) 3) + :y (floor (/ (+ 2 (digit-char-p c)) 3)))) + (t nil))) + +(defun press-key (start target &optional (numeric t)) + (let ((diff (pos-subtract + (key-to-pos target) + (key-to-pos start)))) + (coerce + (append + (loop for x from 0 below (pos-x diff) collect #\>) + (loop for y from 0 below (pos-y diff) collect (if numeric #\^ #\v)) + (loop for y from 0 below (- (pos-y diff)) collect (if numeric #\v #\^)) + (loop for x from 0 below (- (pos-x diff)) collect #\<) + '(#\A)) + 'string))) + +(defun type-code (code &optional (numeric t)) + (let ((keys (coerce code 'list))) + (format + nil "~{~A~}" + (loop + for start in (cons #\A keys) + for target in keys + collect (press-key start target numeric))))) + + + +(defun combinations (as bs &optional (acc '())) + (cond + ((emptyp as) (list (append bs acc))) + ((emptyp bs) (list (append as acc))) + (t (append + (combinations (cdr as) bs (cons (car as) acc)) + (combinations as (cdr bs) (cons (car bs) acc)))))) + +(defun pos-step (pos c &optional (numeric t)) + (case c + ((#\< #\>) (pos-move pos c)) + (#\^ (pos-move pos (if numeric #\v #\^))) + (#\v (pos-move pos (if numeric #\^ #\v))))) + +(defun path-invalid-p (start moves &optional (numeric t)) + (loop + for dir in moves + for pos = (pos-step (or pos start) dir numeric) + thereis (and (zerop (pos-x pos)) (zerop (pos-y pos))))) + +(defun press-key-combinations (start target &optional (numeric t)) + (let* ((start-pos (key-to-pos start)) + (diff (pos-subtract + (key-to-pos target) + start-pos)) + (hp (loop + for x from 0 below (abs (pos-x diff)) + collect (if (plusp (pos-x diff)) #\> #\<))) + (vp (loop + for y from 0 below (abs (pos-y diff)) + collect (if (equal numeric (plusp (pos-y diff))) #\^ #\v))) + ) + (mapcar + #'(lambda (c) (coerce (append c '(#\A)) 'string)) + (remove-if + #'(lambda (moves) (path-invalid-p start-pos moves)) + (combinations hp vp))) + )) + +(defun easiest-to-type (codes &optional (numeric t)) + (format t "~A ~A~%" codes numeric) + (let ((min-taps (loop + for code in codes + minimize (length (type-code (type-code code t) nil))))) + (loop + for code in codes + if (= min-taps (length (type-code (type-code code numeric)))) + collect code))) + + +(defun easy-key-combinations (start target &optional (numeric t)) + (easiest-to-type + (press-key-combinations start target numeric) + t)) + +(defun type-numpad (code) + (let ((keys (coerce code 'list))) + (format nil "~{~A~}" + (loop + for start in (cons #\A keys) + for target in keys + collect (first (easy-key-combinations start target t)))))) + +(defun type-dirpads (code n) + (loop + for i from 1 to n + do (setf code (type-code code nil)) + finally (return code))) + + +(defun type-manual (code n) + (type-dirpads (type-numpad code) n)) + +(defun complexity (code n) + (let ((l (length (type-manual code n))) + (v (parse-integer (ppcre:scan-to-strings "\\d+" code)))) + (* l v))) + +(defun pos-to-key (pos &optional (numeric t)) + (cond + ((zerop (pos-y pos)) (if numeric + (case (pos-x pos) + (2 #\A) + (1 #\0) + (0 #\?)) + (case (pos-x pos) + (2 #\A) + (1 #\^) + (0 #\?)))) + ((plusp (pos-y pos)) (if numeric + (digit-char + (+ + (* 3 (1- (pos-y pos))) + (1+ (pos-x pos)))) + (case (pos-x pos) + (2 #\>) + (1 #\v) + (0 #\<)))) + (t #\? )) + ) + +(defun decode (code &optional (numeric t)) + (let ((parts (ppcre:split "A" code)) + (pos (key-to-pos #\A))) + (coerce + (loop + for sc in parts + do (loop for c across sc do (setf pos (pos-step pos c numeric))) + collect (pos-to-key pos numeric)) + 'string))) + +(defun decode3 (code) + (decode + (decode + (decode code nil) + nil) + t)) (defun part1 (data) - (length data)) + (loop + for code in data + sum (complexity code 2))) (defun part2 (data) - (length data)) + (loop + for code in data + sum (complexity code 25))) (defun solve-day () (format t "part1: ~A~%" (part1 input-data)) diff --git a/src/2024/day22.lisp b/src/2024/day22.lisp new file mode 100644 index 0000000..f51dd94 --- /dev/null +++ b/src/2024/day22.lisp @@ -0,0 +1,42 @@ + +(defpackage :aoc/2024/22 + (:use :cl :aoc :alexandria :trivia :lla) + (:export + #:sample-data + #:sample-data2 + #:part1 + #:part2 + )) + +(in-package :aoc/2024/22) + + +(defun parse-line (line) + line) + + +(defun parse-input (lines) + (mapcar #'parse-line lines)) + +(defparameter input-text (test-input 2024 22)) +(defparameter input-data (parse-input input-text)) + +(defparameter sample-text (aoc:split-lines "")) +(defparameter sample-data + (parse-input sample-text)) + +(defun part1 (data) + (length data)) + +(defun part2 (data) + (length data)) + +(defun solve-day () + (format t "part1: ~A~%" (part1 input-data)) + (format t "part2: ~A~%" (part2 input-data))) + +(defun submit () + (let ((p1 (part1 input-data)) + (p2 (part2 input-data))) + (if p1 (submit-part1 2024 22 p1)) + (if p2 (submit-part2 2024 22 p2)))) diff --git a/src/maze.lisp b/src/maze.lisp index 1e62aef..8ced947 100644 --- a/src/maze.lisp +++ b/src/maze.lisp @@ -8,10 +8,14 @@ #:pos-x #:pos-y #:pos-move + #:pos-add + #:pos-subtract #:show-map #:get-pos #:set-pos #:find-tile + #:rotate-left + #:rotate-right #:manhattan-distance )) @@ -38,6 +42,19 @@ (#\> (make-pos :x (1+ (pos-x pos)) :y (pos-y pos))) )) +(defun pos-add (a b) + (make-pos + :x (+ (pos-x a) (pos-x b)) + :y (+ (pos-y a) (pos-y b)) + )) + +(defun pos-subtract (a b) + (make-pos + :x (- (pos-x a) (pos-x b)) + :y (- (pos-y a) (pos-y b)) + )) + + (defun show-map (map &optional (dest t)) (let ((w (array-dimension map 1)) (h (array-dimension map 0))) diff --git a/tests/2024/day16-test.lisp b/tests/2024/day16-test.lisp index d54ddb9..f80fe3b 100644 --- a/tests/2024/day16-test.lisp +++ b/tests/2024/day16-test.lisp @@ -34,7 +34,7 @@ (define-test test-part2-maze2 :parent suite-2024-16 - (is = 64 (solVe-part2 test-maze2))) + (is = 64 (solve-part2 test-maze2))) diff --git a/tests/2024/day18-test.lisp b/tests/2024/day18-test.lisp index e60c8c8..18cdc31 100644 --- a/tests/2024/day18-test.lisp +++ b/tests/2024/day18-test.lisp @@ -1,5 +1,5 @@ (defpackage :aoc/2024/18/tests - (:use :cl :aoc :aoc/tests :aoc/2024/tests :parachute :aoc/2024/18)) + (:use :cl :aoc :aoc/maze :aoc/tests :aoc/2024/tests :parachute :aoc/2024/18)) (in-package :aoc/2024/18/tests) diff --git a/tests/2024/day21-test.lisp b/tests/2024/day21-test.lisp index 06bab9f..86a7ddf 100644 --- a/tests/2024/day21-test.lisp +++ b/tests/2024/day21-test.lisp @@ -1,5 +1,5 @@ (defpackage :aoc/2024/21/tests - (:use :cl :aoc :aoc/tests :aoc/2024/tests :parachute :aoc/2024/21)) + (:use :cl :aoc :aoc/tests :aoc/maze :aoc/2024/tests :parachute :aoc/2024/21)) (in-package :aoc/2024/21/tests) @@ -7,20 +7,52 @@ ;:parent suite-2024 ) -(define-test test-foo - :parent suite-2024-21 - ) +(define-test test-key-to-pos + :parent suite-2024-21 + (is equalp (make-pos :x 1 :y 0) (key-to-pos #\0)) + (is equalp (make-pos :x 2 :y 0) (key-to-pos #\A)) + (is equalp (make-pos :x 0 :y 1) (key-to-pos #\1)) + (is equalp (make-pos :x 1 :y 2) (key-to-pos #\5)) + (is equalp (make-pos :x 2 :y 3) (key-to-pos #\9)) + (is equalp (make-pos :x 1 :y 0) (key-to-pos #\^)) + (is equalp (make-pos :x 0 :y 1) (key-to-pos #\<)) + (is equalp (make-pos :x 1 :y 1) (key-to-pos #\v)) + (is equalp (make-pos :x 2 :y 1) (key-to-pos #\>)) + ) -(define-test test-bar - :parent suite-2024-21 - ) +(define-test test-press-key + :parent suite-2024-21 + (is equal "^^A" (press-key #\2 #\9)) + (is equal "vvvA" (press-key #\9 #\A)) + ) + +(define-test test-type-code + :parent suite-2024-21 + (is equal "^^AvvvA" (type-code "029A")) + (is equal "v<>^AAvA^Av^A" (type-code (type-code "029A") nil))) + +(define-test test-complexity + :parent suite-2024-21 + (is = (* 68 29) (complexity "029A" 2)) + (is = (* 60 980) (complexity "980A" 2)) + (is = (* 68 179) (complexity "179A" 2)) + (is = (* 64 456) (complexity "456A" 2)) + (is = (* 64 379) (complexity "379A" 2))) + +(define-test test-manual + :parent suite-2024-21 + (is equal + ">^AvA^A>^AAvA<^A>AAvA^A^AAAA>^AAAvA<^A>A" + (type-manual "379A" 2))) (define-test+run test-part1 - :parent suite-2024-21 - (is equal nil (part1 sample-data))) + :parent suite-2024-21 + (is equal 126384 (part1 sample-data))) (define-test+run test-part2 :parent suite-2024-21 diff --git a/tests/2024/day22-test.lisp b/tests/2024/day22-test.lisp new file mode 100644 index 0000000..fa9b6e9 --- /dev/null +++ b/tests/2024/day22-test.lisp @@ -0,0 +1,27 @@ +(defpackage :aoc/2024/22/tests + (:use :cl :aoc :aoc/tests :aoc/2024/tests :parachute :aoc/2024/22)) + +(in-package :aoc/2024/22/tests) + +(define-test suite-2024-22 + ;:parent suite-2024 + ) + +(define-test test-foo + :parent suite-2024-22 + ) + + +(define-test test-bar + :parent suite-2024-22 + ) + + + +(define-test+run test-part1 + :parent suite-2024-22 + (is equal nil (part1 sample-data))) + +(define-test+run test-part2 + :parent suite-2024-22 + (is equal nil (part2 sample-data))) diff --git a/tests/2024/main.lisp b/tests/2024/main.lisp index def052a..2c804e7 100644 --- a/tests/2024/main.lisp +++ b/tests/2024/main.lisp @@ -9,9 +9,7 @@ :parent 'aoc-suite) -(define-test test-day08 - :parent 'suite-2024 - (aoc/2024/08/tests:run-tests)) +