EBNF: parse-alternates options = "("~ elems ( "|"~ elems )* ")"~ => [[ unclip [ first ] dip prefix ]] literal = [NSEW]+ => [[ >string ]] elem = options | literal elems=elem* => [[ >array ]] ;EBNF : letter>direction ( char -- v ) { { CHAR: E [ { 1 0 } ] } { CHAR: W [ { -1 0 } ] } { CHAR: N [ { 0 1 } ] } { CHAR: S [ { 0 -1 } ] } } case ; : explore-string ( str pos map -- pos' ) [ [ letter>direction over v+ ] dip [ [ 2array natural-sort ] dip adjoin ] curry keep ] curry reduce ; : explore ( insn pos map -- pos' ) { { [ pick array? ] [ [ swapd explore ] curry reduce ] } { [ pick vector? ] [ [ explore ] 2curry map first ] } { [ pick string? ] [ explore-string ] } } cond ; USING: path-finding path-finding.private ; TUPLE: facility-bfs < bfs map ; : possible-neighbours ( pos -- neighbours ) { { 1 0 } { -1 0 } { 0 1 } { 0 -1 } } [ v+ ] with map ; M: facility-bfs neighbours [ dup possible-neighbours [ 2array natural-sort ] with map ] [ map>> ] bi* [ in? ] curry filter concat members ; : make-map ( str -- edges ) parse-alternates { 0 0 } HS{ } clone [ explore drop ] keep ; : aoc20 ( str -- ) make-map facility-bfs new swap >>map [ { 0 0 } f ] dip [ find-path drop ] keep g>> values [ supremum . ] [ [ 1000 >= ] count . ] bi ; : aoc20input ( -- ) "/tmp/input" ascii file-lines first rest but-last aoc20 ; aoc20input