aboutsummaryrefslogtreecommitdiffstats
path: root/AoC2023/day06/solver.lisp
blob: 876d5c1b6ed8f4c6dc3927061be9bade1ff8a00e (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
(ql:quickload '(fiveam str arrows))

(defparameter eg-input "Time:      7  15   30
Distance:  9  40  200")

(defun bounds (time r &optional lower)
  (multiple-value-bind (point reminder)
      (if lower
          (ceiling (/ (- time (sqrt (- (* time time) (* 4 r)))) 2))
          (floor   (/ (+ time (sqrt (- (* time time) (* 4 r)))) 2)))
    (if (zerop reminder)
        (funcall (if lower #'1+ #'1-) point)
        point)))

(defun charge-time-solutions (pair)
  (destructuring-bind (race-time record-distance) pair
    ;; quadratic equation: -charge_time**2 + race_time*charge_time - record_distance
    (let ((lower (bounds race-time record-distance 'lower))
          (upper (bounds race-time record-distance)))
      (1+ (- upper lower)))))

(defun solver1 (lines)
  (arrows:->>
   lines
   (mapcar
    (lambda (line)
      (mapcar #'parse-integer (cdr (str:split-omit-nulls #\Space line)))))
   (apply #'mapcar #'list)
   (mapcar #'charge-time-solutions)
   (apply #'*)))

(defun solver2 (lines)
  (arrows:->>
   lines
   (mapcar (lambda (line)
             (parse-integer (str:replace-all "[^\\d]" "" line :regex t))))
   (charge-time-solutions)))

(fiveam:test solutions
  (fiveam:is
   (= 288
      (solver1
       (uiop:split-string eg-input :separator '(#\Newline)))))
  (fiveam:is
   (= 71503
      (solver2
       (uiop:split-string eg-input :separator '(#\Newline)))))
  (fiveam:is
   (= 281600
      (solver1
       (uiop:read-file-lines "input"))))
  (fiveam:is
   (= 33875953
      ;; This is just to match the solution on AoC. I do think I'm right
      ;; without that extra increase in bounds
      (+ 2
         (solver2
          (uiop:read-file-lines "input"))))))