@@ -7,6 +7,7 @@ export type Location = {
77 pos : Position
88 path : Position [ ]
99 status : LocationStatus
10+ level : number
1011}
1112
1213export enum Tile {
@@ -25,10 +26,21 @@ export enum DIRECTION {
2526const START = 'AA'
2627const END = 'ZZ'
2728
28- const isSafe = ( maze : MazeString , visited : boolean [ ] , pos : Position ) => {
29+ type Visited = {
30+ [ key : number ] : {
31+ [ key : number ] : boolean
32+ }
33+ }
34+
35+ const isSafe = (
36+ maze : MazeString ,
37+ visited : Visited ,
38+ pos : Position ,
39+ level : number ,
40+ ) => {
2941 const p = pos . y * maze . width + pos . x
3042 if ( maze . maze [ p ] === undefined ) return false
31- if ( visited [ p ] ) return false
43+ if ( visited [ level ] [ p ] ) return false
3244 if ( maze . maze [ p ] !== Tile . PATH ) return false
3345 return true
3446}
@@ -37,28 +49,32 @@ const equals = (a: Position, b: Position) => a.x === b.x && a.y === b.y
3749
3850const status = (
3951 maze : MazeString ,
40- visited : boolean [ ] ,
41- location : Position ,
52+ visited : Visited ,
53+ pos : Position ,
54+ level : number ,
4255) : LocationStatus => {
43- if ( ! isSafe ( maze , visited , location ) ) return 'Blocked'
56+ if ( ! isSafe ( maze , visited , pos , level ) ) return 'Blocked'
4457 return 'Valid'
4558}
4659
4760const createLocation = (
4861 maze : MazeString ,
49- visited : boolean [ ] ,
62+ visited : Visited ,
5063 location : Location ,
5164) => ( pos : Position ) : Location => ( {
65+ level : location . level ,
5266 pos : pos ,
5367 path : [ ...location . path , location . pos ] ,
54- status : status ( maze , visited , pos ) ,
68+ status : status ( maze , visited , pos , location . level ) ,
5569} )
5670
5771const exploreInDirection = (
5872 maze : MazeString ,
59- visited : boolean [ ] ,
73+ visited : Visited ,
6074 portals : Portal [ ] ,
6175 location : Location ,
76+ // whether to apply recursive rules
77+ recursive = false ,
6278) => ( direction : DIRECTION ) : Location => {
6379 let newLocation : Location
6480 const cl = createLocation ( maze , visited , location )
@@ -78,25 +94,100 @@ const exploreInDirection = (
7894 }
7995
8096 if ( newLocation . status === 'Valid' ) {
81- visited [ newLocation . pos . y * maze . width + newLocation . pos . x ] = true
97+ visited [ newLocation . level ] [
98+ newLocation . pos . y * maze . width + newLocation . pos . x
99+ ] = true
82100 // Is this a portal?
83101 const portal = portals . find ( ( { pos } ) => equals ( pos , newLocation . pos ) )
84102 if ( portal ) {
85- // Is this the last portal
86- if ( portal . label === END ) {
87- return {
88- ...newLocation ,
89- status : 'Target' ,
90- }
91- }
92103 const pair = portals . find (
93104 p => p !== portal && p . label === portal . label ,
94105 ) as Portal
95- visited [ pair . pos . y * maze . width + pair . pos . x ] = true
96- return {
97- pos : pair . pos ,
98- path : [ ...location . path , location . pos , portal . pos ] ,
99- status : 'Valid' ,
106+ if ( recursive ) {
107+ // When you enter the maze, you are at the outermost level (0);
108+ // when at the outermost level, only the outer labels AA and ZZ
109+ // function (as the start and end, respectively); all other outer
110+ // labeled tiles are effectively walls. At any other level,
111+ // AA and ZZ count as walls, but the other outer labeled tiles
112+ // bring you one level outward.
113+ if ( location . level === 0 ) {
114+ // Outermost level
115+ if ( portal . label === END ) {
116+ // ZZ is accessible
117+ return {
118+ ...newLocation ,
119+ status : 'Target' ,
120+ }
121+ }
122+ // Outer portals are walls at outermost level
123+ if ( portal . isOuter ) {
124+ return {
125+ ...newLocation ,
126+ status : 'Blocked' ,
127+ }
128+ } else {
129+ // Inner portal takes you one level deeper
130+ if ( visited [ newLocation . level ] === undefined ) {
131+ visited [ newLocation . level ] = [ ]
132+ }
133+ visited [ newLocation . level + 1 ] [
134+ pair . pos . y * maze . width + pair . pos . x
135+ ] = true
136+ return {
137+ pos : pair . pos ,
138+ path : [ ...location . path , location . pos , portal . pos ] ,
139+ status : 'Valid' ,
140+ level : newLocation . level + 1 ,
141+ }
142+ }
143+ } else {
144+ // Other level
145+ if ( portal . label === END ) {
146+ // ZZ is not accessible
147+ return {
148+ ...newLocation ,
149+ status : 'Blocked' ,
150+ }
151+ }
152+ if ( portal . isOuter ) {
153+ // Outer portal takes you one level up
154+ visited [ newLocation . level - 1 ] [
155+ pair . pos . y * maze . width + pair . pos . x
156+ ] = true
157+ return {
158+ pos : pair . pos ,
159+ path : [ ...location . path , location . pos , portal . pos ] ,
160+ status : 'Valid' ,
161+ level : newLocation . level - 1 ,
162+ }
163+ } else {
164+ // Inner portal takes you one level deeper
165+ visited [ newLocation . level + 1 ] [
166+ pair . pos . y * maze . width + pair . pos . x
167+ ] = true
168+ return {
169+ pos : pair . pos ,
170+ path : [ ...location . path , location . pos , portal . pos ] ,
171+ status : 'Valid' ,
172+ level : newLocation . level + 1 ,
173+ }
174+ }
175+ }
176+ } else {
177+ // Is this the last portal
178+ if ( portal . label === END ) {
179+ return {
180+ ...newLocation ,
181+ status : 'Target' ,
182+ }
183+ }
184+ visited [ newLocation . level ] [ pair . pos . y * maze . width + pair . pos . x ] = true
185+ return {
186+ pos : pair . pos ,
187+ path : [ ...location . path , location . pos , portal . pos ] ,
188+ status : 'Valid' ,
189+ level : newLocation . level ,
190+ }
100191 }
101192 }
102193 }
@@ -109,28 +200,39 @@ export type MazeString = {
109200 width : number
110201}
111202
112- export const transportingMazeSolver = ( maze : string ) : Location | undefined => {
203+ export const transportingMazeSolver = (
204+ maze : string ,
205+ recursive = false ,
206+ ) : Location | undefined => {
113207 const width = maze . indexOf ( '\n' )
114208 const mazeString : MazeString = {
115209 width,
116210 maze : maze . trimEnd ( ) . replace ( / \n / g, '' ) ,
117211 }
118212 const portals = findPortals ( maze )
119- const visited = [ ] as boolean [ ]
213+ const visited = [ ] as Visited
214+ visited [ 0 ] = [ ]
120215
121216 const startPos = portals . find ( ( { label } ) => label === START ) as Portal
122217 const queue = [
123218 {
124219 path : [ ] ,
125220 pos : startPos . pos ,
126221 status : 'Start' ,
222+ level : 0 ,
127223 } ,
128224 ] as Location [ ]
129- visited [ startPos . pos . y * width + startPos . pos . x ] = true
225+ visited [ 0 ] [ startPos . pos . y * width + startPos . pos . x ] = true
130226
131227 while ( queue . length > 0 ) {
132228 const location = queue . shift ( ) as Location
133- const e = exploreInDirection ( mazeString , visited , portals , location )
229+ const e = exploreInDirection (
230+ mazeString ,
231+ visited ,
232+ portals ,
233+ location ,
234+ recursive ,
235+ )
134236
135237 // Up
136238 const up = e ( DIRECTION . UP )
0 commit comments