123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- (import
- (except (rnrs base)
- let-values
- map
- error
- vector-map)
- (only (guile)
- lambda* λ
- simple-format
- current-output-port)
- (fileio)
- (ice-9 pretty-print)
- (ice-9 peg)
- (prefix (peg-tree-utils) peg-tree:)
- ;; (ice-9 format)
- (srfi srfi-1)
- (pipeline)
- ;; (debug)
- (list-helpers)
- (parallelism)
- ;; (math)
- (logic)
- (srfi srfi-9 gnu)
- ;; let-values
- (srfi srfi-11)
- ;; purely functional data structures
- (pfds sets)
- (timing))
- (define input-filename "input")
- ;; QLIST -- Merely adding a Q to avoid any name clashes, either actual
- ;; ones or in my mind.
- (define-peg-pattern COMMA none ",")
- (define-peg-pattern ARROW none "->")
- (define-peg-pattern SPACE none " ")
- (define-peg-pattern SEPARATOR none (and SPACE ARROW SPACE))
- (define-peg-pattern NUMBER body (+ (range #\0 #\9)))
- (define-peg-pattern COORD all NUMBER)
- (define-peg-pattern COORDS all (and COORD COMMA COORD))
- (define-peg-pattern COORDS-LIST all (* (and COORDS (? SEPARATOR))))
- (define-immutable-record-type <pos>
- (make-pos y x)
- coord?
- (x position-x set-position-x)
- (y position-y set-position-y))
- (define-immutable-record-type <segment>
- (make-segment start end)
- segment?
- (start segment-start set-segment-start)
- (end segment-end set-segment-end))
- (define-immutable-record-type <rock-path>
- (make-rock-path rock-segments)
- rock-path?
- (rock-segments rock-path-segments set-rock-path-segments))
- (define extract-parsed-poss
- (λ (parsed-coords-lists)
- (parallel-map (λ (line _ind)
- (peg:tree (match-pattern COORDS-LIST line)))
- parsed-coords-lists)))
- (define parsed-pos->pos
- (λ (parsed-pos)
- (make-pos (-> parsed-pos third second string->number)
- (-> parsed-pos second second string->number))))
- (define parsed-poss->poss
- (λ (parsed-poss)
- (map parsed-pos->pos (drop parsed-poss 1))))
- (define poss->segments
- (λ (coords)
- (let iter ([start° (car coords)]
- [segments° '()]
- [coords° (cdr coords)])
- (cond
- [(null? coords°) segments°]
- [else
- (iter (car coords°)
- (cons (make-segment start° (car coords°))
- segments°)
- (cdr coords°))]))))
- (define all-positions
- (-> (get-lines-from-file input-filename)
- extract-parsed-poss
- (parallel-map (λ (parsed-poss _ind) (parsed-poss->poss parsed-poss))
- #|arg|#)))
- (define rock-paths
- (-> all-positions
- ;; ((λ (thing) (pretty-peek thing #:width 20)))
- (parallel-map (λ (poss _ind) (poss->segments poss))
- #|arg|#)
- (parallel-map (λ (segmentss _ind) (make-rock-path segmentss))
- #|arg|#)
- ;; ((λ (thing) (pretty-peek thing #:width 40)))
- ))
- (define in-inclusive-range?
- (λ (num1 start end)
- (or (and (>= num1 start) (<= num1 end))
- (and (>= num1 end) (<= num1 start)))))
- (assert (in-inclusive-range? 75 50 100))
- (assert (in-inclusive-range? 75 100 50))
- (assert (not (in-inclusive-range? 3 100 50)))
- (assert (not (in-inclusive-range? 103 100 50)))
- (assert (not (in-inclusive-range? 3 50 100)))
- (assert (not (in-inclusive-range? 103 50 100)))
- (define position-on-segment?
- (λ (position segment)
- (let ([pos-x (position-x position)]
- [pos-y (position-y position)]
- [seg-start-pos-x (position-x (segment-start segment))]
- [seg-start-pos-y (position-y (segment-start segment))]
- [seg-end-pos-x (position-x (segment-end segment))]
- [seg-end-pos-y (position-y (segment-end segment))])
- (cond
- ;; vertical segment case
- [(= pos-x seg-start-pos-x seg-end-pos-x)
- (in-inclusive-range? pos-y seg-start-pos-y seg-end-pos-y)]
- ;; horizontal segment case
- [(= pos-y seg-start-pos-y seg-end-pos-y)
- (in-inclusive-range? pos-x seg-start-pos-x seg-end-pos-x)]
- [else
- #f]))))
- (assert (position-on-segment?
- (make-pos 28 40)
- (make-segment (make-pos 28 35) (make-pos 28 40))))
- (assert (not
- (position-on-segment?
- (make-pos 30 40)
- (make-segment (make-pos 28 35) (make-pos 28 40)))))
- (define position-on-rock-path?
- (λ (position rock-path)
- (any? (map (λ (segment) (position-on-segment? position segment))
- (rock-path-segments rock-path)))))
- (assert
- (position-on-rock-path? (make-pos 30 40)
- (make-rock-path
- (list (make-segment (make-pos 28 35) (make-pos 28 40))
- (make-segment (make-pos 28 40) (make-pos 30 40))))))
- (assert
- (not
- (position-on-rock-path? (make-pos 30 40)
- (make-rock-path
- (list (make-segment (make-pos 28 35) (make-pos 28 40))
- (make-segment (make-pos 28 40) (make-pos 29 40)))))))
- (define make-empty-set
- (λ ()
- (make-set
- (λ (p1 p2)
- (or (< (position-x p1) (position-x p2))
- (and (= (position-x p1) (position-x p2))
- (< (position-y p1) (position-y p2))))))))
- (define move-down
- (λ (pos)
- (set-position-y pos (+ (position-y pos) 1))))
- (define move-down-left
- (λ (pos)
- (set-fields pos
- ((position-y) (+ (position-y pos) 1))
- ((position-x) (- (position-x pos) 1)))))
- (define move-down-right
- (λ (pos)
- (set-fields pos
- ((position-y) (+ (position-y pos) 1))
- ((position-x) (+ (position-x pos) 1)))))
- (define calc-max-rock-depth
- (λ (rock-paths)
- (-> rock-paths
- ;; ((λ (rock-paths) (pretty-print (take rock-paths 5)) rock-paths))
- (map rock-path-segments)
- flatten
- (filter (λ (seg)
- (= (position-y (segment-start seg))
- (position-y (segment-end seg)))))
- (map (λ (hseg)
- (max (position-y (segment-start hseg))
- (position-y (segment-end hseg)))))
- (apply max))))
- (define neighbors
- (λ (pos)
- (values (move-down pos)
- (move-down-left pos)
- (move-down-right pos))))
- (define chunked-rock-paths (split-into-n-segments rock-paths 4))
- (define position-blocked?
- (λ (pos rock-paths sand-blocked-positions)
- (or (set-member? sand-blocked-positions pos)
- (any? (map (λ (rock-path)
- (position-on-rock-path? pos rock-path))
- rock-paths)))))
- (define settle-sand-unit
- (λ (sand-pouring-coords max-rock-depth rock-paths sand-blocked-positions)
- (let iter-sand-move ([sand-position° sand-pouring-coords])
- ;; (simple-format #t "sand unit moved to ~a\n" sand-position°)
- (cond
- ;; If one unit of sand flows into the abyss, the
- ;; next one will also flow there, as it starts at
- ;; the same position and the state of the cave
- ;; has not changed.
- [(> (position-y sand-position°) max-rock-depth) sand-blocked-positions]
- ;; Otherwise check, if the sand unit has come to
- ;; rest or can flow further.
- [else
- (let-values ([(down down-left down-right) (neighbors sand-position°)])
- ;; If any of the 3 neighbor positions is not
- ;; blocked, move the sand unit there, but
- ;; adhere to the specified order.
- (cond
- [(not (position-blocked? down rock-paths sand-blocked-positions))
- (iter-sand-move down)]
- [(not (position-blocked? down-left rock-paths sand-blocked-positions))
- (iter-sand-move down-left)]
- [(not (position-blocked? down-right rock-paths sand-blocked-positions))
- (iter-sand-move down-right)]
- ;; The sand unit has come to rest.
- [else
- (set-insert sand-blocked-positions sand-position°)]))]))))
- (define count-sand-units
- (λ (rock-paths sand-pouring-coords)
- (let ([max-rock-depth (calc-max-rock-depth rock-paths)])
- (simple-format #t "max-rock-depth: ~a\n" max-rock-depth)
- ;; TODO: There is a weird case: What if the sand fills
- ;; up the cave up to the sand pouring position?
- (let iter-sand-units ([counter 0]
- [sand-blocked-positions° (make-empty-set)])
- ;; (simple-format #t "already placed ~a units of sand\n" counter)
- (let ([updated-sand-blocked-positions
- (settle-sand-unit sand-pouring-coords
- max-rock-depth
- rock-paths
- sand-blocked-positions°)])
- (cond
- [(= (set-size updated-sand-blocked-positions)
- (set-size sand-blocked-positions°))
- counter]
- [else
- (iter-sand-units (+ counter 1)
- updated-sand-blocked-positions)]))))))
- (define sand-pouring-coords (make-pos 0 500))
- (simple-format #t "result: ~a\n" (count-sand-units rock-paths sand-pouring-coords))
|