Skip to content

Latest commit

 

History

History
227 lines (213 loc) · 6.32 KB

2022.09.org

File metadata and controls

227 lines (213 loc) · 6.32 KB

Day 09

Executing this code

If you have a lisp installation, emacs, org-mode, and org-babel support for lisp installed you can run this by:

  1. Starting slime (M-x slime)
  2. Typing C-c C-c in the block initialize.
  3. In the repl type (in-package :aoc-2022-09)
  4. Typing C-c C-c in the block answers

Initial stuffs

Packages to load

(unless (find-package :cl-ppcre)
  (ql:quickload "cl-ppcre"))
(unless (find-package :parseq)
  (ql:quickload "parseq"))
(unless (find-package :lparallel)
  (ql:quickload "lparallel"))
(unless (find-package :fiveam)
  (ql:quickload "fiveam"))
(unless (find-package :series)
  (ql:quickload "series"))
(unless (find-package :cl-permutation)
  (ql:quickload "cl-permutation"))
(unless (find-package :bordeaux-threads)
  (ql:quickload "bordeaux-threads"))

Create package for this day

<<packages>>
(defpackage :aoc-2022-09
  (:use :common-lisp
        :parseq
        :fiveam)
  (:export :problem-a
           :problem-b))
(in-package :aoc-2022-09)

Input

(defun parse-line (line)
  (let ((distance (parse-integer (subseq line 2))))
    (case (elt line 0)
      (#\U (make-list distance :initial-element #C(0 1)))
      (#\D (make-list distance :initial-element #C(0 -1)))
      (#\R (make-list distance :initial-element #C(1 0)))
      (#\L (make-list distance :initial-element #C(-1 0))))))

(defun process-stream (in)
  (loop for line = (read-line in nil)
        while line
        collect (parse-line line)))
(defun read-input (file)
  (with-open-file (in file)
    (process-stream in)))
(defparameter *input*
  (read-input "input/09.txt"))

Part 1

(defun distance (head tail)
  (+ (abs (- (realpart head) (realpart tail)))
     (abs (- (imagpart head) (imagpart tail)))))

(defun in-line (head tail)
  (or (= (realpart head) (realpart tail))
      (= (imagpart head) (imagpart tail))))

(defun direction-to (head tail)
  (if (in-line head tail)
      (cond ((< (realpart head) (realpart tail))
             #C(-1 0))
            ((< (imagpart head) (imagpart tail))
             #C(0 -1))
            ((> (realpart head) (realpart tail))
             #C(1 0))
            (t #C(0 1)))
      0))

(defun diagonal-to (head tail)
  (let ((dir 0))
    (if (< (realpart head) (realpart tail))
        (incf dir #C(-1 0))
        (incf dir #C(1 0)))
    (if (< (imagpart head) (imagpart tail))
        (incf dir #C(0 -1))
        (incf dir #C(0 1)))
    dir))

(defun move (head tail dir)
  (let* ((head (+ head dir))
         (distance (distance head tail)))
    (case distance
      (0 (list head tail))
      (1 (list head tail))
      (2 (list head (+ tail (direction-to head tail))))
      (otherwise (list head (+ tail (diagonal-to head tail)))))))

(defun apply-moves (moves)
  (loop with head = 0
        with tail = 0
        with locations = (list 0)
        for move in moves
        do (loop for dir in move
                 for result = (move head tail dir)
                 do (setf head (first result))
                    (setf tail (second result))
                    (pushnew tail locations))
        finally (return (length locations))))
(defun problem-a () (format t "Problem 09 A: ~a~%" (apply-moves *input*)))

Part 2

Instead of two parts there are now 10. I think I can apply the logic from above for doing each one in series. The move function above can be generalized into a move-to where the head has already been moved and I determine what the new tail will be.

(defun move-to (head tail)
  (let ((distance (distance head tail)))
    (case distance
      (0 tail)
      (1 tail)
      (2 (+ tail (direction-to head tail)))
      (otherwise (+ tail (diagonal-to head tail))))))

(defun move-snake (moves &optional (length 10))
  (loop with snake = (make-array length :initial-element 0)
        with locations = (list 0)
        for move in moves
        do (loop for dir in move
                 do (incf (aref snake 0) dir)
                    (loop for i from 1 below length
                          for pred = (aref snake (1- i))
                          for succ = (aref snake i)
                          do (setf (aref snake i) (move-to pred succ)))
                    (pushnew (aref snake (1- length)) locations))
        finally (return (length locations))))

(defun problem-b () (format t "Problem 09 B: ~a~%" (move-snake *input*)))

Putting it all together

<<read-input>>
<<input>>
<<initialize>>
<<structs>>
<<functions>>
<<input>>
<<problem-a>>
<<problem-b>>
(problem-a)
(problem-b)

Answer

Problem 09 A: 6367
Problem 09 B: 2536

Test Cases

(def-suite aoc.2022.09)
(in-suite aoc.2022.09)
(defparameter *sample-input*
  "R 4
U 4
L 3
D 1
R 4
D 1
L 5
R 2")

(defparameter *sample*
  (with-input-from-string (in *sample-input*)
    (process-stream in)))

(defparameter *big-sample-input*
  "R 5
U 8
L 8
D 3
R 17
D 10
L 25
U 20")

(defparameter *big-sample*
  (with-input-from-string (in *big-sample-input*)
    (process-stream in)))

(test part-1
  (is (= 13 (apply-moves *sample*)))
  (is (= 13 (move-snake *sample* 2))))
(test part-2
  (is (= 1 (move-snake *sample*)))
  (is (= 36 (move-snake *big-sample*))))
(run! 'aoc.2022.09)

Test Results

Running test suite AOC.2022.09
 Running test PART-1 ..
 Running test PART-2 ..
 Did 4 checks.
    Pass: 4 (100%)
    Skip: 0 ( 0%)
    Fail: 0 ( 0%)

Thoughts