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