aoc-cl/src/2024/day07.lisp

130 lines
3.2 KiB
Common Lisp

(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)))