--- /dev/null
+(in-package #:adventofcode2020)
+
+(defun valid-password-count? (pw-spec)
+ (destructuring-bind (count-str char-str passwd) (split " " pw-spec)
+ (let ((countp (destructuring-bind (count-low count-high) (mapcar #'parse-integer (split "-" count-str))
+ (lambda (n)
+ (<= count-low n count-high))))
+ (char-count (loop with char = (char char-str 0)
+ for c across passwd
+ summing (if (char= c char) 1 0))))
+ (funcall countp char-count))))
+
+(defun valid-password-index? (pw-spec)
+ (destructuring-bind (index-str char-str passwd) (split " " pw-spec)
+ (let* ((indices (mapcar (compose #'1- #'parse-integer) (split "-" index-str)))
+ (char (char char-str 0))
+ (get-char (lambda (n) (char passwd n))))
+ (->> (mapcar get-char indices)
+ (remove char)
+ (length)
+ (= 1)))))
+
+(day 02 input
+ (let ((pws-and-specs (list-from input)))
+ (part1 (->> pws-and-specs
+ (remove-if-not #'valid-password-count?)
+ (length)))
+ (part2 (->> pws-and-specs
+ (remove-if-not #'valid-password-index?)
+ (length)))))
+
+(def-suite day02)
+(in-suite day02)
+
+(test valid-password-count?
+ (is (equal
+ '(t nil t)
+ (mapcar #'valid-password-count?
+ '("1-3 a: abcde" "1-3 b: cdefg" "2-9 c: ccccccccc")))))
+
+(test valid-password-index?
+ (is (equal
+ '(t nil nil)
+ (mapcar #'valid-password-index?
+ '("1-3 a: abcde" "1-3 b: cdefg" "2-9 c: ccccccccc")))))
+
+(run! 'day02)