(in-package :ca.mhcat.advent2022)

;; --- Day 3: Rucksack Reorganization ---

;; One Elf has the important job of loading all of the
;; rucksacks with supplies for the jungle journey.
;; Unfortunately, that Elf didn't quite follow the packing
;; instructions, and so a few items now need to be
;; rearranged.

;; Each rucksack has two large compartments. All items of a
;; given type are meant to go into exactly one of the two
;; compartments. The Elf that did the packing failed to
;; follow this rule for exactly one item type per rucksack.

;; The Elves have made a list of all of the items currently
;; in each rucksack (your puzzle input), but they need your
;; help finding the errors. Every item type is identified by
;; a single lowercase or uppercase letter (that is, a and A
;; refer to different types of items).

;; The list of items for each rucksack is given as
;; characters all on a single line. A given rucksack always
;; has the same number of items in each of its two
;; compartments, so the first half of the characters
;; represent items in the first compartment, while the
;; second half of the characters represent items in the
;; second compartment.

;; For example, suppose you have the following list of
;; contents from six rucksacks:

;; vJrwpWtwJgWrhcsFMMfFFhFp
;; jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
;; PmmdzqPrVvPwwTWBwg
;; wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
;; ttgJtRGJQctTZtZT
;; CrZsJsPPZsGzwwsLwLmpwMDw

;;     The first rucksack contains the items
;;     vJrwpWtwJgWrhcsFMMfFFhFp, which means its first
;;     compartment contains the items vJrwpWtwJgWr, while
;;     the second compartment contains the items
;;     hcsFMMfFFhFp. The only item type that appears in both
;;     compartments is lowercase p.

;;     The second rucksack's compartments contain
;;     jqHRNqRjqzjGDLGL and rsFMfFZSrLrFZsSL. The only item
;;     type that appears in both compartments is uppercase
;;     L.

;;     The third rucksack's compartments contain PmmdzqPrV
;;     and vPwwTWBwg; the only common item type is uppercase
;;     P.

;;     The fourth rucksack's compartments only share item
;;     type v.

;;     The fifth rucksack's compartments only share item
;;     type t.

;;     The sixth rucksack's compartments only share item
;;     type s.

;; To help prioritize item rearrangement, every item type
;; can be converted to a priority:

;;     Lowercase item types a through z have priorities 1
;;     through 26.

;;     Uppercase item types A through Z have priorities 27
;;     through 52.

;; In the above example, the priority of the item type that
;; appears in both compartments of each rucksack is 16 (p),
;; 38 (L), 42 (P), 22 (v), 20 (t), and 19 (s); the sum of
;; these is 157.

;; Find the item type that appears in both compartments of
;; each rucksack. What is the sum of the priorities of those
;; item types?

(defparameter day3/test-data
 '("vJrwpWtwJgWrhcsFMMfFFhFp"
   "jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL"
   "PmmdzqPrVvPwwTWBwg"
   "wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn"
   "ttgJtRGJQctTZtZT"
   "CrZsJsPPZsGzwwsLwLmpwMDw"))

(let ((priorities (loop for i from 0 below 26
                       collect (code-char (+ i (char-code #\a))) into lower
                       collect (code-char (+ i (char-code #\A))) into upper
                       finally (return (append lower upper)))))
 (defun day3/priority (ch)
   (1+ (position ch priorities))))

(defun day3/parse-line (line)
 (let* ((midpoint (/ (length line) 2))
        (compartments (list (subseq line 0 midpoint)
                            (subseq line midpoint))))
   (flet ((make-set (compartment)
            (remove-duplicates
             (coerce compartment 'list)
             :test #'char=)))
     (mapcar #'make-set compartments))))

(defun day3/intersection (coll &rest colls)
 (if (null colls)
     coll
     (apply #'day3/intersection
            (remove-duplicates
             (reduce (lambda (acc item)
                       (if (find item coll)
                           (cons item acc)
                           acc))
                     (first colls)
                     :initial-value nil)
             :test #'char=)
            (rest colls))))

(defun day3/all-dupe-priorities (dupes-by-rucksack)
 (mapcar
  #'day3/priority
  (apply #'append dupes-by-rucksack)))

(defun day3/compute-part1 (lst)
 (apply
  #'+
  (day3/all-dupe-priorities
   (reduce (lambda (acc line)
             (destructuring-bind (compartment1 compartment2)
                 (day3/parse-line line)
               (cons (day3/intersection compartment1 compartment2) acc)))
           lst
           :initial-value nil))))

(defun day3/part1 ()
 (day3/compute-part1 (load-lines "day3.txt")))

;; --- Part Two ---

;; As you finish identifying the misplaced items, the Elves
;; come to you with another issue.

;; For safety, the Elves are divided into groups of three.
;; Every Elf carries a badge that identifies their group.
;; For efficiency, within each group of three Elves, the
;; badge is the only item type carried by all three Elves.
;; That is, if a group's badge is item type B, then all
;; three Elves will have item type B somewhere in their
;; rucksack, and at most two of the Elves will be carrying
;; any other item type.

;; The problem is that someone forgot to put this year's
;; updated authenticity sticker on the badges. All of the
;; badges need to be pulled out of the rucksacks so the new
;; authenticity stickers can be attached.

;; Additionally, nobody wrote down which item type
;; corresponds to each group's badges. The only way to tell
;; which item type is the right one is by finding the one
;; item type that is common between all three Elves in each
;; group.

;; Every set of three lines in your list corresponds to a
;; single group, but each group can have a different badge
;; item type. So, in the above example, the first group's
;; rucksacks are the first three lines:

;; vJrwpWtwJgWrhcsFMMfFFhFp
;; jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
;; PmmdzqPrVvPwwTWBwg

;; And the second group's rucksacks are the next three
;; lines:

;; wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
;; ttgJtRGJQctTZtZT
;; CrZsJsPPZsGzwwsLwLmpwMDw

;; In the first group, the only item type that appears in
;; all three rucksacks is lowercase r; this must be their
;; badges. In the second group, their badge item type must
;; be Z.

;; Priorities for these items must still be found to
;; organize the sticker attachment efforts: here, they are
;; 18 (r) for the first group and 52 (Z) for the second
;; group. The sum of these is 70.

;; Find the item type that corresponds to the badges of each
;; three-Elf group. What is the sum of the priorities of
;; those item types?

(defun day3/compute-part2 (lst)
 (let ((groups (loop repeat (/ (length lst) 3)
                     for i = 0 then j
                     and j = 3 then (+ i 6)
                     collect (subseq lst i j))))
   (apply
    #'+
    (day3/all-dupe-priorities
     (mapcar (lambda (group)
               (apply #'day3/intersection group))
             groups)))))

(defun day3/part2 ()
 (day3/compute-part2 (load-lines "day3.txt")))