Advent-of-Code/day12-scratch.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* #'<))