URI:
       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))
       `----