1(defpackage #:advent.2024.07
2 (:use #:cl #:advent.2024.00))
3(in-package #:advent.2024.07)
4
5;;; Part 1.
6;; Using + and *, find which test values (before the colon) can be solved with
7;; the remaining numbers. Add valid test values to get the answer.
8
9(defvar *example* "190: 10 19
103267: 81 40 27
1183: 17 5
12156: 15 6
137290: 6 8 6 15
14161011: 16 10 13
15192: 17 8 14
1621037: 9 7 18 13
17292: 11 6 16 20")
18
19(defun parse-string (str)
20 (loop for line in (uiop:split-string str :separator '(#\newline))
21 for numbers = (ppcre:split ":?\\s+" line)
22 collect (mapcar #'parse-integer numbers)))
23
24(defun compute1 (nums ops)
25 (reduce (lambda (a b)
26 (funcall (pop ops) a b))
27 nums))
28
29(defun enumerate-oplists (length)
30 (cond
31 ((zerop length) ())
32 ((= length 1) '((+) (*)))
33 (t
34 (flet ((num->oplist (n)
35 (loop for op across (map 'vector (alexandria:compose #'parse-integer #'string)
36 (format nil "~v,'0b" length n))
37 collect (case op
38 (0 '+)
39 (1 '*)))))
40 (loop for n from 0 to (1- (expt 2 length))
41 collect (num->oplist n))))))
42
43(defun valid? (nums)
44 (let ((oplen (- (length nums) 2)))
45 (loop for ops in (enumerate-oplists oplen)
46 if (= (car nums) (compute1 (cdr nums) ops))
47 return (car nums)
48 finally (return 0))))
49
50(defun solve1 (&optional (input (input)))
51 (loop for line in (parse-string (uiop:read-file-string input))
52 sum (valid? line)))
53
54;;; Part 2. A THIRD TYPE OF OPERATOR!
55;; The third op, ||, combines the digits from the left and right inputs into a
56;; single number --- e.g. 12 || 345 -> 12345.
57
58(defun enumerate-oplists2 (length)
59 (flet ((num->oplist (n)
60 (loop for op across (map 'vector (alexandria:compose #'parse-integer
61 #'string)
62 (format nil "~3,v,'0R" length n))
63 collect (case op
64 (0 '+)
65 (1 '*)
66 (2 'glom))))) ; ||
67 (loop for n from 0 to (1- (expt 3 length))
68 collect (num->oplist n))))
69
70(defun glom (a b)
71 (parse-integer (format nil "~D~D" a b)))
72
73(defun valid2? (nums)
74 (let ((oplen (- (length nums) 2)))
75 (loop for ops in (enumerate-oplists2 oplen)
76 if (= (car nums) (compute1 (cdr nums) ops))
77 return (car nums)
78 finally (return 0))))
79
80(defun solve2 (&optional (input (input)))
81 (loop for line in (parse-string (uiop:read-file-string input))
82 sum (valid2? line)))
83
84;;; This is taking a lot longer to run --- more operations to run through.. as
85;;; it goes I should say that I *could've*, and probably *should've*, rewritten
86;;; all the non-2 functions to be more general, then passed in the specifics
87;;; with the solve(1,2) ... but whatever.