(defpackage :aoc/2024/07 (:use :cl :aoc :alexandria :trivia) (:export #:sample-data #:sample-data2 #:part1 #:part2 #:parse-line #:make-equation #:equation-result #:equation-operands #:mul-branch #:plus-branch #:plus-branch #:solvedp #:valid-equation #:concat-branch )) (in-package :aoc/2024/07) (defstruct equation result operands) (defun parse-line (line) "return an equation with the result and the operands in reverse order as in the text" (let ((ns (mapcar #'parse-integer (ppcre:split ":? " line)))) (make-equation :result (first ns) :operands (reverse (cdr ns))))) (defun parse-input (lines) (mapcar #'parse-line lines)) (defparameter input-text (test-input 2024 7)) (defparameter input-data (parse-input input-text)) (defparameter sample-text (aoc:split-lines "190: 10 19 3267: 81 40 27 83: 17 5 156: 15 6 7290: 6 8 6 15 161011: 16 10 13 192: 17 8 14 21037: 9 7 18 13 292: 11 6 16 20")) (defparameter sample-data (parse-input sample-text)) (defun mul-branch (equation) "returns simplified equation after applying the * operator or nil if not possible" (let ((r (equation-result equation)) (os (equation-operands equation))) (and (zerop (mod r (first os))) (make-equation :result (/ r (first os)) :operands (cdr os))))) (defun plus-branch (equation) "returns simplified equation after applying the + operator or nil if not possible" (let ((r (equation-result equation)) (os (equation-operands equation))) (and (>= r(first os)) (make-equation :result (- r (first os)) :operands (cdr os))))) (defun solvedp (equation) "returns true if equation has been solved" (let ((r (equation-result equation)) (os (equation-operands equation))) (and (= (length os) 1) (= r (first os))))) (defun valid-equation (equation) "recursively determine if the equation is valid" (if (equation-operands equation) (let ((mb (mul-branch equation)) (pb (plus-branch equation))) (or (solvedp equation) (and mb (valid-equation mb)) (and pb (valid-equation pb)))) nil)) (defun sum-valid-eqs (eqs) (loop for eq in eqs if (valid-equation eq) sum (equation-result eq))) (defun part1 (data) (format nil "~A" (sum-valid-eqs data))) (defun wrapping-power-of-10 (x) (expt 10 (ceiling (log (1+ x) 10)))) (defun concat-branch (equation) (let* ((r (equation-result equation)) (os (equation-operands equation)) (o (first os)) (wrap (wrapping-power-of-10 o))) (and (= (mod r wrap) o) (make-equation :result (floor (/ r wrap)) :operands (cdr os))))) (defun valid-equation-part2 (equation) (if (equation-operands equation) (let ((mb (mul-branch equation)) (pb (plus-branch equation)) (cb (concat-branch equation))) (or (solvedp equation) (and cb (valid-equation-part2 cb)) (and mb (valid-equation-part2 mb)) (and pb (valid-equation-part2 pb)) )) nil)) (defun sum-valid-eqs-part2 (eqs) (loop for eq in eqs if (valid-equation-part2 eq) sum (equation-result eq))) (defun part2 (data) (format nil "~A" (sum-valid-eqs-part2 data))) (defun solve-day () (format t "part1: ~A~%" (part1 input-data)) (format t "part2: ~A~%" (part2 input-data)))