aboutsummaryrefslogtreecommitdiff
path: root/aoc/2021/day04
diff options
context:
space:
mode:
Diffstat (limited to 'aoc/2021/day04')
-rw-r--r--aoc/2021/day04/part2.neb136
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)))))))