Day 24: Finished part 2.
This commit is contained in:
parent
756b45c72d
commit
92bc60836d
123
src/24.rkt
123
src/24.rkt
|
@ -6,6 +6,10 @@
|
|||
(define input
|
||||
(problem-input 24))
|
||||
|
||||
;; coord : (list number number)
|
||||
;; grid : (vectorof (vectorof char))
|
||||
;; side : ('U | 'D | 'L | 'R)
|
||||
|
||||
(define width
|
||||
(string-length (car input)))
|
||||
|
||||
|
@ -21,19 +25,27 @@
|
|||
(define vector-grid
|
||||
(list->vector (map (∘ list->vector string->list) input)))
|
||||
|
||||
;; make-empty-grid : -> grid
|
||||
;; Create a (height × width) vector grid of #\.
|
||||
(define (make-empty-grid)
|
||||
(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)
|
||||
(match-let ([(list x y) coord])
|
||||
(or (= x 0) (= y 0)
|
||||
(= 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)
|
||||
(match-let ([(list x y) coord])
|
||||
(or (= x mid-x)
|
||||
(= y mid-y))))
|
||||
(or (and (= x mid-x) (<= (sub1 mid-y) y (add1 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)
|
||||
(match side
|
||||
['U (vector-count (∂ char=? #\#) (vector-ref grid 0))]
|
||||
|
@ -41,12 +53,16 @@
|
|||
['L (vector-count (∂ char=? #\#) (vector-map (∂ vector-first) 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)
|
||||
(!= 0 (+ (edge-bugs grid 'U)
|
||||
(edge-bugs grid 'D)
|
||||
(edge-bugs grid 'L)
|
||||
(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)
|
||||
(match side
|
||||
['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)]
|
||||
['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)
|
||||
(!= 0 (+ (middle-bug grid 'U)
|
||||
(middle-bug grid 'D)
|
||||
(middle-bug grid 'L)
|
||||
(middle-bug grid 'R))))
|
||||
|
||||
;; adjacent-bugs : grid -> coord -> number
|
||||
;; Count the number of bugs adjacent to the given tile
|
||||
(define (adjacent-bugs grid coord)
|
||||
(match-let* ([(list x y) coord]
|
||||
[U (vector-grid-ref* grid (list x (sub1 y)) #\.)]
|
||||
|
@ -68,14 +88,39 @@
|
|||
[R (vector-grid-ref* grid (list (add1 x) y) #\.)])
|
||||
(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]
|
||||
[grid (list-ref grids level)]
|
||||
[level-bugs (adjacent-bugs grid coord)])
|
||||
(cond
|
||||
[(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
|
||||
[(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)
|
||||
(+ level-bugs (middle-bug outer-grid 'L))]
|
||||
[(= y 0)
|
||||
|
@ -95,39 +140,49 @@
|
|||
(+ level-bugs (edge-bugs inner-grid 'L))]
|
||||
[(and (= y mid-y) (> x mid-x))
|
||||
(+ level-bugs (edge-bugs inner-grid 'R))]
|
||||
[else level-bugs]))]
|
||||
[else 0]))]
|
||||
[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)])
|
||||
(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 grid coord)]
|
||||
[next-char
|
||||
(match char
|
||||
[#\. #:when (<= 1 bug-count 2) #\#]
|
||||
[#\# #:when (!= 1 bug-count) #\.]
|
||||
[else char])])
|
||||
[bugs (bug-counter coord)]
|
||||
[next-char (step-tile char bugs)])
|
||||
(vector-set! (vector-ref new-grid y) x next-char)))
|
||||
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)
|
||||
(let* ([grids*
|
||||
(foldl
|
||||
(foldr
|
||||
(λ (level grid grids*)
|
||||
(let ([new-grid (make-empty-grid)])
|
||||
(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*)))
|
||||
(cons (step (∂ adjacent-bugs* level grids) grid) grids*))
|
||||
'() (range 0 (length grids)) grids)]
|
||||
[grids* (if (edges-bugs? (first grids*))
|
||||
(cons (make-empty-grid) grids*)
|
||||
|
@ -137,6 +192,10 @@
|
|||
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)
|
||||
(let ([list-grid (append* (map vector->list (vector->list grid)))])
|
||||
(sum (map (λ (char i)
|
||||
|
@ -144,15 +203,25 @@
|
|||
(expt 2 i) 0))
|
||||
list-grid (range 0 (length list-grid))))))
|
||||
|
||||
;; total-bugs : grids -> number
|
||||
(define (total-bugs grids)
|
||||
(sum (map (λ (grid)
|
||||
(sum (vector->list (vector-map (∂ vector-count (∂ char=? #\#)) grid))))
|
||||
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
|
||||
(let loop ([grid 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)
|
||||
(biodiversity next-grid)
|
||||
(loop next-grid (set-add set next-grid))))))
|
||||
|
@ -160,7 +229,7 @@
|
|||
(define part2
|
||||
(let loop ([count 0]
|
||||
[grids (list (make-empty-grid) vector-grid (make-empty-grid))])
|
||||
(if (= count 10)
|
||||
(if (= count 200)
|
||||
(total-bugs grids)
|
||||
(loop (add1 count) (step* grids)))))
|
||||
|
||||
|
|
Loading…
Reference in New Issue