You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

6.8 KiB

Solution to p21

Load map

  (require 'dash)
  (with-temp-buffer
      (insert-file-contents "input-test")
      (advent/replace-multiple-regex-buffer '(("^\\(.*\\)$" . "\"\\1\"")))
      (goto-char (point-min))
      (insert "(setq data '(")
      (goto-char (point-max))
      (insert "))")
      (eval-buffer))

  (setq data-chars (-map #'advent/split-string-into-char-list data)
      height (length data-chars)
      width (length (car data-chars)))
  (setq numeric-keypad '("789"
                         "456"
                         "123"
                         " 0A"))

  (setq dir-keypad '(" ^A"
                     "<v>"))

  (setq numeric-keypad-data (-map #'advent/split-string-into-char-list numeric-keypad)
        dir-keypad-data (-map #'advent/split-string-into-char-list dir-keypad))

This function takes a string and returns a list of (a . b), where these are the adjacent pairs in the string. For instance, if you feed it with "ABC" it will return ((?A . ?B) (?B . ?C))

  (defun split-into-char-pairs (s)
    (let ((char-list (advent/split-string-into-char-list s)))
      (-zip-pair (-drop-last 1 char-list) (-drop 1 char-list))))

  (defun char-pairs-to-coordinate-pairs (p keypad)
    (cons
     (car (advent/coordinates-of (car p) keypad))
     (car (advent/coordinates-of (cdr p) keypad))))

This takes a cons cell (p . q) of positions and returns all paths that go from p to q without getting out of the boundary

  (defun subtract-vector (a b)
    (-zip-with #'- a b))

  (defun add-vector (a b)
    (-zip-with #'+ a b))

  (defun permute-string (s)
    "Return a list with all strings that can be obtained by permuting
  characters of S"
    (--map (apply #'string it)
           (-permutations
            (advent/split-string-into-char-list s))))

  (defun connecting-paths (r keypad)
    (--map (concat it "A")
           (--filter (path-allowed-p it (car r) keypad)
                     (let* ((difference (subtract-vector (cdr r) (car r)))
                            (hor (car difference))
                            (ver (cadr difference)))
                       (permute-string (concat
                                        (if (< hor 0) (make-string (- 0 hor) ?<)
                                          (make-string hor ?>))
                                        (if (< ver 0) (make-string (- 0 ver) ?^)
                                          (make-string ver  ?v))))))))

  (setq dir-alist '((?> .(1 0))
                    (?< . (-1 0))
                    (?^ . (0 -1))
                    (?v . (0 1))))

  (defun path-allowed-p (s start keypad)
    (not (--any (eq it 32)
                (--map (advent/char-at it keypad)
                       (-reductions-from #'add-vector start (--map (cdr (assoc it dir-alist)) (advent/split-string-into-char-list s)))))))
path-allowed-p

The following of course only works for part 1; with part 2 the stack would blow in my face since we would have too many options to check.

   (defun directions-for-string (s keypad-data)
    (--map (connecting-paths it keypad-data)
           (--map
            (char-pairs-to-coordinate-pairs it keypad-data)
            (split-into-char-pairs (concat "A" s)))))

  (defun pick-shortest-string (l)
    "picks the shortest string in the list L"
    (-reduce (lambda (a b)
               (if (> (length a) (length b)) b a))
             l))

  (defun shortest-path (s)
   (let* ((first-directional-keypad
           (directions-for-string s numeric-keypad-data))
          (second-directional-keypad
           (--map   ; this iterates on the possible sequences of the num
            (--map (directions-for-string it dir-keypad-data) it)
            first-directional-keypad))
          (third-directional-keypad
           (--map   ; this iterates on the possible sequences of the num
            (--map ; this iterates on the possible sequences for the dir #1
             (--map ; this iterates on the possible sequences for the dir #2
              (--map
               (directions-for-string it dir-keypad-data)
               it)
              it)
             it)
            second-directional-keypad)
           ))
     (let* ((level-two
             (--map
              (--map
               (--map
                (--map
                 (apply #'concat (-map #'pick-shortest-string it))
                 it)
                it)
               it)
              third-directional-keypad))
            (level-one
             (--map
              (--map
               (apply #'concat (-map #'pick-shortest-string it))
               it)
              level-two)))
       (apply #'concat (-map #'pick-shortest-string level-one)))))

  (defun replace-in-string (what with in)
    (replace-regexp-in-string (regexp-quote what) with in nil 'literal))

  (defun complexity (s)
    (* (string-to-number (replace-in-string "A" "" s))
     (length (shortest-path s))))

  (-reduce #'+ (-map #'complexity data))
1972

The new idea would be to run things bottom-down. For a given pair of chars at a given depth, what is the optimal way to put things together? It should be true that the optimal way to assemble things is the shortest concatenation of the optimal way at the previous depth

So we start from all possible combinations of keypad pairs

  (setq direction-pairs  (let ((directions (advent/split-string-into-char-list "<>^vA")))
                           (-mapcat (lambda (x) (--map (cons x it) directions)) directions)))
  (setq basis-induction
   (--map (cons it (connecting-paths (char-pairs-to-coordinate-pairs it dir-keypad-data) dir-keypad-data)) direction-pairs)
   first-step (--map (cons (car it) (pick-shortest-string (cdr it))) basis-induction) )

  ; here first-step contains the keys that need to be pressed on the
  ; human keypad to have the first keypad do as described
  (defun iterate-robot (s al)
    (apply #'concat (--map (cdr (assoc it al)) (split-into-char-pairs (concat "A" s)))))

  (defun iterate-step (l)
   (--map (cons (car it) (pick-shortest-string
            (--map (iterate-robot it l) (cdr it))))
    basis-induction))

  ; here iterate-step takes a list at step n and returns the same list
  ; at step n+1
  (setq last-step (funcall (-iteratefn 'iterate-step 24) first-step))

  (defun shortest-stuff (s)
    (length (apply 'concat (--map (pick-shortest-string (--map (iterate-robot it last-step) it))
                                  (directions-for-string s numeric-keypad-data)))))

  (defun complexity (s)
      (* (string-to-number (replace-in-string "A" "" s))
       (shortest-stuff s)))

    (-reduce #'+ (-map #'complexity data))

  ;oh, it does…