2019/src/15.rkt

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)