240 lines
9.4 KiB
Common Lisp
240 lines
9.4 KiB
Common Lisp
;;; 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* #'<))
|