aboutsummaryrefslogtreecommitdiffstats
path: root/AoC2022/22/solver.lisp
blob: 431158a22f19fd3217da7b2a48097d67afc66bd3 (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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
(ql:quickload '(fiveam uiop arrows))

(defun parse-instructions (str &optional (start 0))
  (multiple-value-bind (action str-pos)
      (case (aref str start)
        (#\R 'right)
        (#\L 'left)
        (t (parse-integer str :start start :junk-allowed t)))
    (let ((next (or str-pos (1+ start))))
      (if (= next (length str))
          (list action)
          (cons action (parse-instructions str next))))))

(defun create-field (filename)
  (let* ((data (uiop:read-file-lines filename))
         (map (butlast data 2))
         (instructions (car (last data)))
         (rows (length map))
         (columns (reduce (lambda (a r) (max a (length r))) map :initial-value 0))
         (field (make-array (list rows columns) :initial-element nil)))
    (loop for row in map
          for i from 0
          do (loop for entry across row
                   for j from 0
                   do (setf (aref field i j)
                            (ecase entry
                              (#\. 'free)
                              (#\# 'wall)
                              (#\Space nil)))))
    (values field (parse-instructions instructions))))


(defun get-start (field)
  (loop for i from 0
        until (aref field 0 i)
        finally (return i)))

(defstruct state
  x-pos y-pos direction)

(defun advance (field state)
  (destructuring-bind (height width) (array-dimensions field)
    (with-slots (direction x-pos y-pos) state
      (destructuring-bind (new-x new-y)
          (ecase direction
            (north (list x-pos (mod (1- y-pos) height)))
            (south (list x-pos (mod (1+ y-pos) height)))
            (east  (list (mod (1+ x-pos) width) y-pos))
            (west  (list (mod (1- x-pos) width) y-pos)))
        (let ((new-state (make-state :x-pos new-x :y-pos new-y :direction direction)))
          (if (aref field new-y new-x)
              new-state
              (advance field new-state)))))))

(defconstant +directions+ #(north east south west))

(defun new-direction (current-direction turn)
  (svref +directions+
         (mod
          (+
           (ecase turn
             (left -1)
             (right 1))
           (position current-direction +directions+))
          4)))

(defun walk (field state steps)
  (if (zerop steps)
      state
      (let ((new-state (advance field state)))
        (with-slots (x-pos y-pos) new-state
            (ecase (aref field y-pos x-pos)
              (free (walk field new-state (1- steps)))
              (wall state))))))

(fiveam:test preparation
  (fiveam:is (eq 'south (new-direction 'east 'right)))
  (fiveam:is (eq 'west (new-direction 'north 'left)))
  (fiveam:is (equal
              (parse-instructions "10R5L5R10L4R5L5" )
              '(10 RIGHT 5 LEFT 5 RIGHT 10 LEFT 4 RIGHT 5 LEFT 5))))


(defun decode-state (state)
  (with-slots (x-pos y-pos direction) state
    (+ (* 1000 (1+ y-pos))
       (* 4 (1+ x-pos))
       (ecase direction
         (east 0)
         (south 1)
         (west 2)
         (north 3)))))

(defun solver (filename)
  (multiple-value-bind (field instructions) (create-field filename)
    (let ((state (make-state :x-pos (get-start field) :y-pos 0 :direction 'east)))
      (dolist (move instructions)
        (if (numberp move)
            (setf state (walk field state move))
            (setf (state-direction state) (new-direction (state-direction state) move))))
      (decode-state state))))

(fiveam:test solutions
  (fiveam:is (= 6032 (solver "eg-in")))
  (fiveam:is (= 159034 (solver "input"))))