aboutsummaryrefslogtreecommitdiff
path: root/racket/aoc2023/day-22/day-22.rkt
blob: 53668c0f8c933cebbb092a5fc990ac7930f391ed (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
#lang racket

(require advent-of-code
         threading
         data/applicative
         data/monad
         megaparsack
         megaparsack/text
         racket/hash)

(struct posn (x y z))
(struct block (n from to))

(define input (fetch-aoc-input (find-session) 2023 22 #:cache #true))

(define coordinate/p
  (do [coords <- (many/p integer/p #:sep (char/p #\,) #:min 3 #:max 3)]
    (pure (apply posn coords))))

(define block/p
  (do [from <- coordinate/p]
    (char/p #\~)
    [to <- coordinate/p]
    (pure (cons from to))))

(define starting-blocks
  (~> (for/list ([line (in-list (string-split input "\n"))]
                 [n (in-naturals)])
        (match-define (cons from to) (parse-result! (parse-string block/p line)))
        (block n from to))
      (sort < #:key (λ~> block-from posn-z))))

(define (all-in-cross-section-at-level b z)
  (match-define (block _ (posn x1 y1 _) (posn x2 y2 _)) b)
  (for*/list ([x (in-inclusive-range x1 x2)]
              [y (in-inclusive-range y1 y2)])
    (posn x y z)))

(define (place-block-at-level b h dz)
  (match-define (block n (posn x1 y1 z1) (posn x2 y2 z2)) b)
  (define now-occupied
    (for*/hash ([x (in-inclusive-range x1 x2)]
                [y (in-inclusive-range y1 y2)]
                [z (in-inclusive-range dz (+ dz (- z2 z1)))])
      (values (posn x y z) n)))
  (hash-union h now-occupied))

(define (find-lowest-level b h [z (~> b block-from posn-z)])
  (cond
    [(= z 0)
     (place-block-at-level b h 1)]
    [(findf (curry hash-has-key? h) (all-in-cross-section-at-level b z))
     (place-block-at-level b h (add1 z))]
    [else
     (find-lowest-level b h (sub1 z))]))

(define blocks-in-space (foldl find-lowest-level (hash) starting-blocks))
(define block-positions
  (for/fold ([placed-blocks (hash)])
            ([(p n) (in-hash blocks-in-space)])
    (hash-update placed-blocks n (curryr set-add p) (set))))

(define (down-one p)
  (match p
    [(posn x y z) (posn x y (sub1 z))]))

(define supporting-blocks
  (for/hash ([(n-id n-posns) (in-hash block-positions)])
    (values n-id
            (for*/set ([(m-id m-posns) (in-hash block-positions)]
                       #:unless (= n-id m-id)
                       [m-posn (in-set m-posns)]
                       #:when (set-member? n-posns (down-one m-posn)))
              m-id))))

(define supported-by-blocks
  (for/hash ([n-id (in-hash-keys supporting-blocks)])
    (define supporters
      (~> (for*/set
              ([(m-id m-supporting) (in-hash supporting-blocks)]
               #:unless (= n-id m-id)
               #:when (set-member? m-supporting n-id))
            m-id)
          ((λ (s) (if (set-empty? s) (set 'ground) s)))))
    (values n-id supporters)))

;; part 1
(define vulnerable-blocks
  (for/list ([n-id (in-range (length starting-blocks))]
             #:when (for/or ([m-supported-by (in-hash-values supported-by-blocks)])
                      (set-empty? (set-remove m-supported-by n-id))))
    n-id))
(- (length starting-blocks) (length vulnerable-blocks))

;; part 2
(for/sum ([n (in-list vulnerable-blocks)])
  (for/fold ([fallen (set n)]
             [bricks (set n)]
             #:result (~> fallen set-count sub1))
            ([_ (in-naturals)])
    #:break (set-empty? bricks)
    (define bricks-above
      (for*/set
          ([brick (in-set bricks)]
           [supporting (in-set (hash-ref supporting-blocks brick))]
           #:when (for/and ([supports (in-set (hash-ref supported-by-blocks supporting))])
                    (set-member? fallen supports)))
        supporting))
    (values (set-union fallen bricks-above) bricks-above)))