13 static int debug_level
= -1;
17 debug_level
= atoi(getenv("DEBUG") ?: "0");
20 static void __attribute__((format(printf
, 2, 3)))
21 debug_raw(int level
, const char *fmt
, ...) {
24 debug_level
= atoi(getenv("DEBUG") ?: "0");
25 if (debug_level
>= level
) {
27 vfprintf(stderr
, fmt
, ap
);
31 #define debug(...) debug_raw(1, __VA_ARGS__)
33 static int __attribute__((noreturn
)) __attribute__((format(printf
, 1, 2)))
34 die(const char *fmt
, ...)
46 enum dir
{ U
, R
, D
, L
};
52 typedef struct grid_elt grid_row
[MAX
+ 1];
53 typedef grid_row grid_full
[MAX
];
54 static grid_full grids
[PORTALS
];
55 static int maxx
, maxy
;
56 static int oleft
, oright
, otop
, obottom
; /* inclusive corner coords */
57 static int ileft
, iright
, itop
, ibottom
;
58 static int thick
, scans
;
63 uint8_t d
; /* enum dir */
66 static struct portal
{
71 static uint8_t aa
= -1, zz
= -1;
73 static union pt (*next
)(union pt p
, enum dir d
);
75 static union pt
next_flat(union pt p
, enum dir d
) {
76 int8_t idx
= grids
[p
.z
][p
.y
][p
.x
].idx
;
79 if (idx
>= 0 && portals
[idx
].p1
.i
== p
.i
) {
82 debug("traveling through portal %s\n", portals
[idx
].id
);
85 if (idx
>= 0 && portals
[idx
].p2
.i
== p
.i
) {
88 debug("traveling through portal %s\n", portals
[idx
].id
);
100 static union pt
next_recursive(union pt p
, enum dir d
) {
101 int8_t idx
= grids
[p
.z
][p
.y
][p
.x
].idx
;
105 if (idx
>= 0 && portals
[idx
].p1
.x
== p
.x
&& portals
[idx
].p1
.y
== p
.y
&&
106 portals
[idx
].p1
.d
== p
.d
) {
107 if (idx
== aa
|| idx
== zz
) {
109 die("unexpected end-point visit");
110 debug("outer portal %s blocked at inner level %d\n", portals
[idx
].id
, z
);
113 debug("outer portal %s blocked at outer level\n", portals
[idx
].id
);
116 debug("traveling from level %d into outer portal %s\n", z
,
124 if (idx
>= 0 && portals
[idx
].p2
.x
== p
.x
&& portals
[idx
].p2
.y
== p
.y
&&
125 portals
[idx
].p2
.d
== p
.d
) {
128 if (p
.z
== nportals
- 1) {
129 debug("too much recursion into inner portal %s\n", portals
[idx
].id
);
132 debug("traveling from level %d into inner portal %s\n", z
,
139 case U
: p
.y
--; break;
140 case R
: p
.x
++; break;
141 case D
: p
.y
++; break;
142 case L
: p
.x
--; break;
148 minu(unsigned int a
, unsigned b
) {
149 return a
< b
? a
: b
;
160 die("too much recursion");
162 debug("\n *** level %d grid:\n", l
);
163 for (y
= 0; y
< maxy
; y
++) {
164 for (x
= 0; x
<= maxx
; x
++)
165 debug(" %c", g
[y
][x
].tile
);
166 for (x
= 0; x
< maxx
; x
++) {
167 if (isatty(fileno(stderr
)))
168 switch (g
[y
][x
].distance
/ 100U) {
170 case 1: debug("\x1b[32m"); break;
171 case 2: debug("\x1b[33m"); break;
172 case 3: debug("\x1b[34m"); break;
173 default: debug("\x1b[35m"); break;
175 debug("%2d", g
[y
][x
].distance
% 100);
176 if (isatty(fileno(stderr
)))
185 scan_one(union pt p
, short d
, int depth
) {
190 if (grids
[p
.z
][p
.y
][p
.x
].tile
!= '.')
193 if ((unsigned) grids
[p
.z
][p
.y
][p
.x
].distance
< d
)
195 grids
[p
.z
][p
.y
][p
.x
].distance
= d
;
196 if (!p
.z
&& grids
[p
.z
][p
.y
][p
.x
].idx
== zz
) {
197 debug("found ZZ at distance %d\n", d
);
201 die("recompile with 32-bit distance");
203 r
= minu(r
, scan_one(next(p
, U
), d
, depth
+ 1));
205 r
= minu(r
, scan_one(next(p
, R
), d
, depth
+ 1));
207 r
= minu(r
, scan_one(next(p
, D
), d
, depth
+ 1));
209 r
= minu(r
, scan_one(next(p
, L
), d
, depth
+ 1));
214 portal(int x
, int y
, enum dir d
, bool outer
) {
220 id
[0] = grids
[0][y
- 2][x
].tile
;
221 id
[1] = grids
[0][y
- 1][x
].tile
;
224 id
[0] = grids
[0][y
][x
+ 1].tile
;
225 id
[1] = grids
[0][y
][x
+ 2].tile
;
228 id
[0] = grids
[0][y
+ 1][x
].tile
;
229 id
[1] = grids
[0][y
+ 2][x
].tile
;
232 id
[0] = grids
[0][y
][x
- 2].tile
;
233 id
[1] = grids
[0][y
][x
- 1].tile
;
236 debug("labeling %s portal %s at %d,%d %c\n", outer
? "outer" : "inner",
237 id
, x
, y
, "URDL"[d
]);
238 for (i
= 0; i
< nportals
; i
++)
239 if (!strcmp(portals
[i
].id
, id
))
241 grids
[0][y
][x
].idx
= i
;
243 if (++nportals
== PORTALS
)
244 die("recompile with larger PORTALS");
246 die("portal %s has no outer counterpart", id
);
247 memcpy(portals
[i
].id
, id
, 3);
251 if (!strcmp("AA", id
)) {
253 portals
[i
].p2
.x
= x
- (d
== L
) + (d
== R
);
254 portals
[i
].p2
.y
= y
- (d
== U
) + (d
== D
);
255 portals
[i
].p2
.d
= d
^ 2;
256 } else if (!strcmp("ZZ", id
))
259 if (outer
|| portals
[i
].p2
.x
|| i
== aa
|| i
== zz
)
260 die("portal %s seen too many times", id
);
268 in_maze(int x
, int y
) {
269 char c
= grids
[0][y
][x
].tile
;
271 return c
== '.' || c
== '#';
279 while (in_maze(thick
+ 2, j
))
281 if (thick
* 2 + 9 > maxx
)
282 die("can't find hole");
287 ileft
= oleft
+ thick
- 1;
288 iright
= oright
- thick
+ 1;
289 itop
= otop
+ thick
- 1;
290 ibottom
= obottom
- thick
+ 1;
292 for (i
= 0; i
< maxx
; i
++)
293 if (in_maze(i
, 0) || in_maze(i
, 1) ||
294 in_maze(i
, obottom
+ 1) || in_maze(i
, obottom
+ 2))
295 die("upper/lower rows incorrect");
296 for (j
= otop
; j
<= obottom
; j
++)
297 if (in_maze(0, j
) || in_maze(1, j
) ||
298 in_maze(oright
+ 1, j
) || in_maze(oright
+ 2, j
))
299 die("left/right columns incorrect");
300 if (grids
[0][otop
][oleft
].tile
!= '#' ||
301 grids
[0][otop
][oright
].tile
!= '#' ||
302 grids
[0][obottom
][oleft
].tile
!= '#' ||
303 grids
[0][obottom
][oright
].tile
!= '#')
304 die("corners incorrect");
305 for (j
= itop
+ 1; j
< ibottom
; j
++)
306 for (i
= ileft
+ 1; i
< iright
; i
++)
308 die("hole incorrect");
310 for (j
= otop
, i
= oleft
+ 1; i
< oright
; i
++)
311 if (grids
[0][j
][i
].tile
== '.')
312 portal(i
, j
, U
, true);
313 for (i
= oright
, j
= otop
+ 1; j
< obottom
; j
++)
314 if (grids
[0][j
][i
].tile
== '.')
315 portal(i
, j
, R
, true);
316 for (j
= obottom
, i
= oleft
+ 1; i
< oright
; i
++)
317 if (grids
[0][j
][i
].tile
== '.')
318 portal(i
, j
, D
, true);
319 for (i
= oleft
, j
= otop
+ 1; j
< obottom
; j
++)
320 if (grids
[0][j
][i
].tile
== '.')
321 portal(i
, j
, L
, true);
323 for (j
= itop
, i
= ileft
+ 1; i
< iright
; i
++)
324 if (grids
[0][j
][i
].tile
== '.')
325 portal(i
, j
, D
, false);
326 for (i
= iright
, j
= itop
+ 1; j
< ibottom
; j
++)
327 if (grids
[0][j
][i
].tile
== '.')
328 portal(i
, j
, L
, false);
329 for (j
= ibottom
, i
= ileft
+ 1; i
< iright
; i
++)
330 if (grids
[0][j
][i
].tile
== '.')
331 portal(i
, j
, U
, false);
332 for (i
= ileft
, j
= itop
+ 1; j
< ibottom
; j
++)
333 if (grids
[0][j
][i
].tile
== '.')
334 portal(i
, j
, R
, false);
336 if (aa
> PORTALS
|| zz
> PORTALS
)
337 die("can't find aa/zz");
338 for (i
= 0; i
< nportals
; i
++) {
339 if (i
== aa
|| i
== zz
)
341 if (!portals
[i
].p2
.x
)
342 die("no matching inner portal for %s", portals
[i
].id
);
348 main(int argc
, char **argv
) {
354 if (argc
> 1 && strcmp(argv
[1], "-"))
355 if (!(stdin
= freopen(argv
[1], "r", stdin
))) {
360 memset(grids
[0], -1, sizeof grids
[0]);
361 while ((ch
= getchar()) != EOF
) {
362 grids
[0][y
][x
].tile
= ch
;
365 die("uneven line lengths");
371 die("recompile with larger MAX y");
375 die("recompile with larger MAX x");
377 } else if (x
> maxx
) {
378 die("uneven line length");
381 if (maxx
< 11 || maxy
< 11)
382 die("grid is too small");
386 for (i
= 1; i
< nportals
; i
++)
387 memcpy(grids
[i
], grids
[0], sizeof grids
[0]);
390 printf("maze loaded. thickness:%d portals:%d AA:%d,%d ZZ:%d,%d\n",
391 thick
, nportals
, portals
[aa
].p1
.x
, portals
[aa
].p1
.y
,
392 portals
[zz
].p1
.x
, portals
[zz
].p1
.y
);
393 best
= scan_one(next(portals
[aa
].p2
, portals
[aa
].p2
.d
), 0, 0);
395 printf("best flat path in %d steps, with %d scans and call depth %d\n",
396 best
, scans
, deepest
);
398 memcpy(grids
[0], grids
[1], sizeof grids
[0]);
399 next
= next_recursive
;
401 best
= scan_one(next(portals
[aa
].p2
, portals
[aa
].p2
.d
), 0, 0);
402 for (i
= 0; i
< nportals
; i
++)
405 printf("best recursive path in %d steps, with %d scans and call depth %d\n",
406 best
, scans
, deepest
);
408 printf("no recursive path found after %d scans and call depth %d\n",