From 047bc474bd8330a0a48b31627ad02909b9a221aa Mon Sep 17 00:00:00 2001 From: efim Date: Wed, 14 Dec 2022 18:31:16 +0000 Subject: [PATCH] day 12 - paths over grid; dfs --- day12-input.txt | 41 ++++++++ day12-scratch.lisp | 239 +++++++++++++++++++++++++++++++++++++++++++++ day12-test.txt | 5 + day12.lisp | 117 ++++++++++++++++++++++ 4 files changed, 402 insertions(+) create mode 100644 day12-input.txt create mode 100644 day12-scratch.lisp create mode 100644 day12-test.txt create mode 100644 day12.lisp diff --git a/day12-input.txt b/day12-input.txt new file mode 100644 index 0000000..f73c638 --- /dev/null +++ b/day12-input.txt @@ -0,0 +1,41 @@ +abcccccccaaaaaccccaaaaaaaccccccccccccccccccccccccccccccccccccaaaaa +abaacccaaaaaaccccccaaaaaaaaaaaaaccccccccccccccccccccccccccccaaaaaa +abaacccaaaaaaaccccaaaaaaaaaaaaaacccccccccccccaacccccccccccccaaaaaa +abaacccccaaaaaacaaaaaaaaaaaaaaaacccccccccccccaacccccccccccccacacaa +abaccccccaaccaacaaaaaaaaaacccaacccccccccccccaaacccccccccccccccccaa +abcccccccaaaacccaaaaaaaaacccccccccccccaaacccaaacccccccccccccccccaa +abccccccccaaaccccccccaaaacccccccccccccaaaaacaaaccacacccccccccccccc +abccccccccaaacaaacccccaaacccccccccccccaaaaaaajjjjjkkkcccccaacccccc +abcccccaaaaaaaaaacccccaaccccccccccciiiiiijjjjjjjjjkkkcaaaaaacccccc +abcccccaaaaaaaaacccccccccccccccccciiiiiiijjjjjjjrrkkkkaaaaaaaacccc +abcccccccaaaaaccccccccccccccccccciiiiiiiijjjjrrrrrppkkkaaaaaaacccc +abcccaaccaaaaaacccccccccccaacaaciiiiqqqqqrrrrrrrrpppkkkaaaaaaacccc +abccaaaaaaaaaaaaccccacccccaaaaaciiiqqqqqqrrrrrruuppppkkaaaaacccccc +abcccaaaaaaacaaaacaaacccccaaaaaahiiqqqqtttrrruuuuupppkkaaaaacccccc +abcaaaaaaaccccaaaaaaacccccaaaaaahhqqqtttttuuuuuuuuuppkkkccaacccccc +abcaaaaaaaaccccaaaaaacccccaaaaaahhqqqtttttuuuuxxuuuppkklcccccccccc +abcaaaaaaaacaaaaaaaaaaacccccaaachhhqqtttxxxuuxxyyuuppllllccccccccc +abcccaaacaccaaaaaaaaaaaccccccccchhhqqtttxxxxxxxyuupppplllccccccccc +abaacaacccccaaaaaaaaaaaccccccccchhhqqtttxxxxxxyyvvvpppplllcccccccc +abaacccccccccaaaaaaacccccccccccchhhpppttxxxxxyyyvvvvpqqqlllccccccc +SbaaccccccaaaaaaaaaaccccccccccchhhppptttxxxEzzyyyyvvvqqqlllccccccc +abaaaaccccaaaaaaaaacccccccccccchhhpppsssxxxyyyyyyyyvvvqqqlllcccccc +abaaaacccccaaaaaaaacccccccccccgggpppsssxxyyyyyyyyyvvvvqqqlllcccccc +abaaacccaaaacaaaaaaaccccccccccgggpppsswwwwwwyyyvvvvvvqqqllllcccccc +abaaccccaaaacaaccaaaacccccccccgggppssswwwwwwyyywvvvvqqqqmmmccccccc +abaaccccaaaacaaccaaaaccaaaccccggpppssssswwswwyywvqqqqqqmmmmccccccc +abcccccccaaacccccaaacccaaacaccgggpppssssssswwwwwwrqqmmmmmccccccccc +abcccccccccccccccccccaacaaaaacgggppooosssssrwwwwrrrmmmmmcccccccccc +abcccccccccccccccccccaaaaaaaacggggoooooooorrrwwwrrnmmmdddccaaccccc +abaccccccccccccaacccccaaaaaccccggggoooooooorrrrrrrnmmddddcaaaccccc +abaccccccccaaaaaaccccccaaaaaccccggfffffooooorrrrrnnndddddaaaaccccc +abaacccccccaaaaaacccccaaaaaacccccffffffffoonrrrrrnnndddaaaaaaacccc +abaaccccccccaaaaaaaccacaaaacccccccccffffffonnnnnnnndddaaaaaaaacccc +abccccccccccaaaaaaaaaaaaaaaccccccccccccfffennnnnnnddddccaaaccccccc +abcccccccccaaaaaaacaaaaaaaaaacccccccccccffeennnnnedddccccaaccccccc +abcccccccccaaaaaaccaaaaaaaaaaaccccccccccaeeeeeeeeeedcccccccccccccc +abccccccccccccaaaccaaaaaaaaaaaccccccccccaaaeeeeeeeecccccccccccccaa +abcccccccaaccccccccaaaaaaaacccccccccccccaaaceeeeecccccccccccccccaa +abaaccaaaaaaccccccccaaaaaaaacccccccccccccaccccaaacccccccccccaaacaa +abaaccaaaaacccccaaaaaaaaaaacccccccccccccccccccccacccccccccccaaaaaa +abaccaaaaaaaaccaaaaaaaaaaaaaacccccccccccccccccccccccccccccccaaaaaa diff --git a/day12-scratch.lisp b/day12-scratch.lisp new file mode 100644 index 0000000..8da017a --- /dev/null +++ b/day12-scratch.lisp @@ -0,0 +1,239 @@ +;;; https://jira.ringcentral.com/browse/ANY-13016 + +;; climbind the hill - only 1 elevation higher, any elevation lower +;; only movements UP, DOWN, LEFT, RIGHT. +;; bfs should do. +;; and hide current character in order to prevent backtracking + +;; so. from start (consider it to be 'a') +;; one iteration is: +;; collect neighbors, filter them to only have applicable neighbors elevation +1 or lover +;; hide current place, run dfs from the neighbors - it should return the shortest path from them or -1 +;; then select shortest path add +1 and return to parent + +;; not sure which structures would be more comfortable + +(defparameter *day-12-test-lines* + (mapcar (lambda (line) (cl-ppcre:split "" line)) (uiop:read-file-lines "day12-test.txt"))) + +(defparameter *test-array* (make-array (list (length *day-12-test-lines*) (length (first *day-12-test-lines*))))) +(array-dimensions *test-array*) + +(destructuring-bind (rows cols) (array-dimensions *test-array*) + (loop for row from 0 below rows do + (loop for col from 0 below cols do + (setf (aref *test-array* row col) + (nth col (nth row *day-12-test-lines*)))))) + +(coerce (coerce "a" 'character) 'integer) +(char-code (coerce "a" 'character)) + + +(- "c" "a") +(- #\c #\a) +(eq #\c #\a) +(eq #\a #\a) + +;; next - instead of S set a, instead of E set z +;; and store S and E coords in a parameter + +(destructuring-bind (rows cols) (array-dimensions *test-array*) + (loop for row from 0 below rows do + (loop for col from 0 below cols do + (let* ((input-place-string (nth col (nth row *day-12-test-lines*))) + (input-char (coerce input-place-string 'character))) + (when (eq #\S input-char) + (setq input-char #\a) + ;; set coords for start + ) + (when (eq #\E input-char) + (setq input-char #\z) + ;; set end coords + ) + (setf (aref *test-array* row col) + input-char))))) + +*test-array* + +;; well. nah, using different parameters is not cool +;; next steps: +;; +;; function to get next points to check +(defun get-neighbors (row col) + (list (list row (1- col)) + (list (1- row) col) + (list row (1+ col)) + (list (1+ row) col))) + +(defun coord-in-dimentions (coords array) + (destructuring-bind (row col) coords + (destructuring-bind (rows cols) (array-dimensions array) + (and (>= row 0) + (>= col 0) + (< row rows) + (< col cols))))) + +(remove-if-not (lambda (coords) (when (coord-in-dimentions coords *array*) coords)) + (get-neighbors 0 0)) +(array-dimensions *array*) ; (5 8) - + +(defun get-neighbors-in-array (coords array) + (remove-if-not (lambda (coords) (when (coord-in-dimentions coords array) coords)) + (apply #'get-neighbors coords ))) + +(get-neighbors-in-array '(0 0) *array*) +(get-neighbors-in-array '(1 1) *array*) +(get-neighbors-in-array '(2 2) *array*) + +;; function to filter to be inside of array + +(defun move-valid-p (cur-char next-char) + (and (>= 1 (- (char-code next-char) + (char-code cur-char))) + (not (eq next-char #\%)))) + +(move-valid-p #\a #\a) +(move-valid-p #\a #\b) +(move-valid-p #\a #\c) +(move-valid-p #\a #\z) +(move-valid-p #\a #\%) + +;; function to check if target letter valid step from current letter +;; one-step function + +;; now the function would have to be recursive +(defun recursive-search-min-path (coords array end-coords) + (if (equal coords end-coords) + 0 + (let* ((neighbour-coords (get-neighbors-in-array coords array)) + (cur-char (aref array (first coords) (second coords))) + (valid-next-steps (remove-if-not + (lambda (coords) + (let ((next-step-char + (aref array (first coords) (second coords)))) + (move-valid-p cur-char next-step-char))) + neighbour-coords))) + (if (not valid-next-steps) + 999999 + (progn + (setf (aref array (first coords) (second coords)) #\%) + (setq lengts-from-next-steps (mapcar + (lambda (next-coords) + (recursive-search-min-path next-coords array end-coords)) + valid-next-steps)) + (setf (aref array (first coords) (second coords)) cur-char) + (1+ (apply #'min lengts-from-next-steps))))))) + +(print (recursive-search-min-path *day-12-start-coords *array* *day-12-end-coords)) +(recursive-search-min-path *day-12-start-coords *array* '(0 0)) +(recursive-search-min-path *day-12-start-coords *array* '(0 1)) +(recursive-search-min-path *day-12-start-coords *array* '(1 1)) + +;; yes. finally + +(eq '(1 2) '(1 2)) +(apply #'min '(1 3 -1)) +(apply #'min '(-1)) +(apply #'min '()) + +(not '()) +(not '(1 2)) + +(defun search-min-path () + + (defun recursive-search-min-path (coords array end-coords accum-path) + (if (equal coords end-coords) + (return-from search-min-path accum-path) + (let* ((neighbour-coords (get-neighbors-in-array coords array)) + (cur-char (aref array (first coords) (second coords))) + (valid-next-steps (remove-if-not + (lambda (coords) + (let ((next-step-char + (aref array (first coords) (second coords)))) + (move-valid-p cur-char next-step-char))) + neighbour-coords))) + (if (not valid-next-steps) + 999999 + (progn + (format t "reaching coord ~a~%" coords) + (setf (aref array (first coords) (second coords)) #\%) + (setq lengts-from-next-steps (mapcar + (lambda (next-coords) + (recursive-search-min-path next-coords array end-coords (1+ accum-path))) + valid-next-steps)) + (setf (aref array (first coords) (second coords)) cur-char) + (1+ (apply #'min lengts-from-next-steps))))))) + (recursive-search-min-path *day-12-start-coords *array* *day-12-end-coords 0)) + +(print (recursive-search-min-path *day-12-start-coords *array* *day-12-end-coords)) +(print (search-min-path)) + +;; wait. i'm doing dfs here, right? +;; for bfs i need to queue the next points. ugh + +(defun bfs-search-min-path (next-points-to-check) + (if (not next-points-to-check) + -1 ; if exhausted reachable coords + (let ((currently-checking (first next-points-to-check)) + (rest-to-check (rest next-points-to-check))) + (destructuring-bind (coords accum-path) currently-checking + (if (equal coords *day-12-end-coords) + accum-path + (let* ((neighbour-coords (get-neighbors-in-array coords *array*)) + (cur-char (aref *array* (first coords) (second coords))) + (valid-next-steps (remove-if-not + (lambda (coords) + (let ((next-step-char + (aref *array* (first coords) (second coords)))) + (move-valid-p cur-char next-step-char))) + neighbour-coords))) + + (format t "reaching coord ~a~%" coords) + (setf (aref *array* (first coords) (second coords)) #\%) + ;; format is '((1 1) 4) - coords and lenght-up-to + (setq next-steps-with-length (mapcar + (lambda (next-coords) + (list next-coords (1+ accum-path))) + valid-next-steps)) + ;; (setf (aref *array* (first coords) (second coords)) cur-char) + ;; (1+ (apply #'min lengts-from-next-steps)) + (bfs-search-min-path + (concatenate 'list rest-to-check next-steps-with-length)))))))) + +;; so, with bfs there's no need to revert the previous chars? + +(concatenate 'list '(12 3) '(5 4)) +(concatenate 'list '(12 3) '()) + +(bfs-search-min-path (list (list *day-12-start-coords 0))) +*array* + +(destructuring-bind (coords accum-path) '((1 2) 3) + `(got ,coords and ,accum-path)) + +;;; PART 2 + +;; find shortest path from every point at elevation #\a + +;; so, i'd need to reuse my function from different starting points +;; and reset the array after each search + +;; so. collect coords for all starting points +;; hm, yeah, only difference with just starting with all of them - need to reset the array + +(defparameter *day-12-2-starting-points '()) + +(destructuring-bind (rows cols) (array-dimensions *array*) + (loop for row from 0 below rows do + (loop for col from 0 below cols do + (when (eq #\a (aref *array* row col)) + (push `((,row ,col) 0) *day-12-2-starting-points))))) + +(defparameter *day-12-2-all-trail-lengts* nil) + +(setq *day-12-2-all-trail-lengts* + (loop for start-point in *day-12-2-starting-points + collect (bfs-search-min-path (list start-point)) + do (restore-params))) + +(first (sort *day-12-2-all-trail-lengts* #'<)) diff --git a/day12-test.txt b/day12-test.txt new file mode 100644 index 0000000..86e9cac --- /dev/null +++ b/day12-test.txt @@ -0,0 +1,5 @@ +Sabqponm +abcryxxl +accszExk +acctuvwj +abdefghi diff --git a/day12.lisp b/day12.lisp new file mode 100644 index 0000000..387a515 --- /dev/null +++ b/day12.lisp @@ -0,0 +1,117 @@ +;;; https://jira.ringcentral.com/browse/ANY-13016 + +;; (defparameter *day-12-file-name* "day12-test.txt") +(defparameter *day-12-file-name* "day12-input.txt") +(defparameter *day-12-lines* nil) +(defparameter *day-12-start-coords nil) +(defparameter *day-12-end-coords nil) +(defparameter *array* nil) + +(defun restore-params () + (setq *day-12-lines* + (mapcar (lambda (line) (cl-ppcre:split "" line)) (uiop:read-file-lines *day-12-file-name*))) + (setq *array* (make-array (list (length *day-12-lines*) (length (first *day-12-lines*))))) + + (destructuring-bind (rows cols) (array-dimensions *array*) + (loop for row from 0 below rows do + (loop for col from 0 below cols do + (let* ((input-place-string (nth col (nth row *day-12-lines*))) + (input-char (coerce input-place-string 'character))) + (when (eq #\S input-char) + (setq input-char #\a) + (setq *day-12-start-coords (list row col)) + ) + (when (eq #\E input-char) + (setq input-char #\z) + (setq *day-12-end-coords (list row col)) + ;; set end coords + ) + (setf (aref *array* row col) + input-char)))))) +(restore-params) + +*array* +(array-dimensions *array*) +*day-12-start-coords +*day-12-end-coords + +;; yay. + +(defun get-neighbors (row col) + (list (list row (1- col)) + (list (1- row) col) + (list row (1+ col)) + (list (1+ row) col))) + +(defun coord-in-dimentions (coords array) + (destructuring-bind (row col) coords + (destructuring-bind (rows cols) (array-dimensions array) + (and (>= row 0) + (>= col 0) + (< row rows) + (< col cols))))) + +(defun get-neighbors-in-array (coords array) + (remove-if-not (lambda (coords) (when (coord-in-dimentions coords array) coords)) + (apply #'get-neighbors coords ))) + +(defun move-valid-p (cur-char next-char) + (>= 1 (- (char-code next-char) + (char-code cur-char)))) + +;; recursion + +(defun bfs-search-min-path (next-points-to-check) + (if (not next-points-to-check) + 999999 ; if exhausted reachable coords + (let ((currently-checking (first next-points-to-check)) + (rest-to-check (rest next-points-to-check))) + (destructuring-bind (coords accum-path) currently-checking + (if (equal coords *day-12-end-coords) + accum-path + (let* ((neighbour-coords (get-neighbors-in-array coords *array*)) + (cur-char (aref *array* (first coords) (second coords))) + (valid-next-steps (remove-if-not + (lambda (coords) + (let ((next-step-char + (aref *array* (first coords) (second coords)))) + (move-valid-p cur-char next-step-char))) + neighbour-coords))) + + (format t "reaching coord ~a~%" coords) + (setf (aref *array* (first coords) (second coords)) #\%) + ;; format is '((1 1) 4) - coords and lenght-up-to + (setq next-steps-with-length (mapcar + (lambda (next-coords) + (list next-coords (1+ accum-path))) + valid-next-steps)) + ;; (setf (aref *array* (first coords) (second coords)) cur-char) + ;; (1+ (apply #'min lengts-from-next-steps)) + (bfs-search-min-path + (concatenate 'list rest-to-check next-steps-with-length)))))))) + +(bfs-search-min-path (list (list *day-12-start-coords 0))) +;; 339 + +;; PART 2 + +(defparameter *day-12-2-starting-points '()) +(defparameter *day-12-2-all-trail-lengts* nil) + +(progn + ;; get starting points + (destructuring-bind (rows cols) (array-dimensions *array*) + (loop for row from 0 below rows do + (loop for col from 0 below cols do + (when (eq #\a (aref *array* row col)) + (push `((,row ,col) 0) *day-12-2-starting-points))))) + + ;; calculate path lengths from all starting points + (setq *day-12-2-all-trail-lengts* + (loop for start-point in *day-12-2-starting-points + collect (bfs-search-min-path (list start-point)) + do (restore-params)))) +;; get shortest of them +(first (sort *day-12-2-all-trail-lengts* #'<)) + +;; yay. the shortest of all was 332