$ cat 12a.rkt
#lang racket
(provide
problem-input
)
(define (aoc-12a input)
(let loop
((path-count 0)
(pending-paths (pending-paths:new input)))
(let-values
(((pending-path pending-paths)
(pending-paths:pick pending-paths)))
(let-values
(((pc pp) (advance-path pending-path pending-paths)))
(let ((path-count (+ path-count pc)))
(if (pending-paths:empty? pp)
path-count
(loop path-count pp)))))))
(define (advance-path pending-path pending-paths)
(let loop
((next-nodes (pending-path:next-nodes pending-path))
(path-count 0)
(pending-paths pending-paths))
(if (null? next-nodes)
(values path-count pending-paths)
(let ((next-node (car next-nodes))
(next-nodes (cdr next-nodes)))
(if (node:is-end? next-node)
(loop next-nodes (+ path-count 1) pending-paths)
(loop
next-nodes
path-count
(pending-paths:add
pending-paths
(pending-path:extend-to
pending-path next-node))))))))
(define (node:is-end? node)
(string=? node "end"))
(define (node:is-large? node)
(char-upper-case? (string-ref node 0)))
(define (pending-path:current-node pending-path)
(car pending-path))
(define (pending-path:extend-to pending-path node)
(list
node
(set:add (pending-path:visited-nodes pending-path) node)
(pending-path:neighbors pending-path)))
(define (pending-path:initial input)
(list "start"
(set:add (set:new) "start")
(read-graph input)))
(define (pending-path:neighbors pending-path)
(caddr pending-path))
(define (pending-path:next-nodes pending-path)
(let
((visited-nodes
(pending-path:visited-nodes pending-path)))
(filter
(lambda (n)
(or (node:is-large? n)
(not (set:member? visited-nodes n))))
(hash-ref
(pending-path:neighbors pending-path)
(pending-path:current-node pending-path)))))
(define (pending-path:unvisited? pending-path node)
(not
(set:member?
(pending-path:visited-nodes pending-path)
node)))
(define pending-path:visited-nodes cadr)
(define (pending-paths:add pending-paths new-path)
(set:add pending-paths new-path))
(define (pending-paths:empty? pending-paths)
(set:empty? pending-paths))
(define (pending-paths:new input)
(set:new (pending-path:initial input)))
(define (pending-paths:pick pending-paths)
(set:pick pending-paths))
(define (read-graph input)
(let*
((neighbors (make-hash))
(add-neighbor
(lambda (n1 n2)
(if (hash-has-key? neighbors n1)
(hash-set! neighbors n1
(set:add (hash-ref neighbors n1) n2))
(hash-set! neighbors n1
(set:new n2))))))
(for-each
(lambda (l)
(let* ((edge (string-split l "-"))
(n1 (car edge))
(n2 (cadr edge)))
(add-neighbor n1 n2)
(add-neighbor n2 n1)))
(string-split input))
neighbors))
(define (set:add set e)
(cons e set))
(define (set:empty? set)
(null? set))
(define (set:member? set e)
(if (member e set) #t #f))
(define (set:new . elts)
elts)
(define (set:pick set)
(if (set:empty? set)
(error)
(values (car set) (cdr set))))
(define problem-input
(string-join '(
"qi-UD" "start-qi"
"jt-br" "end-aa"
"wb-TF" "hf-HA"
"VO-aa" "hf-UD"
"UD-aa" "aa-hf"
"br-end" "TF-hf"
"end-HA" "VO-start"
"qi-br" "wb-aa"
"br-HA" "UD-wb"
"UD-start" "KX-wb"
"TF-qi" "qi-VO"
"br-hf" "br-TF"
"VO-hf"
)
"\n"))
(module+ main
(aoc-12a problem-input))
(module+ test
(require rackunit "aoc.rkt")
(check-eq? (aoc-12a "start-a") 0)
(check-eq? (aoc-12a "start-end") 1)
(define two-bits
(string-join '(
"start-o0"
"start-z0"
"o0-j0"
"z0-j0"
"j0-o1"
"j0-z1"
"o1-end"
"z1-end"
)
"\n"))
(check-eq? (aoc-12a two-bits) 4)
(define three-bits
(string-join '(
"start-o0" "start-z0" "o0-j0" "z0-j0"
"j0-o1" "j0-z1" "o1-j1" "z1-j1"
"j1-o2" "j1-z2" "o2-end" "z2-end"
)
"\n"))
(check-eq? (aoc-12a three-bits) 8)
(define example1-input
(string-join '(
"start-A"
"start-b"
"A-c"
"A-b"
"b-d"
"A-end"
"b-end"
)
"\n"))
(check-eq? (aoc-12a example1-input) 10)
(define example2-input
(string-join '(
"dc-end" "LN-dc"
"HN-start" "HN-end"
"start-kj" "kj-sa"
"dc-start" "kj-HN"
"dc-HN" "kj-dc"
)
"\n"))
(check-eq? (aoc-12a example2-input) 19)
(define example3-input
(string-join '(
"fs-end" "RW-he"
"he-DX" "fs-DX"
"fs-he" "pj-RW"
"start-DX" "zg-RW"
"pj-DX" "start-pj"
"end-zg" "he-WI"
"zg-sl" "zg-he"
"zg-pj" "pj-fs"
"pj-he" "start-RW"
)
"\n"))
(check-eq? (aoc-12a example3-input) 226)
(check-eq? (aoc-12a problem-input) 3856))
$ raco test 12a.rkt
raco test: (submod "12a.rkt" test)
8 tests passed
$