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))
+