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

(defparameter damaged #\#)
(defparameter operational #\.)
(defparameter unknown #\?)

(defparameter eg-input "???.### 1,1,3
.??..??...?##. 1,1,3
?#?#?#?#?#?#?#? 1,3,1,6
????.#...#... 4,1,1
????.######..#####. 1,6,5
?###???????? 3,2,1")


(destructuring-bind (row . damage-list)
    (cl-ppcre:split "[\\s,]" "???.###. 1,1,3")
  (setf damage-list (mapcar #'parse-integer damage-list))
  (labels ((options (row-idx damage-group)
             (cond
               ;; commited to all options
               ((= damage-group (length damage-list))
                ;; If still damaged springs are left, then
                ;; it was a wrong arrangement, otherwise counts
                (if (find damaged (subseq row row-idx)) 0 1))
               ;; Not enough space to fit the current damaged-group
               ((<= (length row) (+ row-idx (elt damage-list damage-group))) 0)
               ;; Probe options. Either commit to a damage-group at row-idx or skip position
               (t
                (let* ((damaged-count (elt damage-list damage-group))
                       (next-slot (+ row-idx damaged-count)))
                  (+ (if (not (or (find operational (subseq row row-idx next-slot))
                                  (char-equal damaged (aref row next-slot))))
                         ;; commits to the damage group, because in condition it has space
                         ;; Skips one slot, because it checked that it wasn't damaged
                         (options (1+ next-slot) (1+ damage-group))
                         0)
                     (if (not (char-equal damaged (aref row row-idx)))
                         (options (1+ row-idx) damage-group)
                         0)))))))
    (options 0 0)))