blob: 3297fd5ad3525d9a0953deabff6482d94b59eef5 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
(ql:quickload '(fiveam uiop))
(defstruct land
elevation
neighbors)
(defun possible-directions (width height)
;; point on grid is p=x+y*width
(lambda (pos)
(let ((x (mod pos width))
(y (floor pos width)))
(delete nil
(list
(when (< -1 x (1- width)) (1+ pos)) ;; right move
(when (< -1 y (1- height)) (+ pos width)) ;; down move
(when (< 0 x width) (1- pos)) ;; left move
(when (< 0 y height) (- pos width))) ;; up move
:test #'eq))))
(defun elevation (chr)
(case chr
(#\S 0)
(#\E 27)
(t (- (char-code chr) 96))))
(defun land (input-file)
(let ((data (uiop:read-file-lines input-file)))
(make-land :elevation (map 'vector #'elevation (apply #'concatenate 'string data))
:neighbors (possible-directions (length (car data)) (length data)))))
(defun next-steps (current land paths)
(flet ((appropriate (next)
(and (not (gethash next paths)) ;; not visited
(>= 1 (- (aref (land-elevation land) next)
(aref (land-elevation land) current)))
(setf (gethash next paths) (cons next (gethash current paths))))))
(remove-if-not #'appropriate (funcall (land-neighbors land) current))))
(defun shortest-path (starts land paths)
(unless (null starts)
(shortest-path (append (cdr starts) (next-steps (car starts) land paths))
land paths)))
(defun solver (input-file start-p)
(let* ((land (land input-file))
(paths (make-hash-table :test #'eq))
(finish (position 27 (land-elevation land) :test #'eq))
(starts (loop for i from 0
for el across (land-elevation land)
when (funcall start-p el)
do (setf (gethash i paths) (list i))
and collect i)))
(shortest-path starts land paths)
(1- (length (gethash finish paths)))))
(fiveam:test solver
(fiveam:is (= 31 (solver "eg-in" (lambda (x) (= x 0)))))
(fiveam:is (= 29 (solver "eg-in" (lambda (x) (<= x 1)))))
(fiveam:is (= 361 (solver "input" (lambda (x) (= x 0)))))
(fiveam:is (= 354 (solver "input" (lambda (x) (<= x 1))))))
(fiveam:run-all-tests)
|