diff options
| -rw-r--r-- | aoc/2021/day04/part2.neb | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/aoc/2021/day04/part2.neb b/aoc/2021/day04/part2.neb new file mode 100644 index 0000000..032d56d --- /dev/null +++ b/aoc/2021/day04/part2.neb @@ -0,0 +1,136 @@ +;; https://adventofcode.com/2021/day/4 + +; good candidate for loop-acc +(func get-all-boards (inp) + (def ret (list)) + (for-count (/ (length inp) 5) + (redef ret (append ret + (get-board (slice inp (+ (* 5 (- _idx_ 1)) 1) 5))))) + ret) + +(func get-board (inp) + (map get-row inp)) + +; each row is a string of items +; separated by (potentially) several spaces +; return a list of just the strings +(func get-row (row) + (filter + (lambda (x) + (not (eq? "" x))) + (split row " "))) + + +(func process-boards (inp) + (def raw-boards + (filter + (lambda (x) (not (eq? "" x))) + inp)) + (get-all-boards raw-boards)) + +(def lines + (map strip + (read-lines "input.txt"))) + +(def moves (split (first lines) ",")) ; moves is the first line +(def boards (process-boards (rest lines))) + +; this is a helper to work with zippers +; probably good to have in the stdlib +(func unzip (lst idx) + (list + (reverse + (slice lst 1 idx)) + (slice lst (+ 1 idx)))) + +(func has-won-row (row) + (reduce + ;(apply and row)) + (lambda (acc x) + (and acc + (int? x))) + row + #true)) + +(func has-won (board moves) + (def checked (check-board board moves)) + (def winner #false) + + ; check rows + ; no break statement means we have to check everything, + ; even after a winner is found + ; can also use a while, but that would require more redefs + (for-each checked + (if (has-won-row _item_) + (redef winner #true))) + + ; no break here means we check columns even if we won on rows + + ; check columns + (for-count 5 + (if (has-won-row + (map + (lambda (x) (first (slice x _idx_ 1))) + checked)) + (redef winner #true))) + + winner) + +; this could (should?) be interior to check-board +(func check-row (row moves) + (map + (lambda (x) + (if (in? x moves) + 0 ; use an integer + x)) + row)) + +(func check-board (board my-moves) + (map (lambda (x) (check-row x my-moves)) board)) + +; good candidate for stdlib +(func extend (lst1 lst2) + (reduce append lst2 lst1)) + + +; how do we better iterate through this list? +; called/next would be perfect for a zipper +(def cur (list moves (list))) ; init zip +(def winner #false) +(while (not winner) + (redef cur (unzip moves (- (length (first cur)) 1))) + + (def win-boards + (drop-while boards + (has-won _item_ (first cur)))) + + ; if win-boards is not empty, we found a winner! + (if (not (nil? win-boards)) + (block + (redef winner #true) + + ; benefit of zipper -- easy to get recent elements! + (def last-called (string->int (first (first (rest cur))))) + + ; result: a 25 element list of ints, where called numbers + ; are a 0, and uncalled numbers are their integer equivalents + (def winning-board + (map + (lambda (x) ; convert all strings to ints + (if (string? x) + (string->int x) + x)) + (reduce extend ; flatten the board from 5x5 to 1x25 + (check-board ; check the board, called numbers become 0 + (first win-boards) + (append (first cur) (first (first (rest cur))))) + (list)))) + + (print (->string winning-board)) + + (def sum-of-uncalled + (apply + winning-board)) + + (print (concat "sum of uncalled: " (->string sum-of-uncalled))) + (print (concat "last called: " (->string last-called))) + (print (concat "answer: " (->string (* sum-of-uncalled last-called))))))) |
