Advent-of-Code/day5.lisp

211 lines
6.9 KiB
Common Lisp

;;; wow, now this is complicated.
;; first read in until the empty line
;; and somehow construct internal representation
;; i want Stack data structure. cool. where is that?
;; use list with PUSH and REMOVE ?
(defparameter *some-stack* (list 1 2 3))
(push 1 *some-stack*)
(pop *some-stack*)
*some-stack*
;; well, yea. list is a stack. ok
;; so I need to have addressable from 1 to n
;; lists
;; and after i read them - reverse
;; i guess i could read in the string. and calculate index of the list
(defparameter *test-string-0* " [D]")
(defparameter *test-string* "[Z] [M] [P]")
;; [Z] [M] [P]
;; 1 2 3
;; 01234567890
;; so. first letter is at 1 (after 0) and next letter is at i+4
;; let's write function that translates that string into '((1 Z) (2 M) (3 P))
;; and would translate *test-string* into '((2 D))
;; i guess i could just iterate by i+4 from i=1 until the end of the string, and that's it
(defun parse-crate-string (str)
(do
((str-index 1 (+ 4 str-index))
(index 1 (1+ index))
(accum (list)))
((> str-index (1- (length str))) accum)
(let ((box-label (aref str str-index)))
(if (not (eq box-label #\ ))
(setq accum (push (list index (aref str str-index)) accum))))))
(defparameter *test-line-parsed* (parse-crate-string *test-string*))
(do
( (index 1 (+ 4 index)))
((> index 15) "hello")
(format t "lala ~D~%" index)
)
(do ((temp-one 1 (1+ temp-one))
(temp-two 0 (1- temp-two)))
((> (- temp-one temp-two) 5) temp-one))
(aref "hello-world" 1)
(aref " hello-world" 0)
(aref "hello-world" 11)
(length "hello-world")
;;; now. for each string I want to take index, take list on that index and put the label on top
;; now, i want "common lisp MAP", and ideally with default
(defparameter *test-table* (make-hash-table))
(gethash 1 *test-table*)
(setf (gethash 2 *test-table*) "hello")
(gethash 2 *test-table*)
(gethash 3 *test-table* (list)) ; so anyway by default it returns NIL
*test-line-parsed*
(mapcar (lambda (box-descr)
(let* ((index (first box-descr))
(label (second box-descr))
(cur-list (gethash index *test-table*)))
(setf (gethash index *test-table*) (push label cur-list))))
*test-line-parsed*)
*test-table*
(defun apply-parsed-line-to-lists (parsed-line table)
(mapcar (lambda (box-descr)
(let* ((index (first box-descr))
(label (second box-descr))
(cur-list (gethash index table)))
(setf (gethash index table) (push label cur-list))))
parsed-line))
(apply-parsed-line-to-lists *test-line-parsed* *test-table*)
(defparameter *full-test-boxes* " [D]
[N] [C]
[Z] [M] [P] ")
(require 'cl-ppcre)
(cl-ppcre:split (cl-ppcre:create-scanner :end-anchor) *full-test-boxes*)
(cl-ppcre:split (cl-ppcre:create-scanner "\n") *full-test-boxes*)
;; all of this didn't fucking work. can't split the line by the newline, what a joke
;;; god. i'm ready to create these lists manually.
;;; ugh.
(defparameter *all-test-boxes-lines* (list
" [D]"
"[N] [C]"
"[Z] [M] [P]"))
(defparameter *all-input-boxes-lines* (list
" [C] [N] [R]"
"[J] [T] [H] [P] [L]"
"[F] [S] [T] [B] [M] [D]"
"[C] [L] [J] [Z] [S] [L] [B]"
"[N] [Q] [G] [J] [J] [F] [F] [R]"
"[D] [V] [B] [L] [B] [Q] [D] [M] [T]"
"[B] [Z] [Z] [T] [V] [S] [V] [S] [D]"
"[W] [P] [P] [D] [G] [P] [B] [P] [V]"))
(defun get-boxes-lists-hashtable (boxes-lines)
(let ((hash-table (make-hash-table))
(parsed-lines (mapcar #'parse-crate-string boxes-lines)))
(mapcar (lambda (parsed-line) (apply-parsed-line-to-lists parsed-line hash-table)) parsed-lines)
(maphash (lambda (key list) (setf (gethash key hash-table) (reverse list))) hash-table)
hash-table))
(defparameter *test-boxes* (get-boxes-lists-hashtable *all-test-boxes-lines*))
(gethash 2 (get-boxes-lists-hashtable *all-test-boxes-lines*))
;; yay. ok. good enough.
;; now i need a function that would modify that hash table for each line
;; "move 1 from 2 to 1"
;; if i just to intern, would i get numbers?
(cddr (mapcar #'intern (cl-ppcre:split " " "move 1 from 2 to 1")))
;; nope that would be a symbol
;; allright, let's just emit list of numbers
(let ((string "move 1 from 2 to 3"))
(do*
((words (cl-ppcre:split " " string) (cddr words))
(number (parse-integer (second words)) (parse-integer (second words)))
(nums (list number) (push number nums)))
((not (cddr words)) (reverse nums))))
(defun command-string-to-indices (str)
(do*
((words (cl-ppcre:split " " str) (cddr words))
(number (parse-integer (second words)) (parse-integer (second words)))
(nums (list number) (push number nums)))
((not (cddr words)) (reverse nums))))
(command-string-to-indices "move 1 from 2 to 1")
(defun run-command (hashtable amount from to)
(loop
for i from 1 to amount
do (push (pop (gethash from hashtable)) (gethash to hashtable))))
(run-command *test-boxes* 3 2 1)
(gethash 2 *test-boxes*)
(gethash 1 *test-boxes*)
(with-open-file (in "day5-test-input.txt")
(loop
for line = (read-line in nil nil)
while line
do (apply #'run-command (cons *test-boxes* (command-string-to-indices line)))))
;; https://riptutorial.com/common-lisp/example/4463/looping-over-hash-tables
(print (coerce (loop
for v from 1 to 3
collect (first (gethash v *test-boxes*))) 'string))
(defparameter *day-5-boxes* (get-boxes-lists-hashtable *all-input-boxes-lines*))
(gethash 1 *day-5-boxes*)
(with-open-file (in "day5-input.txt")
(loop
for line = (read-line in nil nil)
while line
do (apply #'run-command (cons *day-5-boxes* (command-string-to-indices line)))))
;; printing things as a string
(print (coerce (loop
for v from 1 to (hash-table-count *day-5-boxes*)
collect (first (gethash v *day-5-boxes*))) 'string))
(getf :count *day-5-boxes*)
(hash-table-count *day-5-boxes*)
;; oh, wow. now i need to implement moving "with retaining order".
;; cool
(defun run-command-9001 (hashtable amount from to)
(let ((moving-part (subseq (gethash from hashtable) 0 amount))
(remaining-part (subseq (gethash from hashtable) amount)))
(setf (gethash from hashtable) remaining-part)
(setf (gethash to hashtable) (concatenate 'list moving-part (gethash to hashtable)))))
;; so, taking is subseq, but wound need to drop these elements
;; but how to prepend list to another list? concat?
(concatenate 'list (list 1 2 3) (list 5 6 7))
(subseq (list 1 2 3 4 5) 0 2) ; so "until end"
(subseq (list 1 2 3 4 5) 2)
;; nullify hash with boxes before running
(with-open-file (in "day5-test-input.txt")
(loop
for line = (read-line in nil nil)
while line
do (apply #'run-command-9001 (cons *test-boxes* (command-string-to-indices line)))))
(with-open-file (in "day5-input.txt")
(loop
for line = (read-line in nil nil)
while line
do (apply #'run-command-9001 (cons *day-5-boxes* (command-string-to-indices line)))))