ADVENT OF CODE
       
       It's the holiday season! This year I'm trying my hand at a few
       puzzles. I'm also giving away lots of holiday cards that I design and
       printed myself (image below). All in all, it's a nice time of the year
       to strive for a bit more challenge in my intellectual pursuits, and
       happiness in my friendships.
       
 (IMG) iillustration-11.png
       
       
       Day 1
       ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       What I learned: getting the line count of a file and storing it in a
       variable doesn't work. It seems that a separate shell process a is
       spawned from this assignment, and so the code will carry on with its
       evaluation without waiting for the result. This was solved by moving
       the process that reads the value into the `for' loop.
       
       ,----
       | PWD=$PWD
       | INPUT="data.txt"
       | BUFFER_DIR="$PWD/buffer"
       | # LINES=$(wc -l $INPUT | grep -Po "[0-9]*$") This dosn't work
       | rm count.temp
       | wc -l $INPUT | grep -Po "[0-9]*" >> count.temp
       | 
       | rm -r $BUFFER_DIR
       | mkdir $BUFFER_DIR
       | 
       | INDEX=0
       | CALS_FOR_THIS_ELF=0
       | # Need to count lines inside this statement, otherwise it don't work!
       | for (( INDEX=0; INDEX < $(cat count.temp); INDEX++)); do
       |     ITEM_CALS=$(tail -n $INDEX $INPUT | head -n 1)
       |     IS_EMPTY_LINE=$(echo $ITEM_CALS | grep -Pc "^[[:space:]]*$")
       | 
       |     if [[ IS_EMPTY_LINE -gt 0 ]]; then
       |         touch "$BUFFER_DIR/$CALS_FOR_THIS_ELF"
       |         CALS_FOR_THIS_ELF=0
       |     fi
       | 
       |     CALS_FOR_THIS_ELF=$(( CALS_FOR_THIS_ELF + ITEM_CALS ))
       | done
       | 
       | cd $BUFFER_DIR
       | ls -1 * | sort -Vr | head -n 3
       | cd $PWD
       `----
       
 (IMG) iadvent-of-code-2.png
       
       
       Day 2
       ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       I wanted something easy for my second day doing the Advent of Code, so
       I settled on using Lua.
       
       ,----
       | local SCORE_TABLE = {
       |    X = 1,
       |    Y = 2,
       |    Z = 3
       | }
       | 
       | local WIN_TABLE = {
       |    A = "Z", -- Rock beats sissors
       |    B = "X", -- Paper beats rock
       |    C = "Y"  -- Sissors beats paper
       | }
       | 
       | local DRAW_TABLE = {
       |    A = "X",
       |    B = "Y",
       |    C = "Z",
       | }
       | 
       | function round_info(elf, me)
       |    local info
       |    info = {
       |       score = function()
       |          if info.drawer() then
       |             return 3 + SCORE_TABLE[me]
       |          elseif info.winner() then
       |             return 6 + SCORE_TABLE[me]
       |          else
       |             return SCORE_TABLE[me]
       |          end
       |       end,
       |       winner = function()
       |          return (WIN_TABLE[elf] ~= me)
       |       end,
       |       drawer = function()
       |          return (DRAW_TABLE[elf] == me)
       |       end,
       |    }
       |    return info
       | end
       | 
       | local read_file = function (path)
       |    local file = io.open(path, "rb")
       |    local rows = {}
       |    for col in io.lines(path) do
       |       local row = {}
       |       for x in col:gmatch("%w+") do
       |          table.insert(row, x)
       |       end
       |       table.insert(rows, round_info(row[1],row[2]))
       |    end
       |    file:close()
       |    return rows;
       | end
       | 
       | local my_score = 0
       | for _, row in ipairs(read_file("data.txt")) do
       |    my_score = my_score + row.score()
       | end
       | 
       | print(my_score)
       `----
       
       
       Day 3
       ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       This was a great way for me to apply the LISP I've been reading.
       
       I learned mostly about helpful string methods: splitting string,
       converting a string to a char. Basic string stuff that's always good
       to know in a language! I'm feeling more confident about EmacsLisp. The
       use of recursion is delightful. Much more enjoyable than using a
       loop. More powerful, too, I think, because the act of recursion,
       paired with an accumulator, lets one dataset be built while another is
       being deconstructed. Cool! I am a LISP gal now.
       
       ,----
       | (setq max-specpdl-size 32000)
       | (reduce #'+ (bagger (split-string (file-contents "./data.txt") "\n" t) (list)))
       | 
       | (defun bagger(bag accumulator)
       |   (if (null bag)
       |       accumulator
       |     (let* ((contents (car bag))
       |            (len (cdr (read-from-string contents)))
       |            (front-compartment (substring contents 0 (/ len 2)))
       |            (back-compartment (substring contents (/ len 2) len)))
       |       (setq accumulator
       |             (append (list (char-to-priority (car (find-matching-chars front-compartment back-compartment)))) accumulator))
       |       (bagger (cdr bag) accumulator))))
       | 
       | (defun char-to-priority (char)
       |   (let ((charnum (string-to-char char)))
       |     (if (s-uppercase-p char)
       |         (upper-char-to-priority charnum)
       |       (lower-char-to-priority charnum))))
       | 
       | (defun upper-char-to-priority (char)
       |   (- char 38))
       | 
       | (defun lower-char-to-priority (char)
       |   (- char 96))
       | 
       | (defun find-matching-chars (stringa stringb)
       |   (let ((matched-chars "")
       |         (stringa (split-string stringa "\\|[a-zA-Z]+" t))
       |         (stringb (split-string stringb "\\|[a-zA-Z]+" t)))
       |     (split-string (mapconcat (lambda (x) (char-in-string x stringb)) stringa "")
       |                   "\\|[a-zA-Z]+" t)))
       | 
       | (defun char-in-string (char list)
       |   (cond ((null list) nil)
       |         ((string= char (car list)) char)
       |         (t (char-in-string char (cdr list)))))
       | 
       | (defun file-contents (filename)
       |   (with-temp-buffer
       |     (insert-file-contents filename)
       |     (buffer-string)))
       `----
       
       
       Day 4
       ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       I really wanted to use regex for matching the data, but couldn't find
       great documentation around moving through matched strings. So I used
       the `split-string' function, as before, to parse each row of data into
       the numbers I needed.
       
       I also boxed myself into a corner by doing something weird early on:
       converting the range from its boundaries (e.g: ("1" "6")) into a list
       of each value therein (i.e.: (1 2 3 4 5 6)). Anyways, turns out it's
       possible to check if one list of values is entirely contained within
       another by comparing the length of the intersection with the length of
       both lists.
       
       ,----
       | (defun file-contents (filename)
       |   (with-temp-buffer
       |     (insert-file-contents filename)
       |     (buffer-string)))
       | 
       | (defun int-range-to-list(start end accumulator)
       |   ;; given a range of numbers, produce a list
       |   (if (> start end)
       |       accumulator
       |     (setq accumulator (append (list start) accumulator))
       |     (int-range-to-list (+ 1 start) end accumulator)
       |     ))
       | 
       | (defun unwrap-elves(elves)
       | (mapcar (lambda (elf-pair)
       |           (let* ((cleaning-zones-boundaries (mapcar (lambda (elf)
       |                                                       (split-string elf "-" t))
       |                                                     (split-string elf-pair "," t)))
       |                  (cleaning-zones-range (mapcar (lambda (zone)
       |                                                  (int-range-to-list (string-to-number (car zone)) (string-to-number (cadr zone)) (list)))
       |                                                cleaning-zones-boundaries))
       |                  (range-a (car cleaning-zones-range))
       |                  (range-b (cadr cleaning-zones-range))
       |                  (overlap (intersection range-a range-b))
       |                  ;; part 1
       |                  (is-contained (cond ((and (> (length overlap) 0)
       |                                            (or (= (length range-a) (length overlap))
       |                                                (= (length range-b) (length overlap)))) 1)
       |                                      (t 0)))
       |                  ;; part 2
       |                  (all-contained (cond ((> (length overlap) 0) 1)
       |                                       (t 0)))
       |                  )
       |             all-contained
       |             )) elves))
       | 
       | (reduce #'+(unwrap-elves (split-string (file-contents "./data.txt") "\n" t)))
       `----
       
       
       Day 5
       ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       Today was punishing. The input data included a visual representation
       of a table that needed to be parsed for the letters it contained. The
       letters needed to include column info, and this proved to be the most
       challenging information to extract. I had to create a function
       `translate' that took the location of the letter and translated that
       number to a column value. It was a real pain in the butt.
       
       This solution makes use of `with-temp-buffer' to store and parse the
       data. In yesterday's question I struggled with understanding how to
       use EmacsLisp's regexp faculties. Turns out, they're much easier to
       use inside of a buffer. Consequently, much of my code includes
       operations inside of a temp buffer.
       
       
       Sample Data
       ----------------------------------------------------------------------
       ,----
       |     [D]
       | [N] [C]
       | [Z] [M] [P]
       |  1   2   3
       `----
       
       
       Solution
       ----------------------------------------------------------------------
       ,----
       |   (setq stacks-count 9)
       | 
       | (defun find-pattern-in-file (filename pattern point-transform)
       |   (with-temp-buffer
       |     (insert-file-contents filename)
       |     (setq case-fold-search nil) ;; might need to toggle via M-x toggle-case-fold-search
       |     (goto-char (point-min)) ;; We need to move to start of buffer
       |     (let* ((accumulator (list)))
       |       (while (re-search-forward pattern nil t)
       |         (setq accumulator (append accumulator (list (list (match-string 0)
       |                                                     (funcall point-transform (- (match-beginning 0) (line-beginning-position))))))))
       |       accumulator)))
       | 
       | (defun translate(p)
       |   (let ((col nil)
       |         (start 0)
       |         (end 0))
       |     (dotimes (i 9)
       |       (setq start (* i 4)) ;; 4 because '[N] ' contains 4 characters, and is considered one column.
       |       (setq end (+ (* i 4) 4))
       |       (cond ((not (eq nil col))
       |              col)
       |             ((and (>= p start)
       |                   (<= p end))
       |               (setq col i))))
       |     col))
       | 
       | (progn
       |   (let* ((boxes (find-pattern-in-file "./data.txt" "[A-Z]" #'(lambda(p)
       |                                                                (+ (translate p) 1))))
       |          (movements-ungrouped (nthcdr 9 (find-pattern-in-file "./data.txt" "[0-9]+" #'(lambda(p) nil))))
       |          (movements-grouped (list)))
       |     (cl-do* ((i 0 (+ i 3)))
       |         ((>= i (length movements-ungrouped)))
       |           (let ((count (car (nth i movements-ungrouped)))
       |                 (from (car (nth (+ i 1) movements-ungrouped)))
       |                 (to (car (nth (+ i 2) movements-ungrouped))))
       |             (setq movements-grouped (append movements-grouped (list (list (string-to-number count) (string-to-number from) (string-to-number to)))))))
       |     (setq rearranged-boxes (with-temp-buffer
       |       (dotimes (i (+ 1 stacks-count))
       |         (insert "\n"))
       |       (cl-do* ((i (- (length boxes) 1) (- i 1)))
       | 
       |           j((< i 0))
       |         (let* ((box (car (nth i boxes)))
       |                (column (cadr (nth i boxes))))
       |           (goto-line column)
       |           (insert box)))
       |       (cl-do ((i 0 (+ i 1))) ((= i (length movements-grouped)))
       |         (let* ((m (nth i movements-grouped))
       |                (count (nth 0 m))
       |                (from (- (nth 1 m) 0))
       |                (to (- (nth 2 m) 0)))
       |           (goto-line from)
       |           (line-beginning-position)
       |           (let ((box (buffer-substring (point) (+ (point) count))))
       |             (goto-line to)
       |             (insert box)) ;; (insert (reverse box)) ;; for part 1 use reverse
       |           (goto-line from)
       |           (line-beginning-position)
       |           (delete-region (point) (+ (point) count))))
       |       (dotimes (i (+ 1 stacks-count))
       |         (goto-line i)
       |         (delete-region (+ (point) 1) (line-end-position)))
       |       (replace-string-in-region "\n" "")
       |       (buffer-string)))
       |     (mapconcat (lambda(s) s) (split-string rearranged-boxes "\n" t) "")))
       `----
       
       
       Day 6
       ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       I completed today's problem in about 30 minutes. I made use of some
       new-to-me sequence functions like `seq-take' and `seq-uniq' to grab
       and compare parts of lists.
       
       ,----
       | (defun file-contents (filename)
       |   (with-temp-buffer
       |     (insert-file-contents filename)
       |     (buffer-string)))
       | 
       | ;; Part 1
       | (let ((message (split-string (file-contents "data.txt") "" t))
       |       (start nil))
       |   (dotimes (i (- (length message) 4))
       |     (let* ((maybe-header (seq-take (nthcdr i message) 4)))
       |       (cond ((and (eq nil start)
       |                   (eq 4 (seq-length (seq-uniq maybe-header))))
       |              (setq start (+ 4 i))))))
       |   (print start))
       | 
       | ;; Part 2
       | (let ((message (split-string (file-contents "data.txt") "" t))
       |       (start nil)
       |       (m-start nil))
       |   (dotimes (i (- (length message) 4))
       |     (let* ((maybe-header (seq-take (nthcdr i message) 4)))
       |       (cond ((and (eq nil start)
       |                   (eq 4 (seq-length (seq-uniq maybe-header))))
       |              (setq start (+ 4 i)))
       |             ((and (numberp start) (eq nil m-start))
       |              (let ((maybe-message (seq-take (nthcdr i message) 14)))
       |                (cond ((eq 14 (seq-length (seq-uniq maybe-message)))
       |                       (setq m-start (+ 14 i))
       |                       ))
       |                )))))
       |   (print m-start))
       `----