145
151
GC gc, cgc, tgc, sgc, ugc, logo_gc, erase_gc;
147
int logo_width, logo_height; /* in pixels */
154
int logo_x, logo_y; /* in grid cells (or -1) */
155
int logo_width, logo_height; /* in pixels */
156
int fps_width, fps_height; /* in pixels */
149
158
int solve_delay, pre_solve_delay, post_solve_delay;
152
160
unsigned short maze[MAX_MAZE_SIZE_X][MAX_MAZE_SIZE_Y];
286
304
if ((st->maze_size_x-logow >= 6) && (st->maze_size_y-logoh >= 6))
288
/* not closer than 3 grid units from a wall */
306
/* not closer than 3 grid units from a wall, to ensure that it
307
won't overlap the entrance or exit. */
289
308
st->logo_x = get_random (st->maze_size_x - logow - 5) + 3;
290
309
st->logo_y = get_random (st->maze_size_y - logoh - 5) + 3;
291
310
for (i=0; i<logow; i++)
292
311
for (j=0; j<logoh; j++)
293
st->maze[st->logo_x + i][st->logo_y + j] |= DOOR_IN_TOP;
312
/* mark as having doors to prevent these cells being used in maze. */
313
st->maze[st->logo_x + i][st->logo_y + j] |= DOOR_IN_ANY;
296
316
st->logo_y = st->logo_x = -1;
298
/* #### should mask out the cells covered by the FPS display if "doFPS"
299
is true. But that's hard, because the "logo_x" crap that does
300
that trick for the logo is scattered all around the code... */
318
/* mask out the fps area */
319
if (st->fps_width > 0)
321
int fpsw = 1 + st->fps_width / st->grid_width;
322
int fpsh = 1 + st->fps_height / st->grid_width;
324
/* if the chosen logo position overlapped the fps area, try again! */
325
if (st->logo_x < fpsw+3 && st->logo_y+logoh > st->maze_size_y-fpsh-4)
328
/* if the start or end point is inside the fps area, try again! */
329
if ((st->start_x <= fpsw && st->start_y >= st->maze_size_y-fpsh-1) ||
330
(st->end_x <= fpsw && st->end_y >= st->maze_size_y-fpsh-1))
333
for (i=0; i<fpsw; i++)
334
for (j=0; j<fpsh; j++) {
335
/* mark as having doors to prevent these cells being used in maze.
336
mark as visit to prevent it being colored "unreachable". */
337
st->maze[i][st->maze_size_y - j - 1] |= DOOR_IN_ANY|SOLVER_VISIT;
338
/* take off left/bottom wall or the FPS text overlaps it */
339
st->maze[i][st->maze_size_y - j - 1] &= ~(WALL_BOTTOM|WALL_LEFT);
303
static int choose_door (struct state *st);
304
static int backup (struct state *st);
305
static void draw_wall (struct state *, int, int, int, GC);
306
static void draw_solid_square (struct state *, int, int, int, GC);
307
/*static void enter_square (struct state *, int);*/
308
static void build_wall (struct state *, int, int, int);
309
/*static void break_wall (struct state *, int, int, int);*/
311
static void join_sets(struct state *, int, int);
345
/****************************************************************************
346
Generator 2: Set-based maze generator.
348
Put each square in the maze in a separate set. Also, make a list of all the
349
hedges. Randomize that list. Walk through the list. If, for a certain
350
hedge, the two squares on both sides of it are in different sets, union the
351
sets and remove the hedge. Continue until all hedges have been processed or
352
only one set remains.
354
This is essentially the "Kruskal" algorithm.
356
****************************************************************************/
358
static void mask_out_set_rect(struct state *st, int x, int y, int w, int h);
315
360
/* Initialise the sets. */
317
362
init_sets(struct state *st)
347
392
st->hedges[2*((st->maze_size_y-1)*st->maze_size_x+i)] = -1;
349
/* Mask out a possible logo. */
395
/* Mask out the logo area. */
350
396
if(st->logo_x!=-1)
352
int logow = 1 + st->logo_width / st->grid_width;
398
int logow = 1 + st->logo_width / st->grid_width;
353
399
int logoh = 1 + st->logo_height / st->grid_height;
354
int bridge_dir, bridge_c;
356
if(st->bridge_p && logoh>=3 && logow>=3)
358
bridge_dir = 1+random()%2;
361
bridge_c = st->logo_y+random()%(logoh-2)+1;
365
bridge_c = st->logo_x+random()%(logow-2)+1;
374
for(xx = st->logo_x; xx < st->logo_x+logow; xx++)
375
for(yy = st->logo_y; yy < st->logo_y+logoh; yy++)
377
/* I should check for the bridge here, except that I join the
378
* bridge together below.
380
st->hedges[2*(xx+st->maze_size_x*yy)+1] = -1;
381
st->hedges[2*(xx+st->maze_size_x*yy)] = -1;
383
for(xx = st->logo_x; xx < st->logo_x+logow; xx++)
385
if(!(bridge_dir==2 && xx==bridge_c))
387
build_wall(st, xx, st->logo_y, 0);
388
build_wall(st, xx, st->logo_y+logoh, 0);
390
st->hedges[2*(xx+st->maze_size_x*(st->logo_y-1))] = -1;
393
build_wall(st, xx, bridge_c, 0);
394
build_wall(st, xx, bridge_c, 2);
397
for(yy = st->logo_y; yy < st->logo_y+logoh; yy++)
399
if(!(bridge_dir==1 && yy==bridge_c))
401
build_wall(st, st->logo_x, yy, 3);
402
build_wall(st, st->logo_x+logow, yy, 3);
404
st->hedges[2*(st->logo_x-1+st->maze_size_x*yy)+1] = -1;
407
build_wall(st, bridge_c, yy, 1);
408
build_wall(st, bridge_c, yy, 3);
411
/* Join the whole bridge together. */
418
for(i = st->logo_x; i < st->logo_x+logow+1; i++)
419
join_sets(st, xx+yy*st->maze_size_x, i+yy*st->maze_size_x);
425
for(i = st->logo_y; i < st->logo_y+logoh+1; i++)
426
join_sets(st, xx+yy*st->maze_size_x, xx+i*st->maze_size_x);
400
mask_out_set_rect(st, st->logo_x, st->logo_y, logow, logoh);
403
/* Mask out the FPS area. */
404
if(st->fps_width > 0)
406
int fpsw = 1 + st->fps_width / st->grid_width;
407
int fpsh = 1 + st->fps_height / st->grid_height;
408
mask_out_set_rect(st, 0, st->maze_size_y-fpsh, fpsw, fpsh);
431
411
for(i = 0; i < st->maze_size_x*st->maze_size_y*2; i++)
484
/* Temporary hack. */
486
show_set(int num, GC gc)
488
int st->x, st->y, set;
492
for(st->x = 0; st->x < st->maze_size_x; st->x++)
493
for(st->y = 0; st->y < st->maze_size_y; st->y++)
495
if(get_set(st->x+st->y*st->maze_size_x)==set)
497
XFillRectangle(st->dpy, st->window, st->gc, border_x + st->bw + st->grid_width * st->x,
498
border_y + st->bw + st->grid_height * st->y,
499
st->grid_width-2*st->bw , st->grid_height-2*st->bw);
505
/* Second alternative maze creator: Put each square in the maze in a
506
* separate set. Also, make a list of all the hedges. Randomize that list.
507
* Walk through the list. If, for a certain hedge, the two squares on both
508
* sides of it are in different sets, union the sets and remove the hedge.
509
* Continue until all hedges have been processed or only one set remains.
512
465
set_create_maze(struct state *st)
514
467
int i, h, xx, yy, dir, v, w;
520
469
/* Do almost all the setup. */
549
show_set(st, xx+yy*st->maze_size_x, st->logo_gc);
550
show_set(st, v+w*st->maze_size_x, st->tgc);
552
497
if(get_set(st, xx+yy*st->maze_size_x)!=get_set(st, v+w*st->maze_size_x))
557
499
join_sets(st, xx+yy*st->maze_size_x, v+w*st->maze_size_x);
558
500
/* Don't draw the wall. */
565
504
/* Don't join the sets. */
566
505
build_wall(st, xx, yy, dir);
571
XSync(st->dpy, False);
576
show_set(xx+yy*st->maze_size_x, st->erase_gc);
577
show_set(v+w*st->maze_size_x, st->erase_gc);
581
509
/* Free some memory. */
585
/* First alternative maze creator: Pick a random, empty corner in the maze.
586
* Pick a random direction. Draw a wall in that direction, from that corner
587
* until we hit a wall. Option: Only draw the wall if it's going to be
588
* shorter than a certain length. Otherwise we get lots of long walls.
513
/* mark a rectangle as being unavailable for usage in the maze */
515
mask_out_set_rect(struct state *st, int x, int y, int w, int h)
518
for(xx = x; xx < x+w; xx++)
519
for(yy = y; yy < y+h; yy++)
521
st->hedges[2*(xx+st->maze_size_x*yy)+1] = -1;
522
st->hedges[2*(xx+st->maze_size_x*yy)] = -1;
524
for(xx = x; xx < x+w; xx++)
526
build_wall(st, xx, y, 0);
527
build_wall(st, xx, y+h, 0);
528
st->hedges[2*(xx+st->maze_size_x*(y-1))] = -1;
530
for(yy = y; yy < y+h; yy++)
532
build_wall(st, x, yy, 3);
533
build_wall(st, x+w, yy, 3);
534
st->hedges[2*(x-1+st->maze_size_x*yy)+1] = -1;
539
/****************************************************************************
540
Generator 1: Wall-building maze generator.
542
Pick a random, empty corner in the maze. Pick a random direction. Draw a
543
wall in that direction, from that corner until we hit a wall. Option: Only
544
draw the wall if it's going to be shorter than a certain length. Otherwise
545
we get lots of long walls.
547
This is essentially the "Prim" algorithm.
548
****************************************************************************/
550
static void alt_mask_out_rect(struct state *st, char *corners,
551
int x, int y, int w, int h);
591
554
alt_create_maze(struct state *st)
631
594
corners[i*width] = 1;
632
595
corners[i*width+width-1] = 1;
634
/* Walls around logo. In fact, inside the logo, too. */
635
/* Also draw the walls. */
598
/* mask out the logo area */
636
599
if(st->logo_x!=-1)
638
601
int logow = 1 + st->logo_width / st->grid_width;
639
602
int logoh = 1 + st->logo_height / st->grid_height;
640
int bridge_dir, bridge_c;
603
alt_mask_out_rect (st, corners, st->logo_x, st->logo_y, logow, logoh);
642
if(st->bridge_p && logoh>=3 && logow>=3)
644
bridge_dir = 1+random()%2;
647
bridge_c = st->logo_y+random()%(logoh-2)+1;
651
bridge_c = st->logo_x+random()%(logow-2)+1;
659
for(i = st->logo_x; i <= st->logo_x + logow; i++)
661
for(j = st->logo_y; j <= st->logo_y + logoh; j++)
663
corners[i+width*j] = 1;
666
for(xx = st->logo_x; xx < st->logo_x+logow; xx++)
668
if(!(bridge_dir==2 && xx==bridge_c))
670
build_wall(st, xx, st->logo_y, 0);
671
build_wall(st, xx, st->logo_y+logoh, 0);
675
build_wall(st, xx, bridge_c, 0);
676
build_wall(st, xx, bridge_c, 2);
679
for(yy = st->logo_y; yy < st->logo_y+logoh; yy++)
681
if(!(bridge_dir==1 && yy==bridge_c))
683
build_wall(st, st->logo_x, yy, 3);
684
build_wall(st, st->logo_x+logow, yy, 3);
688
build_wall(st, bridge_c, yy, 1);
689
build_wall(st, bridge_c, yy, 3);
692
/* Connect one wall of the logo with an outside wall. */
694
dir = (bridge_dir+1)%4;
700
xx = st->logo_x+(random()%(logow+1));
704
xx = st->logo_x+logow;
705
yy = st->logo_y+(random()%(logoh+1));
708
xx = st->logo_x+(random()%(logow+1));
709
yy = st->logo_y+logoh;
713
yy = st->logo_y+(random()%(logoh+1));
718
corners[xx+width*yy] = 1;
722
build_wall(st, xx-1, yy-1, 1);
726
build_wall(st, xx, yy, 0);
730
build_wall(st, xx, yy, 3);
734
build_wall(st, xx-1, yy-1, 2);
739
while(!corners[xx+width*yy]);
746
xx = st->logo_x+(random()%(logow+1));
750
xx = st->logo_x+logow;
751
yy = st->logo_y+(random()%(logoh+1));
754
xx = st->logo_x+(random()%(logow+1));
755
yy = st->logo_y+logoh;
759
yy = st->logo_y+(random()%(logoh+1));
764
corners[xx+width*yy] = 1;
768
build_wall(st, xx-1, yy-1, 1);
772
build_wall(st, xx, yy, 0);
776
build_wall(st, xx, yy, 3);
780
build_wall(st, xx-1, yy-1, 2);
785
while(!corners[xx+width*yy]);
606
/* mask out the FPS area */
609
int fpsw = 1 + st->fps_width / st->grid_width;
610
int fpsh = 1 + st->fps_height / st->grid_height;
611
alt_mask_out_rect (st, corners, 0, st->maze_size_y-fpsh, fpsw, fpsh);
789
614
/* Count open gridpoints. */
870
/* The original maze creator. Start somewhere. Take a step in a random
871
* direction. Keep doing this until we hit a wall. Then, backtrack until
872
* we find a point where we can go in another direction.
696
/* mark a rectangle as being unavailable for usage in the maze */
698
alt_mask_out_rect(struct state *st, char *corners, int x, int y, int w, int h)
701
int mazew = st->maze_size_x+1;
703
for(i = x; i <= x+w; i++)
704
for(j = y; j <= y+h; j++)
705
corners[i+mazew*j] = 1;
707
for(xx = x; xx < x+w; xx++)
709
build_wall(st, xx, y, 0);
710
if (y+h < st->maze_size_y)
711
build_wall(st, xx, y+h, 0);
713
for(yy = y; yy < y+h; yy++)
716
build_wall(st, x, yy, 3);
717
build_wall(st, x+w, yy, 3);
722
/****************************************************************************
723
Generator 0: The original maze generator.
725
Start somewhere. Take a step in a random direction. Keep doing this until
726
we hit a wall. Then, backtrack until we find a point where we can go in
729
This is essentially the "depth-first recursive backtracker" algorithm.
730
****************************************************************************/
732
static int choose_door (struct state *st);
733
static int backup (struct state *st);
875
736
create_maze (struct state *st) /* create a maze layout given the initialized maze */
877
register int i, newdoor = 0;
878
int logow = 1 + st->logo_width / st->grid_width;
879
int logoh = 1 + st->logo_height / st->grid_height;
881
/* Maybe we should make a bridge? */
882
if(st->bridge_p && st->logo_x >= 0 && logow>=3 && logoh>=3)
884
int bridge_dir, bridge_c;
886
bridge_dir = 1+random()%2;
890
bridge_c = st->logo_y+random()%(logoh-2)+1;
892
bridge_c = st->logo_y+random()%logoh;
897
bridge_c = st->logo_x+random()%(logow-2)+1;
899
bridge_c = st->logo_x+random()%logow;
904
for(i = st->logo_x; i < st->logo_x+logow; i++)
906
st->maze[i][bridge_c] &= ~DOOR_IN_TOP;
911
for(i = st->logo_y; i < st->logo_y+logoh; i++)
913
st->maze[bridge_c][i] &= ~DOOR_IN_TOP;
919
741
st->move_list[st->sqnum].x = st->cur_sq_x;
920
742
st->move_list[st->sqnum].y = st->cur_sq_y;
959
/* Mark the maze grid as having a wall at the given coordinate,
960
and draw that wall on the screen. */
962
build_wall(struct state *st, int i, int j, int dir)
964
/* Draw it on the screen. */
965
draw_wall(st, i, j, dir, st->gc);
966
/* Put it in the maze. */
970
st->maze[i][j] |= WALL_TOP;
972
st->maze[i][j-1] |= WALL_BOTTOM;
975
st->maze[i][j] |= WALL_RIGHT;
976
if(i<st->maze_size_x-1)
977
st->maze[i+1][j] |= WALL_LEFT;
980
st->maze[i][j] |= WALL_BOTTOM;
981
if(j<st->maze_size_y-1)
982
st->maze[i][j+1] |= WALL_TOP;
985
st->maze[i][j] |= WALL_LEFT;
987
st->maze[i-1][j] |= WALL_RIGHT;
1134
994
draw_wall(struct state *st, int i, int j, int dir, GC with_gc) /* draw a single wall */
1179
/* Actually build a wall. */
1181
build_wall(struct state *st, int i, int j, int dir)
1183
/* Draw it on the screen. */
1184
draw_wall(st, i, j, dir, st->gc);
1185
/* Put it in the maze. */
1189
st->maze[i][j] |= WALL_TOP;
1191
st->maze[i][j-1] |= WALL_BOTTOM;
1194
st->maze[i][j] |= WALL_RIGHT;
1195
if(i<st->maze_size_x-1)
1196
st->maze[i+1][j] |= WALL_LEFT;
1199
st->maze[i][j] |= WALL_BOTTOM;
1200
if(j<st->maze_size_y-1)
1201
st->maze[i][j+1] |= WALL_TOP;
1204
st->maze[i][j] |= WALL_LEFT;
1206
st->maze[i-1][j] |= WALL_RIGHT;
1211
/* Break out a wall. */
1214
break_wall(i, j, dir)
1217
/* Draw it on the screen. */
1218
draw_wall(i, j, dir, st->erase_gc);
1219
/* Put it in the maze. */
1223
st->maze[i][j] &= ~WALL_TOP;
1225
maze[i][j-1] &= ~WALL_BOTTOM;
1228
st->maze[i][j] &= ~WALL_RIGHT;
1229
if(i<st->maze_size_x-1)
1230
maze[i+1][j] &= ~WALL_LEFT;
1233
st->maze[i][j] &= ~WALL_BOTTOM;
1234
if(j<st->maze_size_y-1)
1235
maze[i][j+1] &= ~WALL_BOTTOM;
1238
st->maze[i][j] &= ~WALL_LEFT;
1240
maze[i-1][j] &= ~WALL_RIGHT;
1248
1041
draw_solid_square(struct state *st,
1249
int i, int j, /* draw a solid square in a square */
1250
1043
int dir, GC with_gc)
1547
enter_square (int n) /* move into a neighboring square */
1549
draw_solid_square( (int)st->path[n].x, (int)st->path[n].y,
1550
(int)st->path[n].dir, st->tgc);
1552
st->path[n+1].dir = -1;
1553
switch (st->path[n].dir) {
1554
case 0: st->path[n+1].x = st->path[n].x;
1555
st->path[n+1].y = st->path[n].y - 1;
1557
case 1: st->path[n+1].x = st->path[n].x + 1;
1558
st->path[n+1].y = st->path[n].y;
1560
case 2: st->path[n+1].x = st->path[n].x;
1561
st->path[n+1].y = st->path[n].y + 1;
1563
case 3: st->path[n+1].x = st->path[n].x - 1;
1564
st->path[n+1].y = st->path[n].y;
1572
* jmr additions for Jamie Zawinski's <jwz@jwz.org> screensaver stuff,
1573
* note that the code above this has probably been hacked about in some
1344
/****************************************************************************
1345
XScreenSaver boilerplate: resources, command line options, and main loop.
1346
****************************************************************************/
1577
1348
static const char *maze_defaults[] = {
1578
1349
".background: black",
1611
1381
{ "-surround-color", ".surroundColor",XrmoptionSepArg, 0 },
1612
1382
{ "-generator", ".generator", XrmoptionSepArg, 0 },
1613
1383
{ "-max-length", ".maxLength", XrmoptionSepArg, 0 },
1614
{ "-bridge", ".bridge", XrmoptionNoArg, "True" },
1615
{ "-no-bridge", ".bridge", XrmoptionNoArg, "False" },
1645
1410
st->post_solve_delay = get_integer_resource (st->dpy, "postDelay", "Integer");
1646
1411
generator = get_integer_resource(st->dpy, "generator", "Integer");
1647
1412
st->max_length = get_integer_resource(st->dpy, "maxLength", "Integer");
1648
st->bridge_p = get_boolean_resource(st->dpy, "bridge", "Boolean");
1649
1413
st->ignorant_p = get_boolean_resource(st->dpy, "ignorant", "Boolean");
1415
if (get_boolean_resource (st->dpy, "doFPS", "DoFPS"))
1417
/* Just guess, rather than loading and measuring the "fpsFont"... */
1418
st->fps_width = 210;
1419
st->fps_height = 62;
1651
1422
if (!size) st->ifrandom = 1;
1653
1424
if (size < 2) size = 7 + (random () % 30);
1669
1440
st->logo_gc = XCreateGC(st->dpy, st->window, 0, 0);
1670
1441
st->erase_gc = XCreateGC(st->dpy, st->window, 0, 0);
1673
gray = XCreateBitmapFromData (st->dpy,st->window,gray1_bits,gray1_width,gray1_height);
1676
1443
bg = get_pixel_resource (st->dpy, xgwa.colormap, "background","Background");
1677
1444
fg = get_pixel_resource (st->dpy, xgwa.colormap, "foreground","Foreground");
1678
1445
pfg = get_pixel_resource (st->dpy, xgwa.colormap, "liveColor", "Foreground");
1695
1462
XSetForeground (st->dpy, st->erase_gc, bg);
1696
1463
XSetBackground (st->dpy, st->erase_gc, bg);
1699
XSetStipple (st->dpy, st->cgc, gray);
1700
XSetFillStyle (st->dpy, st->cgc, FillOpaqueStippled);
1701
XSetStipple (st->dpy, st->sgc, gray);
1702
XSetFillStyle (st->dpy, st->sgc, FillOpaqueStippled);
1703
XSetStipple (st->dpy, st->ugc, gray);
1704
XSetFillStyle (st->dpy, st->ugc, FillOpaqueStippled);