aboutsummaryrefslogtreecommitdiff
path: root/aoc/2021/day04/part2.neb
blob: 032d56d75f557e95be97fe846535ade0845ce1a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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)))))))