(in-package :ca.mhcat.advent2022) ;; --- Day 5: Supply Stacks --- ;; The expedition can depart as soon as the final supplies ;; have been unloaded from the ships. Supplies are stored in ;; stacks of marked crates, but because the needed supplies ;; are buried under many other crates, the crates need to be ;; rearranged. ;; The ship has a giant cargo crane capable of moving crates ;; between stacks. To ensure none of the crates get crushed ;; or fall over, the crane operator will rearrange them in a ;; series of carefully-planned steps. After the crates are ;; rearranged, the desired crates will be at the top of each ;; stack. ;; The Elves don't want to interrupt the crane operator ;; during this delicate procedure, but they forgot to ask ;; her which crate will end up where, and they want to be ;; ready to unload them as soon as possible so they can ;; embark. ;; They do, however, have a drawing of the starting stacks ;; of crates and the rearrangement procedure (your puzzle ;; input). For example: ;; [D] ;; [N] [C] ;; [Z] [M] [P] ;; 1 2 3 ;; move 1 from 2 to 1 ;; move 3 from 1 to 3 ;; move 2 from 2 to 1 ;; move 1 from 1 to 2 ;; In this example, there are three stacks of crates. Stack ;; 1 contains two crates: crate Z is on the bottom, and ;; crate N is on top. Stack 2 contains three crates; from ;; bottom to top, they are crates M, C, and D. Finally, ;; stack 3 contains a single crate, P. ;; Then, the rearrangement procedure is given. In each step ;; of the procedure, a quantity of crates is moved from one ;; stack to a different stack. In the first step of the ;; above rearrangement procedure, one crate is moved from ;; stack 2 to stack 1, resulting in this configuration: ;; [D] ;; [N] [C] ;; [Z] [M] [P] ;; 1 2 3 ;; In the second step, three crates are moved from stack 1 ;; to stack 3. Crates are moved one at a time, so the first ;; crate to be moved (D) ends up below the second and third ;; crates: ;; [Z] ;; [N] ;; [C] [D] ;; [M] [P] ;; 1 2 3 ;; Then, both crates are moved from stack 2 to stack 1. ;; Again, because crates are moved one at a time, crate C ;; ends up below crate M: ;; [Z] ;; [N] ;; [M] [D] ;; [C] [P] ;; 1 2 3 ;; Finally, one crate is moved from stack 1 to stack 2: ;; [Z] ;; [N] ;; [D] ;; [C] [M] [P] ;; 1 2 3 ;; The Elves just need to know which crate will end up on ;; top of each stack; in this example, the top crates are C ;; in stack 1, M in stack 2, and Z in stack 3, so you should ;; combine these together and give the Elves the message ;; CMZ. ;; After the rearrangement procedure completes, what crate ;; ends up on top of each stack? (defparameter day5/test-data '(" [D]" "[N] [C]" "[Z] [M] [P]" " 1 2 3" "" "move 1 from 2 to 1" "move 3 from 1 to 3" "move 2 from 2 to 1" "move 1 from 1 to 2")) (defun day5/whitespacep (ch) (or (char= ch #\space) (char= ch #\tab) (char= ch #\newline))) (defun day5/accumulate-tokens (line) (let ((tokens)) (reduce (lambda (acc ch) (cond ((not (day5/whitespacep ch)) (cons ch acc)) ((null acc) nil) (t (push acc tokens) nil))) ;; add a space to trigger the final push (reverse (cons #\space line)) :initial-value nil) tokens)) (defun day5/parse-drawing (lines) (let* ((lines (nreverse lines)) (col-count (length (day5/accumulate-tokens (car lines)))) (pile-lines (mapcar (lambda (line) (append line (make-sequence 'list (- (* col-count 4) (length line)) :initial-element #\space))) (cdr lines)))) (loop for line in pile-lines collect (loop for i from 0 below col-count collect (nth (1+ (* 4 i)) line))))) (defun day5/partition-input (lines) (let* ((lines (mapcar (lambda (line) (coerce line 'list)) lines)) (split-pos (position nil lines))) (list (subseq lines 0 split-pos) (subseq lines (1+ split-pos))))) (defun day5/construct-stacks (drawing) (let ((lines (day5/parse-drawing drawing))) (loop for i from 0 below (length (car lines)) collect (reduce (lambda (acc row) (let ((ch (nth i row))) (if (char= #\space ch) acc (cons ch acc)))) lines :initial-value nil)))) (defun day5/parse-instructions (lines) (loop for line in (mapcar #'day5/accumulate-tokens lines) collect (mapcar (lambda (idx) (parse-integer (coerce (nth idx line) 'string))) '(1 3 5)))) (defun day5/compute-part1 (lines) (destructuring-bind (drawing instructions) (day5/partition-input lines) (let ((instructions (day5/parse-instructions instructions)) (stacks (day5/construct-stacks drawing))) (dolist (task instructions) (destructuring-bind (n from to) task (dotimes (i n) (push (pop (nth (1- from) stacks)) (nth (1- to) stacks))))) (coerce (mapcar #'car stacks) 'string)))) (defun day5/part1 () (day5/compute-part1 (load-lines "day5.txt"))) ;; --- Part Two --- ;; As you watch the crane operator expertly rearrange the ;; crates, you notice the process isn't following your ;; prediction. ;; Some mud was covering the writing on the side of the ;; crane, and you quickly wipe it away. The crane isn't a ;; CrateMover 9000 - it's a CrateMover 9001. ;; The CrateMover 9001 is notable for many new and exciting ;; features: air conditioning, leather seats, an extra cup ;; holder, and the ability to pick up and move multiple ;; crates at once. ;; Again considering the example above, the crates begin in ;; the same configuration: ;; [D] ;; [N] [C] ;; [Z] [M] [P] ;; 1 2 3 ;; Moving a single crate from stack 2 to stack 1 behaves the ;; same as before: ;; [D] ;; [N] [C] ;; [Z] [M] [P] ;; 1 2 3 ;; However, the action of moving three crates from stack 1 ;; to stack 3 means that those three moved crates stay in ;; the same order, resulting in this new configuration: ;; [D] ;; [N] ;; [C] [Z] ;; [M] [P] ;; 1 2 3 ;; Next, as both crates are moved from stack 2 to stack 1, ;; they retain their order as well: ;; [D] ;; [N] ;; [C] [Z] ;; [M] [P] ;; 1 2 3 ;; Finally, a single crate is still moved from stack 1 to ;; stack 2, but now it's crate C that gets moved: ;; [D] ;; [N] ;; [Z] ;; [M] [C] [P] ;; 1 2 3 ;; In this example, the CrateMover 9001 has put the crates ;; in a totally different order: MCD. ;; Before the rearrangement process finishes, update your ;; simulation so that the Elves know where they should stand ;; to be ready to unload the final supplies. After the ;; rearrangement procedure completes, what crate ends up on ;; top of each stack? (defun day5/compute-part2 (lines) (destructuring-bind (drawing instructions) (day5/partition-input lines) (let ((instructions (day5/parse-instructions instructions)) (stacks (day5/construct-stacks drawing))) (dolist (task instructions) (destructuring-bind (n from to) task (macrolet ((src () '(nth (1- from) stacks)) (dst () '(nth (1- to) stacks))) (setf (dst) (append (subseq (src) 0 n) (dst)) (src) (subseq (src) n))))) (coerce (mapcar #'car stacks) 'string)))) (defun day5/part2 () (day5/compute-part2 (load-lines "day5.txt")))