Day 24: Finished part 2.

This commit is contained in:
Jonathan Chan 2019-12-24 13:10:07 -08:00
parent 756b45c72d
commit 92bc60836d
1 changed files with 96 additions and 27 deletions

View File

@ -6,6 +6,10 @@
(define input (define input
(problem-input 24)) (problem-input 24))
;; coord : (list number number)
;; grid : (vectorof (vectorof char))
;; side : ('U | 'D | 'L | 'R)
(define width (define width
(string-length (car input))) (string-length (car input)))
@ -21,19 +25,27 @@
(define vector-grid (define vector-grid
(list->vector (map ( list->vector string->list) input))) (list->vector (map ( list->vector string->list) input)))
;; make-empty-grid : -> grid
;; Create a (height × width) vector grid of #\.
(define (make-empty-grid) (define (make-empty-grid)
(build-vector height (λ (_) (make-vector width #\.)))) (build-vector height (λ (_) (make-vector width #\.))))
;; outer-edge? : coord -> boolean
;; Return whether the given coordinate is on the edge of the grid
(define (outer-edge? coord) (define (outer-edge? coord)
(match-let ([(list x y) coord]) (match-let ([(list x y) coord])
(or (= x 0) (= y 0) (or (= x 0) (= y 0)
(= x (sub1 width)) (= y (sub1 height))))) (= x (sub1 width)) (= y (sub1 height)))))
;; inner-edge? : coord -> boolean
;; Return whether the given coordinate is adjacent to the centre tile
(define (inner-edge? coord) (define (inner-edge? coord)
(match-let ([(list x y) coord]) (match-let ([(list x y) coord])
(or (= x mid-x) (or (and (= x mid-x) (<= (sub1 mid-y) y (add1 mid-y)))
(= y mid-y)))) (and (= y mid-y) (<= (sub1 mid-x) x (add1 mid-x))))))
;; edge-bugs : grid -> side -> number
;; Return the number of bugs on the top/bottom/left/right edge
(define (edge-bugs grid side) (define (edge-bugs grid side)
(match side (match side
['U (vector-count ( char=? #\#) (vector-ref grid 0))] ['U (vector-count ( char=? #\#) (vector-ref grid 0))]
@ -41,12 +53,16 @@
['L (vector-count ( char=? #\#) (vector-map ( vector-first) grid))] ['L (vector-count ( char=? #\#) (vector-map ( vector-first) grid))]
['R (vector-count ( char=? #\#) (vector-map ( vector-last) grid))])) ['R (vector-count ( char=? #\#) (vector-map ( vector-last) grid))]))
;; edges-bugs? : grid -> boolean
;; Return whether any of the edges have bugs
(define (edges-bugs? grid) (define (edges-bugs? grid)
(!= 0 (+ (edge-bugs grid 'U) (!= 0 (+ (edge-bugs grid 'U)
(edge-bugs grid 'D) (edge-bugs grid 'D)
(edge-bugs grid 'L) (edge-bugs grid 'L)
(edge-bugs grid 'R)))) (edge-bugs grid 'R))))
;; middle-bug : grid -> side -> number
;; Return the number of bugs on the tile above/below/leftof/rightof the centre tile
(define (middle-bug grid side) (define (middle-bug grid side)
(match side (match side
['U (if (char=? #\# (vector-grid-ref* grid (list mid-x (sub1 mid-y)) #\.)) 1 0)] ['U (if (char=? #\# (vector-grid-ref* grid (list mid-x (sub1 mid-y)) #\.)) 1 0)]
@ -54,12 +70,16 @@
['L (if (char=? #\# (vector-grid-ref* grid (list (sub1 mid-x) mid-y) #\.)) 1 0)] ['L (if (char=? #\# (vector-grid-ref* grid (list (sub1 mid-x) mid-y) #\.)) 1 0)]
['R (if (char=? #\# (vector-grid-ref* grid (list (add1 mid-x) mid-y) #\.)) 1 0)])) ['R (if (char=? #\# (vector-grid-ref* grid (list (add1 mid-x) mid-y) #\.)) 1 0)]))
;; middle-bugs? : grid -> boolean
;; Return whether any of the tiles adjacent to the centre tile has bugs
(define (middle-bugs? grid) (define (middle-bugs? grid)
(!= 0 (+ (middle-bug grid 'U) (!= 0 (+ (middle-bug grid 'U)
(middle-bug grid 'D) (middle-bug grid 'D)
(middle-bug grid 'L) (middle-bug grid 'L)
(middle-bug grid 'R)))) (middle-bug grid 'R))))
;; adjacent-bugs : grid -> coord -> number
;; Count the number of bugs adjacent to the given tile
(define (adjacent-bugs grid coord) (define (adjacent-bugs grid coord)
(match-let* ([(list x y) coord] (match-let* ([(list x y) coord]
[U (vector-grid-ref* grid (list x (sub1 y)) #\.)] [U (vector-grid-ref* grid (list x (sub1 y)) #\.)]
@ -68,14 +88,39 @@
[R (vector-grid-ref* grid (list (add1 x) y) #\.)]) [R (vector-grid-ref* grid (list (add1 x) y) #\.)])
(count ( char=? #\#) (list U D L R)))) (count ( char=? #\#) (list U D L R))))
(define (adjacent-bugs* grids level coord) ;; adjacent-bugs* : level -> grid -> coord
;; Count the number of bugs adjacent to the given tile
;; on the grid on the given level
;; However! If the coordinate is on the edge, instead of treating the other side
;; like empty space, we look at the corresponding tile adjacent to the centre tile
;; on the previous level's grid, as if the current grid were embedded
;; Furthermore! If the coordinate is adjacent to the centre tile, instead of looking
;; at the centre tile, we look at the corresponding edge of the next level's grid,
;; as if that grid were embedded in the current one.
;; By "corresponding", it means, for instance, if the current coordinate were on
;; the top edge, we need to look at the tile above the centre tile in the previous grid;
;; if the current coordinate were above the centre tile, we need to look at the top edge
;; of the next grid.
(define (adjacent-bugs* level grids coord)
(match-let* ([(list x y) coord] (match-let* ([(list x y) coord]
[grid (list-ref grids level)] [grid (list-ref grids level)]
[level-bugs (adjacent-bugs grid coord)]) [level-bugs (adjacent-bugs grid coord)])
(cond (cond
[(and (outer-edge? coord) (> level 0)) [(and (outer-edge? coord) (> level 0))
(let* ([outer-grid (list-ref grids (sub1 level))]) (let* ([outer-grid (list-ref grids (sub1 level))]
[U (middle-bug outer-grid 'U)]
[D (middle-bug outer-grid 'D)]
[L (middle-bug outer-grid 'L)]
[R (middle-bug outer-grid 'R)])
(cond (cond
[(and (= x 0) (= y 0))
(+ level-bugs L U)]
[(and (= x 0) (= y (sub1 height)))
(+ level-bugs L D)]
[(and (= x (sub1 width)) (= y 0))
(+ level-bugs R U)]
[(and (= x (sub1 width)) (= y (sub1 height)))
(+ level-bugs R D)]
[(= x 0) [(= x 0)
(+ level-bugs (middle-bug outer-grid 'L))] (+ level-bugs (middle-bug outer-grid 'L))]
[(= y 0) [(= y 0)
@ -95,39 +140,49 @@
(+ level-bugs (edge-bugs inner-grid 'L))] (+ level-bugs (edge-bugs inner-grid 'L))]
[(and (= y mid-y) (> x mid-x)) [(and (= y mid-y) (> x mid-x))
(+ level-bugs (edge-bugs inner-grid 'R))] (+ level-bugs (edge-bugs inner-grid 'R))]
[else level-bugs]))] [else 0]))]
[else level-bugs]))) [else level-bugs])))
(define (step grid) ;; step-tile : char -> number -> char
;; A bug dies unless it has exactly one bug neighbour
;; A bug spawns if a space has one or two bug neighbours
(define (step-tile char bugs)
(match char
[#\. #:when (<= 1 bugs 2) #\#]
[#\# #:when (!= 1 bugs) #\.]
[else char]))
;; step : (coord -> number) -> grid -> grid
;; Return the grid after one minute of bugs dying and spawning
;; The bug-counter takes a coordinate and returns the number of
;; bugs adjacent to that coordinate
;; For part 1, this is simply adjacent-bugs
;; For part 2, we need to take into account previous and next levels,
;; and so should be adjacent-bugs* wrt to the current level and grids
(define (step bug-counter grid)
(let ([new-grid (make-empty-grid)]) (let ([new-grid (make-empty-grid)])
(for ([coord (cartesian-product (range 0 width) (range 0 height))]) (for ([coord (cartesian-product (range 0 width) (range 0 height))])
(match-let* ([(list x y) coord] (match-let* ([(list x y) coord]
[char (vector-ref (vector-ref grid y) x)] [char (vector-ref (vector-ref grid y) x)]
[bug-count (adjacent-bugs grid coord)] [bugs (bug-counter coord)]
[next-char [next-char (step-tile char bugs)])
(match char
[#\. #:when (<= 1 bug-count 2) #\#]
[#\# #:when (!= 1 bug-count) #\.]
[else char])])
(vector-set! (vector-ref new-grid y) x next-char))) (vector-set! (vector-ref new-grid y) x next-char)))
new-grid)) new-grid))
;; step* : grids -> grids
;; Return the grids after one minute of bugs dying and spawning
;; The bugs on the edge and near the centre of a given level
;; will depend on the bugs near the centre and on the edge
;; of the previous and next levels
;; If the first (outermost) grid has bugs on the edge, their fate
;; will depend on a yet outer grid, so we prepend an empty grid
;; If the last (innermost) grid has bugs near the centre, their fate
;; will depend on a yet inner grid, so we postpend an empty grid
(define (step* grids) (define (step* grids)
(let* ([grids* (let* ([grids*
(foldl (foldr
(λ (level grid grids*) (λ (level grid grids*)
(let ([new-grid (make-empty-grid)]) (cons (step ( adjacent-bugs* level grids) grid) grids*))
(for ([coord (cartesian-product (range 0 width) (range 0 height))])
(match-let* ([(list x y) coord]
[char (vector-ref (vector-ref grid y) x)]
[bug-count (adjacent-bugs* grids level coord)]
[next-char
(match char
[#\. #:when (<= 1 bug-count 2) #\#]
[#\# #:when (!= 1 bug-count) #\.]
[else char])])
(vector-set! (vector-ref new-grid y) x next-char)))
(cons new-grid grids*)))
'() (range 0 (length grids)) grids)] '() (range 0 (length grids)) grids)]
[grids* (if (edges-bugs? (first grids*)) [grids* (if (edges-bugs? (first grids*))
(cons (make-empty-grid) grids*) (cons (make-empty-grid) grids*)
@ -137,6 +192,10 @@
grids*)]) grids*)])
grids*)) grids*))
;; biodiversity : grid -> number
;; From left to right and top to bottom, the biodiversity of the grid
;; is 2 to the power of the index of the tile if they were lined up
;; in order starting from an index of 0
(define (biodiversity grid) (define (biodiversity grid)
(let ([list-grid (append* (map vector->list (vector->list grid)))]) (let ([list-grid (append* (map vector->list (vector->list grid)))])
(sum (map (λ (char i) (sum (map (λ (char i)
@ -144,15 +203,25 @@
(expt 2 i) 0)) (expt 2 i) 0))
list-grid (range 0 (length list-grid)))))) list-grid (range 0 (length list-grid))))))
;; total-bugs : grids -> number
(define (total-bugs grids) (define (total-bugs grids)
(sum (map (λ (grid) (sum (map (λ (grid)
(sum (vector->list (vector-map ( vector-count ( char=? #\#)) grid)))) (sum (vector->list (vector-map ( vector-count ( char=? #\#)) grid))))
grids))) grids)))
;; show-grids : grids -> void
;; Print each grid from outermost to innermost separated by line breaks
;; for... debugging
(define (show-grids grids)
(for ([grid grids])
(for ([row grid])
(displayln (list->string (vector->list row))))
(displayln "")))
(define part1 (define part1
(let loop ([grid vector-grid] (let loop ([grid vector-grid]
[set (set vector-grid)]) [set (set vector-grid)])
(let ([next-grid (step grid)]) (let ([next-grid (step ( adjacent-bugs grid) grid)])
(if (set-member? set next-grid) (if (set-member? set next-grid)
(biodiversity next-grid) (biodiversity next-grid)
(loop next-grid (set-add set next-grid)))))) (loop next-grid (set-add set next-grid))))))
@ -160,7 +229,7 @@
(define part2 (define part2
(let loop ([count 0] (let loop ([count 0]
[grids (list (make-empty-grid) vector-grid (make-empty-grid))]) [grids (list (make-empty-grid) vector-grid (make-empty-grid))])
(if (= count 10) (if (= count 200)
(total-bugs grids) (total-bugs grids)
(loop (add1 count) (step* grids))))) (loop (add1 count) (step* grids)))))