;;; 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* #'<))