(in-package :ca.mhcat.advent2022)

;; --- Day 1: Calorie Counting ---

;; Santa's reindeer typically eat regular reindeer food, but
;; they need a lot of magical energy to deliver presents on
;; Christmas. For that, their favorite snack is a special
;; type of star fruit that only grows deep in the jungle.
;; The Elves have brought you on their annual expedition to
;; the grove where the fruit grows.

;; To supply enough magical energy, the expedition needs to
;; retrieve a minimum of fifty stars by December 25th.
;; Although the Elves assure you that the grove has plenty
;; of fruit, you decide to grab any fruit you see along the
;; way, just in case.

;; Collect stars by solving puzzles. Two puzzles will be
;; made available on each day in the Advent calendar; the
;; second puzzle is unlocked when you complete the first.
;; Each puzzle grants one star. Good luck!

;; The jungle must be too overgrown and difficult to
;; navigate in vehicles or access from the air; the Elves'
;; expedition traditionally goes on foot. As your boats
;; approach land, the Elves begin taking inventory of their
;; supplies. One important consideration is food - in
;; particular, the number of Calories each Elf is carrying
;; (your puzzle input).

;; The Elves take turns writing down the number of Calories
;; contained by the various meals, snacks, rations, etc.
;; that they've brought with them, one item per line. Each
;; Elf separates their own inventory from the previous Elf's
;; inventory (if any) by a blank line.

;; For example, suppose the Elves finish writing their
;; items' Calories and end up with the following list:

;; 1000
;; 2000
;; 3000

;; 4000

;; 5000
;; 6000

;; 7000
;; 8000
;; 9000

;; 10000

;; This list represents the Calories of the food carried by
;; five Elves:

;;     The first Elf is carrying food with 1000, 2000, and
;;     3000 Calories, a total of 6000 Calories.

;;     The second Elf is carrying one food item with 4000
;;     Calories.

;;     The third Elf is carrying food with 5000 and 6000
;;     Calories, a total of 11000 Calories.

;;     The fourth Elf is carrying food with 7000, 8000, and
;;     9000 Calories, a total of 24000 Calories.

;;     The fifth Elf is carrying one food item with 10000
;;     Calories.

;; In case the Elves get hungry and need extra snacks, they
;; need to know which Elf to ask: they'd like to know how
;; many Calories are being carried by the Elf carrying the
;; most Calories. In the example above, this is 24000
;; (carried by the fourth Elf).

;; Find the Elf carrying the most Calories. How many total
;; Calories is that Elf carrying?

(defparameter test-data
 '(1000
   2000
   3000
   nil
   4000
   nil
   5000
   6000
   nil
   7000
   8000
   9000
   nil
   10000))

(defun day1/load-dataset (fname)
 (flet ((parse-line (line)
          (if (= 0 (length (string-trim '(#\space #\newline) line)))
              nil
              (parse-integer line))))
   (mapcar #'parse-line (load-lines fname))))

(defun day1/partition-dataset (lst)
 (reduce (lambda (&optional acc item)
           (if (null item)
               (cons nil acc)
               (cons
                (cons item (car acc))
                (cdr acc))))
         lst
         :initial-value '(nil)))

(defun day1/sum-calories (collected-counts)
 (mapcar (lambda (group)
           (apply #'+ group))
         collected-counts))

(defun day1/pick-biggest (summed-counts)
 (apply #'max summed-counts))

(defun day1/compute-part1 (counts)
 (day1/pick-biggest
  (day1/sum-calories
   (day1/partition-dataset counts))))

(defun day1/part1 ()
 (let ((lst (day1/load-dataset "day1.txt")))
   (day1/compute-part1 lst)))


;; --- Part Two ---

;; By the time you calculate the answer to the Elves'
;; question, they've already realized that the Elf carrying
;; the most Calories of food might eventually run out of
;; snacks.

;; To avoid this unacceptable situation, the Elves would
;; instead like to know the total Calories carried by the
;; top three Elves carrying the most Calories. That way,
;; even if one of those Elves runs out of snacks, they still
;; have two backups.

;; In the example above, the top three Elves are the fourth
;; Elf (with 24000 Calories), then the third Elf (with 11000
;; Calories), then the fifth Elf (with 10000 Calories). The
;; sum of the Calories carried by these three elves is
;; 45000.

;; Find the top three Elves carrying the most Calories. How
;; many Calories are those Elves carrying in total?

(defun day1/find-top-three (summed-counts)
 (let ((sorted-counts (sort summed-counts #'>)))
   (apply #'+ (subseq sorted-counts 0 3))))

(defun day1/compute-part2 (counts)
 (day1/find-top-three
  (day1/sum-calories
   (day1/partition-dataset counts))))

(defun day1/part2 ()
 (let ((lst (day1/load-dataset "day1.txt")))
   (day1/compute-part2 lst)))