aboutsummaryrefslogtreecommitdiffstats
path: root/AoC2022/17/solver.lisp
blob: bad968a434bfc024ac8161c8e432b4aab2e47e37 (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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
(ql:quickload '(:fiveam :uiop :arrows))

(defconstant +chamber-width+ (the fixnum 7))

(defun point (x y)
  (+ x (* y +chamber-width+)))

(defun coords (point)
  (declare (optimize (speed 3)))
  (declare (type fixnum point))
  (floor point +chamber-width+))

(defparameter *rocks*
  (vector (list (point 0 0) (point 1 0) (point 2 0) (point 3 0))
          (list (point 1 0)
                (point 0 1) (point 1 1) (point 2 1)
                (point 1 2))
          (list (point 0 0) (point 1 0) (point 2 0)
                (point 2 1)
                (point 2 2))
          (list (point 0 0) (point 0 1) (point 0 2) (point 0 3))
          (list (point 0 0) (point 1 0)
                (point 0 1) (point 1 1))))

(defun left (point) (1- point))
(defun right (point) (1+ point))
(defun down (point) (- point +chamber-width+))

(defun no-collision-move (object direction obstacles)
  (declare (optimize (speed 3)))
  (mapcar
   (lambda (current)
     (let ((next (funcall
                  (ecase direction
                    (#\> #'right)
                    (#\< #'left)
                    (#\v #'down))
                  current)))
       (multiple-value-bind (y2 x2) (coords next)
         (declare (type fixnum y2 x2))
         (if (and (>= y2 0)
                  ;; no rolling boundaries
                  (multiple-value-bind (y x) (coords current)
                    (declare (type fixnum y x))
                    (= 1 (+ (abs (- x x2)) (abs (- y y2)))))
                  (not (member next obstacles :test #'eq)))
             next (return-from no-collision-move object)))))
   object))

(fiveam:test moves
  (fiveam:is (equal '(6) (no-collision-move '(6) #\v nil)))
  (fiveam:is (equal '(6) (no-collision-move '(6) #\> nil)))
  (fiveam:is (equal '(6) (no-collision-move '(6) #\< '(5)))))

(defun gen-object (seq)
  (let ((len (length seq))
        (index 0))
    (lambda ()
      (prog1 (aref seq index)
        (setf index (mod (1+ index) len))))))

(defun place-rock (rock highpoint)
  (declare (optimize (speed 3)))
  (declare (type fixnum highpoint))
  (let ((left-pad 2)
        (altitude (* +chamber-width+ highpoint)))
    (declare (type fixnum altitude))
    (mapcar (lambda (point)
              (declare (type fixnum point))
              (the fixnum (+ point left-pad altitude))) rock)))

(defun next-move (rock next-move obstacles)
  (let* ((shift (no-collision-move rock next-move obstacles))
         (drop (no-collision-move shift #\v obstacles)))
    (values drop (equal shift drop))))

(defun highpoint (obstacles)
  (1+ (coords (reduce #'max obstacles))))

(defun simulate (chamber rock next-shift)
  (declare (optimize (speed 3)))
  (declare (type function next-shift))
  (multiple-value-bind (new-place done-falling-p)
      (next-move rock (funcall next-shift) chamber)
    (if done-falling-p
        (values (append new-place chamber) (highpoint new-place))
        (simulate chamber new-place next-shift))))


(defun solver (drops-left highpoint obstacles next-rock next-shift)
  (declare (optimize (speed 3)))
  (declare (type fixnum drops-left highpoint))
  (declare (type function next-rock))
  (if (zerop drops-left)
      ;; (values highpoint obstacles)
      highpoint
      (multiple-value-bind (new-obstacles posible-high)
          (simulate obstacles
                    (place-rock (funcall next-rock) (the fixnum (+ highpoint 3)))
                    next-shift)

        (declare (type fixnum posible-high))
        (solver (1- drops-left) (max highpoint posible-high)
                (if (zerop (mod drops-left 100)) ;; arbitrarily chop list
                    (subseq new-obstacles 0 (min 128 (length new-obstacles)))
                    new-obstacles)
                next-rock next-shift))))

(fiveam:test solutions
  (fiveam:is
   (= 3068 (solver 2022 0 nil (gen-object *rocks*) (gen-object (uiop:read-file-line "eg-in")))))
  (fiveam:is
   (= 3141 (solver 2022 0 nil (gen-object *rocks*) (gen-object (uiop:read-file-line "input"))))))

;; (print (solver 1000000000000 0 nil (gen-object *rocks*) (gen-object (uiop:read-file-line "eg-in"))))
(time (solver 40000 0 nil (gen-object *rocks*) (gen-object (uiop:read-file-line "eg-in"))))
;; (require :sb-sprof)
;; (sb-sprof::start-profiling)
;; (sb-sprof:report :type :flat)

;; (let ((a (make-array 5 :fill-pointer 0 :adjustable t)))
;;   (vector-push-extend 2 a)
;;   (vector-push-extend 6 a)
;;   (vector-push-extend 8 a)
;;   ;; (vector-push-extend 2 a)
;;   ;; (vector-push-extend 6 a)
;;   ;; (vector-push-extend 5 a)
;;   (vector-push-extend 5 a))