100 lines
3.5 KiB
Racket
100 lines
3.5 KiB
Racket
#lang plai
|
|
|
|
(require graph
|
|
data/queue
|
|
(except-in "../lib.rkt" transpose)
|
|
"IntCode.rkt")
|
|
|
|
(define input
|
|
(string->program (car (problem-input 15))))
|
|
|
|
;; grid : (hashof (coord => status))
|
|
;; We will use a hashtable from coordinates to status codes:
|
|
;; 0: wall
|
|
;; 1: floor
|
|
;; 2: oxygen system
|
|
;; 3: unexplored
|
|
;; 4: droid
|
|
;; Use (show-hash-grid status-hash grid 3) to print out the grid as a map
|
|
(define status-hash (make-hash '((0 . #\#) (1 . #\.) (2 . #\O) (3 . #\ ) (4 . #\D))))
|
|
|
|
;; coord : (number . number)
|
|
;; Coordinates increase in the southern and eastern directions
|
|
|
|
;; next-doors : dir -> coord -> (listof coord)
|
|
;; Give a list of the next coordinates we could visit
|
|
(define (next-coords coord)
|
|
(let* ([x (car coord)]
|
|
[y (cdr coord)])
|
|
(list (cons x (sub1 y))
|
|
(cons x (add1 y))
|
|
(cons (sub1 x) y)
|
|
(cons (add1 x) y))))
|
|
|
|
;; next-dir : coord -> coord -> dir
|
|
;; Given our old coordinate and the new coordinate,
|
|
;; return the direction we have gone in
|
|
(define (next-dir old-coord new-coord)
|
|
(let ([old-x (car old-coord)]
|
|
[old-y (cdr old-coord)]
|
|
[new-x (car new-coord)]
|
|
[new-y (cdr new-coord)])
|
|
(cond
|
|
[(< new-y old-y) 1] ; north
|
|
[(> new-y old-y) 2] ; south
|
|
[(< new-x old-x) 3] ; west
|
|
[(> new-x old-x) 4]))) ; east
|
|
|
|
;; move-list : coord -> state -> grid -> (list grid (listof procedure))
|
|
;; For each possible direction we can move in, return an update grid
|
|
;; and a list of functions, each of which are advancing the state
|
|
;; from the next possible coordinate when given a grid
|
|
;; I'm not sure how to assign a type to this...
|
|
(define (move-list coord st grid)
|
|
(let ([unvisited (filter-not (∂ hash-has-key? grid) (next-coords coord))])
|
|
(foldl
|
|
(λ (next grid-lst)
|
|
(match-let ([(list grid lst) grid-lst])
|
|
(type-case state (resume-with-input st (next-dir coord next))
|
|
[out (value resume)
|
|
(list (hash-set grid next value)
|
|
(if (= 0 value) lst
|
|
(cons (∂ move-list next (resume)) lst)))]
|
|
[else (error "Unexpected program state")])))
|
|
(list grid '()) unvisited)))
|
|
|
|
;; explore-map : grid -> grid
|
|
;; Breadth-first search of all accessible locations
|
|
(define (explore-map grid)
|
|
(let ([Q (make-queue)])
|
|
(enqueue! Q (∂ move-list '(0 . 0) (exec input)))
|
|
(let loop ([grid grid])
|
|
(if (queue-empty? Q) grid
|
|
(match-let ([(list grid nexts)
|
|
((dequeue! Q) grid)])
|
|
(for-each (∂ enqueue! Q) nexts)
|
|
(loop grid))))))
|
|
|
|
;; grid->graph : grid -> unweighted, undirected graph between coordinates
|
|
;; There is an edge between coordinates if we can travel from one to the other
|
|
(define (grid->graph grid)
|
|
(define (filter-non-wall coords)
|
|
(filter (λ (c) (!= 0 (hash-ref grid c 0))) coords))
|
|
(unweighted-graph/undirected
|
|
(apply append
|
|
(map (λ (coord)
|
|
(map (∂ list coord)
|
|
(filter-non-wall (next-coords coord))))
|
|
(filter-non-wall (hash-keys grid))))))
|
|
|
|
(define-values (part1 part2)
|
|
(let* ([grid (make-immutable-hash '(((0 . 0) . 4)))]
|
|
[grid (explore-map grid)]
|
|
[oxygen (car (findf (∘ (∂ = 2) cdr) (hash->list grid)))]
|
|
[graph (grid->graph grid)])
|
|
(show-hash-grid status-hash grid 3)
|
|
(let-values ([(dist-hash _) (dijkstra graph oxygen)])
|
|
(values (hash-ref dist-hash '(0 . 0))
|
|
(apply max (hash-values dist-hash))))))
|
|
|
|
(show-solution part1 part2) |