source: trunk/third/xscreensaver/hacks/maze.c @ 20148

Revision 20148, 42.4 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20147, which included commits to RCS files with non-trunk default branches.
Line 
1/******************************************************************************
2 * [ maze ] ...
3 *
4 * modified:  [ 1-04-00 ]  Johannes Keukelaar <johannes@nada.kth.se>
5 *              Added -ignorant option (not the default) to remove knowlege
6 *              of the direction in which the exit lies.
7 *
8 * modified:  [ 6-28-98 ]  Zack Weinberg <zack@rabi.phys.columbia.edu>
9 *
10 *              Made the maze-solver somewhat more intelligent.  There are
11 *              three optimizations:
12 *
13 *              - Straight-line lookahead: the solver does not enter dead-end
14 *                corridors.  This is a win with all maze generators.
15 *
16 *              - First order direction choice: the solver knows where the
17 *                exit is in relation to itself, and will try paths leading in
18 *                that direction first. This is a major win on maze generator 1
19 *                which tends to offer direct routes to the exit.
20 *
21 *              - Dead region elimination: the solver already has a map of
22 *                all squares visited.  Whenever it starts to backtrack, it
23 *                consults this map and marks off all squares that cannot be
24 *                reached from the exit without crossing a square already
25 *                visited.  Those squares can never contribute to the path to
26 *                the exit, so it doesn't bother checking them.  This helps a
27 *                lot with maze generator 2 and somewhat less with generator 1.
28 *
29 *              Further improvements would require knowledge of the wall map
30 *              as well as the position of the exit and the squares visited.
31 *              I would consider that to be cheating.  Generator 0 makes
32 *              mazes which are remarkably difficult to solve mechanically --
33 *              even with these optimizations the solver generally must visit
34 *              at least two-thirds of the squares.  This is partially
35 *              because generator 0's mazes have longer paths to the exit.
36 *
37 * modified:  [ 4-10-97 ]  Johannes Keukelaar <johannes@nada.kth.se>
38 *              Added multiple maze creators. Robustified solver.
39 *              Added bridge option.
40 * modified:  [ 8-11-95 ] Ed James <james@mml.mmc.com>
41 *              added fill of dead-end box to solve_maze while loop.
42 * modified:  [ 3-7-93 ]  Jamie Zawinski <jwz@jwz.org>
43 *              added the XRoger logo, cleaned up resources, made
44 *              grid size a parameter.
45 * modified:  [ 3-3-93 ]  Jim Randell <jmr@mddjmr.fc.hp.com>
46 *              Added the colour stuff and integrated it with jwz's
47 *              screenhack stuff.  There's still some work that could
48 *              be done on this, particularly allowing a resource to
49 *              specify how big the squares are.
50 * modified:  [ 10-4-88 ]  Richard Hess    ...!uunet!cimshop!rhess 
51 *              [ Revised primary execution loop within main()...
52 *              [ Extended X event handler, check_events()...
53 * modified:  [ 1-29-88 ]  Dave Lemke      lemke@sun.com 
54 *              [ Hacked for X11...
55 *              [  Note the word "hacked" -- this is extremely ugly, but at
56 *              [   least it does the job.  NOT a good programming example
57 *              [   for X.
58 * original:  [ 6/21/85 ]  Martin Weiss    Sun Microsystems  [ SunView ]
59 *
60 ******************************************************************************
61 Copyright 1988 by Sun Microsystems, Inc. Mountain View, CA.
62 
63 All Rights Reserved
64 
65 Permission to use, copy, modify, and distribute this software and its
66 documentation for any purpose and without fee is hereby granted,
67 provided that the above copyright notice appear in all copies and that
68 both that copyright notice and this permission notice appear in
69 supporting documentation, and that the names of Sun or MIT not be
70 used in advertising or publicity pertaining to distribution of the
71 software without specific prior written permission. Sun and M.I.T.
72 make no representations about the suitability of this software for
73 any purpose. It is provided "as is" without any express or implied warranty.
74 
75 SUN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
76 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
77 PURPOSE. IN NO EVENT SHALL SUN BE LIABLE FOR ANY SPECIAL, INDIRECT
78 OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
79 OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
80 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
81 OR PERFORMANCE OF THIS SOFTWARE.
82 *****************************************************************************/
83
84#include "screenhack.h"
85#include "erase.h"
86
87#define XSCREENSAVER_LOGO
88
89static int solve_delay, pre_solve_delay, post_solve_delay;
90
91#include  <stdio.h>
92#include  <X11/Xlib.h>
93#include  <X11/Xutil.h>
94
95/* #include  <X11/bitmaps/gray1> */
96#define gray1_width  2
97#define gray1_height 2
98static char gray1_bits[] = { 0x01, 0x02 };
99
100
101#define MAX_MAZE_SIZE_X 1000
102#define MAX_MAZE_SIZE_Y 1000
103
104#define MOVE_LIST_SIZE  (MAX_MAZE_SIZE_X * MAX_MAZE_SIZE_Y)
105
106#define NOT_DEAD        0x8000
107#define SOLVER_VISIT    0x4000
108#define START_SQUARE    0x2000
109#define END_SQUARE      0x1000
110
111#define WALL_TOP        0x8
112#define WALL_RIGHT      0x4
113#define WALL_BOTTOM     0x2
114#define WALL_LEFT       0x1
115#define WALL_ANY        0xF
116
117#define DOOR_IN_TOP     0x800
118#define DOOR_IN_RIGHT   0x400
119#define DOOR_IN_BOTTOM  0x200
120#define DOOR_IN_LEFT    0x100
121#define DOOR_IN_ANY     0xF00
122
123#define DOOR_OUT_TOP    0x80
124#define DOOR_OUT_RIGHT  0x40
125#define DOOR_OUT_BOTTOM 0x20
126#define DOOR_OUT_LEFT   0x10
127
128
129#define border_x        (0)
130#define border_y        (0)
131
132#define get_random(x)   (random() % (x))
133
134
135static int logo_x, logo_y;
136
137#ifdef XSCREENSAVER_LOGO
138# define logo_width  54
139# define logo_height 54
140#else
141# include  <X11/bitmaps/xlogo64>
142# define logo_width xlogo64_width
143# define logo_height xlogo64_height
144# define logo_bits xlogo64_bits
145#endif
146
147static unsigned short maze[MAX_MAZE_SIZE_X][MAX_MAZE_SIZE_Y];
148
149static struct {
150  unsigned short x;
151  unsigned short y;
152  unsigned char dir, ways;
153} move_list[MOVE_LIST_SIZE], save_path[MOVE_LIST_SIZE], path[MOVE_LIST_SIZE];
154
155static int maze_size_x, maze_size_y;
156static int sqnum, cur_sq_x, cur_sq_y, path_length;
157static int start_x, start_y, start_dir, end_x, end_y, end_dir;
158static int grid_width, grid_height;
159static int bw;
160
161static Display  *dpy;
162static Window   win;
163static GC       gc, cgc, tgc, sgc, ugc, logo_gc, erase_gc;
164static Pixmap   logo_map;
165
166static int      x = 0, y = 0, restart = 0, stop = 1, state = 1, max_length;
167static int      sync_p, bridge_p, ignorant_p;
168
169static int
170check_events (void)                        /* X event handler [ rhess ] */
171{
172  XEvent        e;
173
174  if (XPending(dpy)) {
175    XNextEvent(dpy, &e);
176    switch (e.type) {
177
178    case ButtonPress:
179      switch (e.xbutton.button) {
180      case 3:
181        exit (0);
182        break;
183      case 2:
184        stop = !stop ;
185        if (state == 5) state = 4 ;
186        else {
187          restart = 1;
188          stop = 0;
189        }
190        break;
191      default:
192        restart = 1 ;
193        stop = 0 ;
194        break;
195      }
196      break;
197
198    case ConfigureNotify:
199      restart = 1;
200      break;
201    case UnmapNotify:
202      stop = 1;
203      XClearWindow (dpy, win);
204      XSync (dpy, False);
205      break;
206    case Expose:
207      restart = 1;
208      break;
209    default:
210      screenhack_handle_event(dpy, &e);
211      break;
212    }
213    return(1);
214  }
215  return(0);
216}
217
218
219static void
220set_maze_sizes (int width, int height)
221{
222  maze_size_x = width / grid_width;
223  maze_size_y = height / grid_height;
224}
225
226
227static void
228initialize_maze (void) /* draw the surrounding wall and start/end squares */
229{
230  register int i, j, wall;
231  int logow = 1 + logo_width / grid_width;
232  int logoh = 1 + logo_height / grid_height;
233 
234  /* initialize all squares */
235  for ( i=0; i<maze_size_x; i++) {
236    for ( j=0; j<maze_size_y; j++) {
237      maze[i][j] = 0;
238    }
239  }
240 
241  /* top wall */
242  for ( i=0; i<maze_size_x; i++ ) {
243    maze[i][0] |= WALL_TOP;
244  }
245 
246  /* right wall */
247  for ( j=0; j<maze_size_y; j++ ) {
248    maze[maze_size_x-1][j] |= WALL_RIGHT;
249  }
250 
251  /* bottom wall */
252  for ( i=0; i<maze_size_x; i++ ) {
253    maze[i][maze_size_y-1] |= WALL_BOTTOM;
254  }
255 
256  /* left wall */
257  for ( j=0; j<maze_size_y; j++ ) {
258    maze[0][j] |= WALL_LEFT;
259  }
260 
261  /* set start square */
262  wall = get_random(4);
263  switch (wall) {
264  case 0:       
265    i = get_random(maze_size_x);
266    j = 0;
267    break;
268  case 1:       
269    i = maze_size_x - 1;
270    j = get_random(maze_size_y);
271    break;
272  case 2:       
273    i = get_random(maze_size_x);
274    j = maze_size_y - 1;
275    break;
276  case 3:       
277    i = 0;
278    j = get_random(maze_size_y);
279    break;
280  }
281  maze[i][j] |= START_SQUARE;
282  maze[i][j] |= ( DOOR_IN_TOP >> wall );
283  maze[i][j] &= ~( WALL_TOP >> wall );
284  cur_sq_x = i;
285  cur_sq_y = j;
286  start_x = i;
287  start_y = j;
288  start_dir = wall;
289  sqnum = 0;
290 
291  /* set end square */
292  wall = (wall + 2)%4;
293  switch (wall) {
294  case 0:
295    i = get_random(maze_size_x);
296    j = 0;
297    break;
298  case 1:
299    i = maze_size_x - 1;
300    j = get_random(maze_size_y);
301    break;
302  case 2:
303    i = get_random(maze_size_x);
304    j = maze_size_y - 1;
305    break;
306  case 3:
307    i = 0;
308    j = get_random(maze_size_y);
309    break;
310  }
311  maze[i][j] |= END_SQUARE;
312  maze[i][j] |= ( DOOR_OUT_TOP >> wall );
313  maze[i][j] &= ~( WALL_TOP >> wall );
314  end_x = i;
315  end_y = j;
316  end_dir = wall;
317 
318  /* set logo */
319  if ((maze_size_x-logow >= 6) && (maze_size_y-logoh >= 6))
320    {
321      /* not closer than 3 grid units from a wall */
322      logo_x = get_random (maze_size_x - logow - 5) + 3;
323      logo_y = get_random (maze_size_y - logoh - 5) + 3;
324      for (i=0; i<logow; i++)
325        for (j=0; j<logoh; j++)
326          maze[logo_x + i][logo_y + j] |= DOOR_IN_TOP;
327    }
328  else
329    logo_y = logo_x = -1;
330}
331
332static int choose_door (void);
333static int backup (void);
334static void draw_wall (int, int, int, GC);
335static void draw_solid_square (int, int, int, GC);
336/*static void enter_square (int);*/
337static void build_wall (int, int, int);
338/*static void break_wall (int, int, int);*/
339
340static void join_sets(int, int);
341
342/* For set_create_maze. */
343/* The sets that our squares are in. */
344static int *sets = 0;
345/* The `list' of hedges. */
346static int *hedges = 0;
347
348#define DEBUG_SETS 0
349
350/* Initialise the sets. */
351static void
352init_sets(void)
353{
354  int i, t, r, x, y;
355
356  if(sets)
357    free(sets);
358  sets = (int *)malloc(maze_size_x*maze_size_y*sizeof(int));
359  if(!sets)
360    abort();
361  for(i = 0; i < maze_size_x*maze_size_y; i++)
362    {
363      sets[i] = i;
364    }
365 
366  if(hedges)
367    free(hedges);
368  hedges = (int *)malloc(maze_size_x*maze_size_y*2*sizeof(int));
369  if(!hedges)
370    abort();
371  for(i = 0; i < maze_size_x*maze_size_y*2; i++)
372    {
373      hedges[i] = i;
374    }
375  /* Mask out outside walls. */
376  for(i = 0; i < maze_size_y; i++)
377    {
378      hedges[2*((maze_size_x)*i+maze_size_x-1)+1] = -1;
379    }
380  for(i = 0; i < maze_size_x; i++)
381    {
382      hedges[2*((maze_size_y-1)*maze_size_x+i)] = -1;
383    }
384  /* Mask out a possible logo. */
385  if(logo_x!=-1)
386    {
387      int logow = 1 + logo_width / grid_width;
388      int logoh = 1 + logo_height / grid_height;
389      int bridge_dir, bridge_c;
390
391      if(bridge_p && logoh>=3 && logow>=3)
392        {
393          bridge_dir = 1+random()%2;
394          if(bridge_dir==1)
395            {
396              bridge_c = logo_y+random()%(logoh-2)+1;
397            }
398          else
399            {
400              bridge_c = logo_x+random()%(logow-2)+1;
401            }
402        }
403      else
404        {
405          bridge_dir = 0;
406          bridge_c = -1;
407        }
408
409      for(x = logo_x; x < logo_x+logow; x++)
410        for(y = logo_y; y < logo_y+logoh; y++)
411          {
412            /* I should check for the bridge here, except that I join the
413             * bridge together below.
414             */
415            hedges[2*(x+maze_size_x*y)+1] = -1;
416            hedges[2*(x+maze_size_x*y)] = -1;
417          }
418      for(x = logo_x; x < logo_x+logow; x++)
419        {
420          if(!(bridge_dir==2 && x==bridge_c))
421            {
422              build_wall(x, logo_y, 0);
423              build_wall(x, logo_y+logoh, 0);
424            }
425          hedges[2*(x+maze_size_x*(logo_y-1))] = -1;
426          if(bridge_dir==1)
427            {
428              build_wall(x, bridge_c, 0);
429              build_wall(x, bridge_c, 2);
430            }
431        }
432      for(y = logo_y; y < logo_y+logoh; y++)
433        {
434          if(!(bridge_dir==1 && y==bridge_c))
435            {
436              build_wall(logo_x, y, 3);
437              build_wall(logo_x+logow, y, 3);
438            }
439          hedges[2*(logo_x-1+maze_size_x*y)+1] = -1;
440          if(bridge_dir==2)
441            {
442              build_wall(bridge_c, y, 1);
443              build_wall(bridge_c, y, 3);
444            }
445        }
446      /* Join the whole bridge together. */
447      if(bridge_p)
448        {
449          if(bridge_dir==1)
450            {
451              x = logo_x-1;
452              y = bridge_c;
453              for(i = logo_x; i < logo_x+logow+1; i++)
454                join_sets(x+y*maze_size_x, i+y*maze_size_x);
455            }
456          else
457            {
458              y = logo_y-1;
459              x = bridge_c;
460              for(i = logo_y; i < logo_y+logoh+1; i++)
461                join_sets(x+y*maze_size_x, x+i*maze_size_x);
462            }
463        }
464    }
465
466  for(i = 0; i < maze_size_x*maze_size_y*2; i++)
467    {
468      t = hedges[i];
469      r = random()%(maze_size_x*maze_size_y*2);
470      hedges[i] = hedges[r];
471      hedges[r] = t;
472    }
473}
474
475/* Get the representative of a set. */
476static int
477get_set(int num)
478{
479  int s;
480
481  if(sets[num]==num)
482    return num;
483  else
484    {
485      s = get_set(sets[num]);
486      sets[num] = s;
487      return s;
488    }
489}
490
491/* Join two sets together. */
492static void
493join_sets(num1, num2)
494     int num1, num2;
495{
496  int s1, s2;
497
498  s1 = get_set(num1);
499  s2 = get_set(num2);
500 
501  if(s1<s2)
502    sets[s2] = s1;
503  else
504    sets[s1] = s2;
505}
506
507/* Exitialise the sets. */
508static void
509exit_sets(void)
510{
511  if(hedges)
512    free(hedges);
513  hedges = 0;
514  if(sets)
515    free(sets);
516  sets = 0;
517}
518
519#if DEBUG_SETS
520/* Temporary hack. */
521static void
522show_set(int num, GC gc)
523{
524  int x, y, set;
525
526  set = get_set(num);
527
528  for(x = 0; x < maze_size_x; x++)
529    for(y = 0; y < maze_size_y; y++)
530      {
531        if(get_set(x+y*maze_size_x)==set)
532          {
533            XFillRectangle(dpy, win, gc,  border_x + bw + grid_width * x,
534                         border_y + bw + grid_height * y,
535                         grid_width-2*bw , grid_height-2*bw);
536          }
537      }
538}
539#endif
540
541/* Second alternative maze creator: Put each square in the maze in a
542 * separate set. Also, make a list of all the hedges. Randomize that list.
543 * Walk through the list. If, for a certain hedge, the two squares on both
544 * sides of it are in different sets, union the sets and remove the hedge.
545 * Continue until all hedges have been processed or only one set remains.
546 */
547static void
548set_create_maze(void)
549{
550  int i, h, x, y, dir, v, w;
551#if DEBUG_SETS
552  int cont = 0;
553  char c;
554#endif
555
556  /* Do almost all the setup. */
557  init_sets();
558
559  /* Start running through the hedges. */
560  for(i = 0; i < 2*maze_size_x*maze_size_y; i++)
561    {
562      h = hedges[i];
563
564      /* This one is in the logo or outside border. */
565      if(h==-1)
566        continue;
567
568      dir = h%2?1:2;
569      x = (h>>1)%maze_size_x;
570      y = (h>>1)/maze_size_x;
571
572      v = x;
573      w = y;
574      switch(dir)
575        {
576        case 1:
577          v++;
578          break;
579        case 2:
580          w++;
581          break;
582        }
583
584#if DEBUG_SETS
585      show_set(x+y*maze_size_x, logo_gc);
586      show_set(v+w*maze_size_x, tgc);
587#endif
588      if(get_set(x+y*maze_size_x)!=get_set(v+w*maze_size_x))
589        {
590#if DEBUG_SETS
591          printf("Join!");
592#endif
593          join_sets(x+y*maze_size_x, v+w*maze_size_x);
594          /* Don't draw the wall. */
595        }
596      else
597        {
598#if DEBUG_SETS
599          printf("Build.");
600#endif
601          /* Don't join the sets. */
602          build_wall(x, y, dir);
603        }
604#if DEBUG_SETS
605      if(!cont)
606        {
607          XSync(dpy, False);
608          c = getchar();
609          if(c=='c')
610            cont = 1;
611        }
612      show_set(x+y*maze_size_x, erase_gc);
613      show_set(v+w*maze_size_x, erase_gc);
614#endif
615    }
616
617  /* Free some memory. */
618  exit_sets();
619}
620
621/* First alternative maze creator: Pick a random, empty corner in the maze.
622 * Pick a random direction. Draw a wall in that direction, from that corner
623 * until we hit a wall. Option: Only draw the wall if it's going to be
624 * shorter than a certain length. Otherwise we get lots of long walls.
625 */
626static void
627alt_create_maze(void)
628{
629  char *corners;
630  int *c_idx;
631  int i, j, height, width, open_corners, k, dir, x, y;
632
633  height = maze_size_y+1;
634  width = maze_size_x+1;
635
636  /* Allocate and clear some mem. */
637  corners = (char *)calloc(height*width, 1);
638  if(!corners)
639    return;
640
641  /* Set up the indexing array. */
642  c_idx = (int *)malloc(sizeof(int)*height*width);
643  if(!c_idx)
644    {
645      free(corners);
646      return;
647    }
648  for(i = 0; i < height*width; i++)
649    c_idx[i] = i;
650  for(i = 0; i < height*width; i++)
651    {
652      j = c_idx[i];
653      k = random()%(height*width);
654      c_idx[i] = c_idx[k];
655      c_idx[k] = j;
656    }
657
658  /* Set up some initial walls. */
659  /* Outside walls. */
660  for(i = 0; i < width; i++)
661    {
662      corners[i] = 1;
663      corners[i+width*(height-1)] = 1;
664    }
665  for(i = 0; i < height; i++)
666    {
667      corners[i*width] = 1;
668      corners[i*width+width-1] = 1;
669    }
670  /* Walls around logo. In fact, inside the logo, too. */
671  /* Also draw the walls. */
672  if(logo_x!=-1)
673    {
674      int logow = 1 + logo_width / grid_width;
675      int logoh = 1 + logo_height / grid_height;
676      int bridge_dir, bridge_c;
677
678      if(bridge_p && logoh>=3 && logow>=3)
679        {
680          bridge_dir = 1+random()%2;
681          if(bridge_dir==1)
682            {
683              bridge_c = logo_y+random()%(logoh-2)+1;
684            }
685          else
686            {
687              bridge_c = logo_x+random()%(logow-2)+1;
688            }
689        }
690      else
691        {
692          bridge_dir = 0;
693          bridge_c = -1;
694        }
695      for(i = logo_x; i <= logo_x + logow; i++)
696        {
697          for(j = logo_y; j <= logo_y + logoh; j++)
698            {
699              corners[i+width*j] = 1;
700            }
701        }
702      for(x = logo_x; x < logo_x+logow; x++)
703        {
704          if(!(bridge_dir==2 && x==bridge_c))
705            {
706              build_wall(x, logo_y, 0);
707              build_wall(x, logo_y+logoh, 0);
708            }
709          if(bridge_dir==1)
710            {
711              build_wall(x, bridge_c, 0);
712              build_wall(x, bridge_c, 2);
713            }
714        }
715      for(y = logo_y; y < logo_y+logoh; y++)
716        {
717          if(!(bridge_dir==1 && y==bridge_c))
718            {
719              build_wall(logo_x, y, 3);
720              build_wall(logo_x+logow, y, 3);
721            }
722          if(bridge_dir==2)
723            {
724              build_wall(bridge_c, y, 1);
725              build_wall(bridge_c, y, 3);
726            }
727        }
728      /* Connect one wall of the logo with an outside wall. */
729      if(bridge_p)
730        dir = (bridge_dir+1)%4;
731      else
732        dir = random()%4;
733      switch(dir)
734        {
735        case 0:
736          x = logo_x+(random()%(logow+1));
737          y = logo_y;
738          break;
739        case 1:
740          x = logo_x+logow;
741          y = logo_y+(random()%(logoh+1));
742          break;
743        case 2:
744          x = logo_x+(random()%(logow+1));
745          y = logo_y+logoh;
746          break;
747        case 3:
748          x = logo_x;
749          y = logo_y+(random()%(logoh+1));
750          break;
751        }
752      do
753        {
754          corners[x+width*y] = 1;
755          switch(dir)
756            {
757            case 0:
758              build_wall(x-1, y-1, 1);
759              y--;
760              break;
761            case 1:
762              build_wall(x, y, 0);
763              x++;
764              break;
765            case 2:
766              build_wall(x, y, 3);
767              y++;
768              break;
769            case 3:
770              build_wall(x-1, y-1, 2);
771              x--;
772              break;     
773            }
774        }
775      while(!corners[x+width*y]);
776      if(bridge_p)
777        {
778          dir = (dir+2)%4;
779          switch(dir)
780            {
781            case 0:
782              x = logo_x+(random()%(logow+1));
783              y = logo_y;
784              break;
785            case 1:
786              x = logo_x+logow;
787              y = logo_y+(random()%(logoh+1));
788              break;
789            case 2:
790              x = logo_x+(random()%(logow+1));
791              y = logo_y+logoh;
792              break;
793            case 3:
794              x = logo_x;
795              y = logo_y+(random()%(logoh+1));
796              break;
797            }
798          do
799            {
800              corners[x+width*y] = 1;
801              switch(dir)
802                {
803                case 0:
804                  build_wall(x-1, y-1, 1);
805                  y--;
806                  break;
807                case 1:
808                  build_wall(x, y, 0);
809                  x++;
810                  break;
811                case 2:
812                  build_wall(x, y, 3);
813                  y++;
814                  break;
815                case 3:
816                  build_wall(x-1, y-1, 2);
817                  x--;
818                  break;         
819                }
820            }
821          while(!corners[x+width*y]);
822        }
823    }
824
825  /* Count open gridpoints. */
826  open_corners = 0;
827  for(i = 0; i < width; i++)
828    for(j = 0; j < height; j++)
829      if(!corners[i+width*j])
830        open_corners++;
831
832  /* Now do actual maze generation. */
833  while(open_corners>0)
834    {
835      for(i = 0; i < width*height; i++)
836        {
837          if(!corners[c_idx[i]])
838            {
839              x = c_idx[i]%width;
840              y = c_idx[i]/width;
841              /* Choose a random direction. */
842              dir = random()%4;
843             
844              k = 0;
845              /* Measure the length of the wall we'd draw. */
846              while(!corners[x+width*y])
847                {
848                  k++;
849                  switch(dir)
850                    {
851                    case 0:
852                      y--;
853                      break;
854                    case 1:
855                      x++;
856                      break;
857                    case 2:
858                      y++;
859                      break;
860                    case 3:
861                      x--;
862                      break;
863                    }
864                }
865             
866              if(k<=max_length)
867                {
868                  x = c_idx[i]%width;
869                  y = c_idx[i]/width;
870                 
871                  /* Draw a wall until we hit something. */
872                  while(!corners[x+width*y])
873                    {
874                      open_corners--;
875                      corners[x+width*y] = 1;
876                      switch(dir)
877                        {
878                        case 0:
879                          build_wall(x-1, y-1, 1);
880                          y--;
881                          break;
882                        case 1:
883                          build_wall(x, y, 0);
884                          x++;
885                          break;
886                        case 2:
887                          build_wall(x, y, 3);
888                          y++;
889                          break;
890                        case 3:
891                          build_wall(x-1, y-1, 2);
892                          x--;
893                          break;
894                        }
895                    }
896                }
897            }
898        }
899    }
900
901  /* Free some memory we used. */
902  free(corners);
903  free(c_idx);
904}
905
906/* The original maze creator. Start somewhere. Take a step in a random
907 * direction. Keep doing this until we hit a wall. Then, backtrack until
908 * we find a point where we can go in another direction.
909 */
910static void
911create_maze (void)    /* create a maze layout given the initialized maze */
912{
913  register int i, newdoor = 0;
914  int logow = 1 + logo_width / grid_width;
915  int logoh = 1 + logo_height / grid_height;
916 
917  /* Maybe we should make a bridge? */
918  if(bridge_p && logo_x>=0 && logow>=3 && logoh>=3)
919    {
920      int bridge_dir, bridge_c;
921
922      bridge_dir = 1+random()%2;
923      if(bridge_dir==1)
924        {
925          if(logoh>=3)
926            bridge_c = logo_y+random()%(logoh-2)+1;
927          else
928            bridge_c = logo_y+random()%logoh;
929        }
930      else
931        {
932          if(logow>=3)
933            bridge_c = logo_x+random()%(logow-2)+1;
934          else
935            bridge_c = logo_x+random()%logow;
936        }
937
938      if(bridge_dir==1)
939        {
940          for(i = logo_x; i < logo_x+logow; i++)
941            {
942              maze[i][bridge_c] &= ~DOOR_IN_TOP;
943            }
944        }
945      else
946        {
947          for(i = logo_y; i < logo_y+logoh; i++)
948            {
949              maze[bridge_c][i] &= ~DOOR_IN_TOP;
950            }
951        }
952    }
953
954  do {
955    move_list[sqnum].x = cur_sq_x;
956    move_list[sqnum].y = cur_sq_y;
957    move_list[sqnum].dir = newdoor;
958    while ( ( newdoor = choose_door() ) == -1 ) { /* pick a door */
959      if ( backup() == -1 ) { /* no more doors ... backup */
960        return; /* done ... return */
961      }
962    }
963   
964    /* mark the out door */
965    maze[cur_sq_x][cur_sq_y] |= ( DOOR_OUT_TOP >> newdoor );
966   
967    switch (newdoor) {
968    case 0: cur_sq_y--;
969      break;
970    case 1: cur_sq_x++;
971      break;
972    case 2: cur_sq_y++;
973      break;
974    case 3: cur_sq_x--;
975      break;
976    }
977    sqnum++;
978   
979    /* mark the in door */
980    maze[cur_sq_x][cur_sq_y] |= ( DOOR_IN_TOP >> ((newdoor+2)%4) );
981   
982    /* if end square set path length and save path */
983    if ( maze[cur_sq_x][cur_sq_y] & END_SQUARE ) {
984      path_length = sqnum;
985      for ( i=0; i<path_length; i++) {
986        save_path[i].x = move_list[i].x;
987        save_path[i].y = move_list[i].y;
988        save_path[i].dir = move_list[i].dir;
989      }
990    }
991   
992  } while (1);
993 
994}
995
996
997static int
998choose_door (void)                                   /* pick a new path */
999{
1000  int candidates[3];
1001  register int num_candidates;
1002 
1003  num_candidates = 0;
1004 
1005  /* top wall */
1006  if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_TOP )
1007    goto rightwall;
1008  if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_TOP )
1009    goto rightwall;
1010  if ( maze[cur_sq_x][cur_sq_y] & WALL_TOP )
1011    goto rightwall;
1012  if ( maze[cur_sq_x][cur_sq_y - 1] & DOOR_IN_ANY ) {
1013    maze[cur_sq_x][cur_sq_y] |= WALL_TOP;
1014    maze[cur_sq_x][cur_sq_y - 1] |= WALL_BOTTOM;
1015    draw_wall(cur_sq_x, cur_sq_y, 0, gc);
1016    goto rightwall;
1017  }
1018  candidates[num_candidates++] = 0;
1019 
1020 rightwall:
1021  /* right wall */
1022  if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_RIGHT )
1023    goto bottomwall;
1024  if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_RIGHT )
1025    goto bottomwall;
1026  if ( maze[cur_sq_x][cur_sq_y] & WALL_RIGHT )
1027    goto bottomwall;
1028  if ( maze[cur_sq_x + 1][cur_sq_y] & DOOR_IN_ANY ) {
1029    maze[cur_sq_x][cur_sq_y] |= WALL_RIGHT;
1030    maze[cur_sq_x + 1][cur_sq_y] |= WALL_LEFT;
1031    draw_wall(cur_sq_x, cur_sq_y, 1, gc);
1032    goto bottomwall;
1033  }
1034  candidates[num_candidates++] = 1;
1035 
1036 bottomwall:
1037  /* bottom wall */
1038  if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_BOTTOM )
1039    goto leftwall;
1040  if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_BOTTOM )
1041    goto leftwall;
1042  if ( maze[cur_sq_x][cur_sq_y] & WALL_BOTTOM )
1043    goto leftwall;
1044  if ( maze[cur_sq_x][cur_sq_y + 1] & DOOR_IN_ANY ) {
1045    maze[cur_sq_x][cur_sq_y] |= WALL_BOTTOM;
1046    maze[cur_sq_x][cur_sq_y + 1] |= WALL_TOP;
1047    draw_wall(cur_sq_x, cur_sq_y, 2, gc);
1048    goto leftwall;
1049  }
1050  candidates[num_candidates++] = 2;
1051 
1052 leftwall:
1053  /* left wall */
1054  if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_LEFT )
1055    goto donewall;
1056  if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_LEFT )
1057    goto donewall;
1058  if ( maze[cur_sq_x][cur_sq_y] & WALL_LEFT )
1059    goto donewall;
1060  if ( maze[cur_sq_x - 1][cur_sq_y] & DOOR_IN_ANY ) {
1061    maze[cur_sq_x][cur_sq_y] |= WALL_LEFT;
1062    maze[cur_sq_x - 1][cur_sq_y] |= WALL_RIGHT;
1063    draw_wall(cur_sq_x, cur_sq_y, 3, gc);
1064    goto donewall;
1065  }
1066  candidates[num_candidates++] = 3;
1067 
1068 donewall:
1069  if (num_candidates == 0)
1070    return ( -1 );
1071  if (num_candidates == 1)
1072    return ( candidates[0] );
1073  return ( candidates[ get_random(num_candidates) ] );
1074 
1075}
1076
1077
1078static int
1079backup (void)                                          /* back up a move */
1080{
1081  sqnum--;
1082  cur_sq_x = move_list[sqnum].x;
1083  cur_sq_y = move_list[sqnum].y;
1084  return ( sqnum );
1085}
1086
1087
1088static void
1089draw_maze_border (void)                         /* draw the maze outline */
1090{
1091  register int i, j;
1092 
1093 
1094  for ( i=0; i<maze_size_x; i++) {
1095    if ( maze[i][0] & WALL_TOP ) {
1096      XDrawLine(dpy, win, gc,
1097                border_x + grid_width * i,
1098                border_y,
1099                border_x + grid_width * (i+1) - 1,
1100                border_y);
1101    }
1102    if ((maze[i][maze_size_y - 1] & WALL_BOTTOM)) {
1103      XDrawLine(dpy, win, gc,
1104                border_x + grid_width * i,
1105                border_y + grid_height * (maze_size_y) - 1,
1106                border_x + grid_width * (i+1) - 1,
1107                border_y + grid_height * (maze_size_y) - 1);
1108    }
1109  }
1110  for ( j=0; j<maze_size_y; j++) {
1111    if ( maze[maze_size_x - 1][j] & WALL_RIGHT ) {
1112      XDrawLine(dpy, win, gc,
1113                border_x + grid_width * maze_size_x - 1,
1114                border_y + grid_height * j,
1115                border_x + grid_width * maze_size_x - 1,
1116                border_y + grid_height * (j+1) - 1);
1117    }
1118    if ( maze[0][j] & WALL_LEFT ) {
1119      XDrawLine(dpy, win, gc,
1120                border_x,
1121                border_y + grid_height * j,
1122                border_x,
1123                border_y + grid_height * (j+1) - 1);
1124    }
1125  }
1126 
1127  if (logo_x != -1)
1128    {
1129      Window r;
1130      int x, y;
1131      unsigned int w, h, bw, d;
1132
1133      /* round up to grid size */
1134      int ww = ((logo_width  / grid_width) + 1)  * grid_width;
1135      int hh = ((logo_height / grid_height) + 1) * grid_height;
1136
1137      XGetGeometry (dpy, logo_map, &r, &x, &y, &w, &h, &bw, &d);
1138
1139# ifdef XSCREENSAVER_LOGO
1140      /* kludge: if the logo "hole" is around the same size as the logo,
1141         don't center it (since the xscreensaver logo image is a little
1142         off center...  But do center if if the hole/gridsize is large. */
1143      if (ww < logo_width  + 5) ww = w;
1144      if (hh < logo_height + 5) hh = h;
1145# endif /* XSCREENSAVER_LOGO */
1146
1147      if (d == 1)
1148        XCopyPlane (dpy, logo_map, win, logo_gc,
1149                    0, 0, w, h,
1150                    border_x + 3 + grid_width  * logo_x + ((ww - w) / 2),
1151                    border_y + 3 + grid_height * logo_y + ((hh - h) / 2),
1152                    1);
1153      else
1154        XCopyArea (dpy, logo_map, win, logo_gc,
1155                   0, 0, w, h,
1156                   border_x + 3 + grid_width  * logo_x + ((ww - w) / 2),
1157                   border_y + 3 + grid_height * logo_y + ((hh - h) / 2));
1158    }
1159  draw_solid_square (start_x, start_y, WALL_TOP >> start_dir, tgc);
1160  draw_solid_square (end_x, end_y, WALL_TOP >> end_dir, tgc);
1161}
1162
1163
1164static void
1165draw_wall(int i, int j, int dir, GC gc)                /* draw a single wall */
1166{
1167  switch (dir) {
1168  case 0:
1169    XDrawLine(dpy, win, gc,
1170              border_x + grid_width * i,
1171              border_y + grid_height * j,
1172              border_x + grid_width * (i+1),
1173              border_y + grid_height * j);
1174    break;
1175  case 1:
1176    XDrawLine(dpy, win, gc,
1177              border_x + grid_width * (i+1),
1178              border_y + grid_height * j,
1179              border_x + grid_width * (i+1),
1180              border_y + grid_height * (j+1));
1181    break;
1182  case 2:
1183    XDrawLine(dpy, win, gc,
1184              border_x + grid_width * i,
1185              border_y + grid_height * (j+1),
1186              border_x + grid_width * (i+1),
1187              border_y + grid_height * (j+1));
1188    break;
1189  case 3:
1190    XDrawLine(dpy, win, gc,
1191              border_x + grid_width * i,
1192              border_y + grid_height * j,
1193              border_x + grid_width * i,
1194              border_y + grid_height * (j+1));
1195    break;
1196  }
1197  if(sync_p)
1198    XSync(dpy, False);
1199}
1200
1201/* Actually build a wall. */
1202static void
1203build_wall(i, j, dir)
1204     int i, j, dir;
1205{
1206  /* Draw it on the screen. */
1207  draw_wall(i, j, dir, gc);
1208  /* Put it in the maze. */
1209  switch(dir)
1210    {
1211    case 0:
1212      maze[i][j] |= WALL_TOP;
1213      if(j>0)
1214        maze[i][j-1] |= WALL_BOTTOM;
1215      break;
1216    case 1:
1217      maze[i][j] |= WALL_RIGHT;
1218      if(i<maze_size_x-1)
1219        maze[i+1][j] |= WALL_LEFT;
1220      break;
1221    case 2:
1222      maze[i][j] |= WALL_BOTTOM;
1223      if(j<maze_size_y-1)
1224        maze[i][j+1] |= WALL_TOP;
1225      break;
1226    case 3:
1227      maze[i][j] |= WALL_LEFT;
1228      if(i>0)
1229        maze[i-1][j] |= WALL_RIGHT;
1230      break;
1231    }
1232}
1233
1234/* Break out a wall. */
1235#if 0
1236static void
1237break_wall(i, j, dir)
1238     int i, j, dir;
1239{
1240  /* Draw it on the screen. */
1241  draw_wall(i, j, dir, erase_gc);
1242  /* Put it in the maze. */
1243  switch(dir)
1244    {
1245    case 0:
1246      maze[i][j] &= ~WALL_TOP;
1247      if(j>0)
1248        maze[i][j-1] &= ~WALL_BOTTOM;
1249      break;
1250    case 1:
1251      maze[i][j] &= ~WALL_RIGHT;
1252      if(i<maze_size_x-1)
1253        maze[i+1][j] &= ~WALL_LEFT;
1254      break;
1255    case 2:
1256      maze[i][j] &= ~WALL_BOTTOM;
1257      if(j<maze_size_y-1)
1258        maze[i][j+1] &= ~WALL_BOTTOM;
1259      break;
1260    case 3:
1261      maze[i][j] &= ~WALL_LEFT;
1262      if(i>0)
1263        maze[i-1][j] &= ~WALL_RIGHT;
1264      break;
1265    }
1266}
1267#endif /* 0 */
1268
1269
1270static void
1271draw_solid_square(int i, int j,          /* draw a solid square in a square */
1272                  int dir, GC gc)
1273{
1274  switch (dir) {
1275  case WALL_TOP:
1276      XFillRectangle(dpy, win, gc,
1277                     border_x + bw+(bw==0?1:0) + grid_width * i,
1278                     border_y - bw-(bw==0?1:0) + grid_height * j,
1279                     grid_width - (bw+bw+(bw==0?1:0)), grid_height);
1280      break;
1281  case WALL_RIGHT:
1282      XFillRectangle(dpy, win, gc,
1283                     border_x + bw+(bw==0?1:0) + grid_width * i,
1284                     border_y + bw+(bw==0?1:0) + grid_height * j,
1285                     grid_width, grid_height - (bw+bw+(bw==0?1:0)));
1286      break;
1287  case WALL_BOTTOM:
1288      XFillRectangle(dpy, win, gc,
1289                     border_x + bw+(bw==0?1:0) + grid_width * i,
1290                     border_y + bw+(bw==0?1:0) + grid_height * j,
1291                     grid_width - (bw+bw+(bw==0?1:0)), grid_height);
1292      break;
1293  case WALL_LEFT:
1294      XFillRectangle(dpy, win, gc,
1295                     border_x - bw-(bw==0?1:0) + grid_width * i,
1296                     border_y + bw+(bw==0?1:0) + grid_height * j,
1297                     grid_width, grid_height - (bw+bw+(bw==0?1:0)));
1298      break;
1299  }
1300  XSync (dpy, False);
1301}
1302
1303int
1304longdeadend_p(int x1, int y1, int x2, int y2, int endwall)
1305{
1306    int dx = x2 - x1, dy = y2 - y1;
1307    int sidewalls;
1308
1309    sidewalls = endwall | (endwall >> 2 | endwall << 2);
1310    sidewalls = ~sidewalls & WALL_ANY;
1311
1312    while((maze[x2][y2] & WALL_ANY) == sidewalls)
1313    {
1314        x2 += dx;
1315        y2 += dy;
1316    }
1317
1318    if((maze[x2][y2] & WALL_ANY) == (sidewalls | endwall))
1319    {
1320        endwall = (endwall >> 2 | endwall << 2) & WALL_ANY;
1321        while(x1 != x2 || y1 != y2)
1322        {
1323            x1 += dx;
1324            y1 += dy;
1325            draw_solid_square(x1, y1, endwall, sgc);
1326            maze[x1][y1] |= SOLVER_VISIT;
1327        }
1328        return 1;
1329    }
1330    else
1331        return 0;
1332}
1333
1334/* Find all dead regions -- areas from which the goal cannot be reached --
1335   and mark them visited. */
1336void
1337find_dead_regions(void)
1338{
1339    int x, y, flipped;
1340
1341    /* Find all not SOLVER_VISIT squares bordering NOT_DEAD squares
1342       and mark them NOT_DEAD also.  Repeat until no more such squares. */
1343    maze[start_x][start_y] |= NOT_DEAD;
1344   
1345    do
1346    {
1347        flipped = 0;
1348        for(x = 0; x < maze_size_x; x++)
1349            for(y = 0; y < maze_size_y; y++)
1350                if(!(maze[x][y] & (SOLVER_VISIT | NOT_DEAD))
1351                   && (   (x && (maze[x-1][y] & NOT_DEAD))
1352                       || (y && (maze[x][y-1] & NOT_DEAD))))
1353                {
1354                    flipped = 1;
1355                    maze[x][y] |= NOT_DEAD;
1356                }
1357        for(x = maze_size_x-1; x >= 0; x--)
1358            for(y = maze_size_y-1; y >= 0; y--)
1359                if(!(maze[x][y] & (SOLVER_VISIT | NOT_DEAD))
1360                   && (   (x != maze_size_x-1 && (maze[x+1][y] & NOT_DEAD))
1361                       || (y != maze_size_y-1 && (maze[x][y+1] & NOT_DEAD))))
1362                {
1363                    flipped = 1;
1364                    maze[x][y] |= NOT_DEAD;
1365                }
1366    }
1367    while(flipped);
1368
1369    for (y = 0; y < maze_size_y; y++)
1370      for (x = 0; x < maze_size_x; x++)
1371      {
1372        if (maze[x][y] & NOT_DEAD)
1373          maze[x][y] &= ~NOT_DEAD;
1374        else if (!(maze[x][y] & SOLVER_VISIT))
1375        {
1376          maze[x][y] |= SOLVER_VISIT;
1377          if((x < logo_x || x > logo_x + logo_width / grid_width) ||
1378             (y < logo_y || y > logo_y + logo_height / grid_height))
1379          {
1380            if (!(maze[x][y] & WALL_ANY))
1381              XFillRectangle(dpy, win, ugc,
1382                             border_x + bw + grid_width * x,
1383                             border_y + bw + grid_height * y,
1384                             grid_width - (bw+bw), grid_height - (bw+bw));
1385            else
1386            {
1387              if (! (maze[x][y] & WALL_LEFT))
1388                draw_solid_square(x, y, WALL_LEFT, ugc);
1389              if (! (maze[x][y] & WALL_RIGHT))
1390                draw_solid_square(x, y, WALL_RIGHT, ugc);
1391              if (! (maze[x][y] & WALL_TOP))
1392                draw_solid_square(x, y, WALL_TOP, ugc);
1393              if (! (maze[x][y] & WALL_BOTTOM))
1394                draw_solid_square(x, y, WALL_BOTTOM, ugc);
1395            }
1396          }
1397        }
1398      }
1399    XSync(dpy, False);
1400}
1401
1402static void
1403solve_maze (void)                     /* solve it with graphical feedback */
1404{
1405    int i, dir, from, x, y, ways, bt = 0;
1406
1407    /* plug up the surrounding wall */
1408    maze[end_x][end_y] |= (WALL_TOP >> end_dir);
1409   
1410    /* initialize search path */
1411    i = 0;
1412    path[i].x = end_x;
1413    path[i].y = end_y;
1414    path[i].dir = 0;
1415    maze[end_x][end_y] |= SOLVER_VISIT;
1416   
1417    /* do it */
1418    while (1)
1419    {
1420        if ( maze[path[i].x][path[i].y] & START_SQUARE )
1421            return;
1422
1423        /* Abort solve on expose - cheapo repaint strategy */
1424        if (check_events()) return;
1425       
1426        if (solve_delay) usleep (solve_delay);
1427       
1428        if(!path[i].dir)
1429        {
1430            ways = 0;
1431            /* First visit this square.  Which adjacent squares are open? */
1432            for(dir = WALL_TOP; dir & WALL_ANY; dir >>= 1)
1433            {
1434                if(maze[path[i].x][path[i].y] & dir)
1435                    continue;
1436               
1437                y = path[i].y - !!(dir & WALL_TOP) + !!(dir & WALL_BOTTOM);
1438                x = path[i].x + !!(dir & WALL_RIGHT) - !!(dir & WALL_LEFT);
1439               
1440                if(maze[x][y] & SOLVER_VISIT)
1441                    continue;
1442               
1443                from = (dir << 2 & WALL_ANY) | (dir >> 2 & WALL_ANY);
1444                /* don't enter obvious dead ends */
1445                if(((maze[x][y] & WALL_ANY) | from) != WALL_ANY)
1446                {
1447                    if(!longdeadend_p(path[i].x, path[i].y, x, y, dir))
1448                        ways |= dir;
1449                }
1450                else
1451                {
1452                    draw_solid_square(x, y, from, sgc);
1453                    maze[x][y] |= SOLVER_VISIT;
1454                }
1455            }
1456        }
1457        else
1458            ways = path[i].ways;
1459        /* ways now has a bitmask of open paths. */
1460       
1461        if(!ways)
1462            goto backtrack;
1463
1464        if (!ignorant_p)
1465          {
1466            x = path[i].x - start_x;
1467            y = path[i].y - start_y;
1468            /* choice one */
1469            if(abs(y) <= abs(x))
1470              dir = (x > 0) ? WALL_LEFT : WALL_RIGHT;
1471            else
1472              dir = (y > 0) ? WALL_TOP : WALL_BOTTOM;
1473           
1474            if(dir & ways)
1475              goto found;
1476           
1477            /* choice two */
1478            switch(dir)
1479              {
1480              case WALL_LEFT:
1481              case WALL_RIGHT:
1482                dir = (y > 0) ? WALL_TOP : WALL_BOTTOM; break;
1483              case WALL_TOP:
1484              case WALL_BOTTOM:
1485                dir = (x > 0) ? WALL_LEFT : WALL_RIGHT;
1486              }
1487           
1488            if(dir & ways)
1489              goto found;
1490           
1491            /* choice three */
1492           
1493            dir = (dir << 2 & WALL_ANY) | (dir >> 2 & WALL_ANY);
1494            if(dir & ways)
1495              goto found;
1496           
1497            /* choice four */
1498            dir = ways;
1499            if(!dir)
1500              goto backtrack;
1501           
1502          found: ;
1503          }
1504        else
1505          {
1506            if(ways&WALL_TOP)
1507              dir = WALL_TOP;
1508            else if(ways&WALL_LEFT)
1509              dir = WALL_LEFT;
1510            else if(ways&WALL_BOTTOM)
1511              dir = WALL_BOTTOM;
1512            else if(ways&WALL_RIGHT)
1513              dir = WALL_RIGHT;
1514            else
1515              goto backtrack;
1516          }
1517        bt = 0;
1518        ways &= ~dir;  /* tried this one */
1519       
1520        y = path[i].y - !!(dir & WALL_TOP) + !!(dir & WALL_BOTTOM);
1521        x = path[i].x + !!(dir & WALL_RIGHT) - !!(dir & WALL_LEFT);
1522       
1523        /* advance in direction dir */
1524        path[i].dir = dir;
1525        path[i].ways = ways;
1526        draw_solid_square(path[i].x, path[i].y, dir, tgc);
1527       
1528        i++;
1529        path[i].dir = 0;
1530        path[i].ways = 0;
1531        path[i].x = x;
1532        path[i].y = y;
1533        maze[x][y] |= SOLVER_VISIT;
1534        continue;
1535
1536    backtrack:
1537        if(i == 0)
1538        {
1539            printf("Unsolvable maze.\n");
1540            return;
1541        }
1542
1543        if(!bt && !ignorant_p)
1544            find_dead_regions();
1545        bt = 1;
1546        from = path[i-1].dir;
1547        from = (from << 2 & WALL_ANY) | (from >> 2 & WALL_ANY);
1548       
1549        draw_solid_square(path[i].x, path[i].y, from, cgc);
1550        i--;
1551    }
1552}
1553
1554#if 0
1555static void
1556enter_square (int n)                      /* move into a neighboring square */
1557{
1558  draw_solid_square( (int)path[n].x, (int)path[n].y,
1559                    (int)path[n].dir, tgc);
1560 
1561  path[n+1].dir = -1;
1562  switch (path[n].dir) {
1563  case 0: path[n+1].x = path[n].x;
1564    path[n+1].y = path[n].y - 1;
1565    break;
1566  case 1: path[n+1].x = path[n].x + 1;
1567    path[n+1].y = path[n].y;
1568    break;
1569  case 2: path[n+1].x = path[n].x;
1570    path[n+1].y = path[n].y + 1;
1571    break;
1572  case 3: path[n+1].x = path[n].x - 1;
1573    path[n+1].y = path[n].y;
1574    break;
1575  }
1576}
1577#endif /* 0 */
1578
1579
1580/*
1581 *  jmr additions for Jamie Zawinski's <jwz@jwz.org> screensaver stuff,
1582 *  note that the code above this has probably been hacked about in some
1583 *  arbitrary way.
1584 */
1585
1586char *progclass = "Maze";
1587
1588char *defaults[] = {
1589  ".background: black",
1590  ".foreground: white",
1591  "*gridSize:   0",
1592  "*solveDelay: 5000",
1593  "*preDelay:   2000000",
1594  "*postDelay:  4000000",
1595  "*liveColor:  green",
1596  "*deadColor:  red",
1597  "*skipColor:  orange",
1598  "*surroundColor: slateblue",
1599  "*generator:  -1",
1600  "*maxLength:  5",
1601  "*syncDraw:   False",
1602  "*bridge:     False",
1603  0
1604};
1605
1606XrmOptionDescRec options[] = {
1607  { "-ignorant",        ".ignorant",    XrmoptionNoArg, "True" },
1608  { "-no-ignorant",     ".ignorant",    XrmoptionNoArg, "False" },
1609  { "-grid-size",       ".gridSize",    XrmoptionSepArg, 0 },
1610  { "-solve-delay",     ".solveDelay",  XrmoptionSepArg, 0 },
1611  { "-pre-delay",       ".preDelay",    XrmoptionSepArg, 0 },
1612  { "-post-delay",      ".postDelay",   XrmoptionSepArg, 0 },
1613  { "-bg-color",        ".background",  XrmoptionSepArg, 0 },
1614  { "-fg-color",        ".foreground",  XrmoptionSepArg, 0 },
1615  { "-live-color",      ".liveColor",   XrmoptionSepArg, 0 },
1616  { "-dead-color",      ".deadColor",   XrmoptionSepArg, 0 },
1617  { "-skip-color",      ".skipColor",   XrmoptionSepArg, 0 },
1618  { "-surround-color",  ".surroundColor",XrmoptionSepArg, 0 },
1619  { "-generator",       ".generator",   XrmoptionSepArg, 0 },
1620  { "-max-length",      ".maxLength",   XrmoptionSepArg, 0 },
1621  { "-bridge",          ".bridge",      XrmoptionNoArg, "True" },
1622  { "-no-bridge",       ".bridge",      XrmoptionNoArg, "False" },
1623  { 0, 0, 0, 0 }
1624};
1625
1626void
1627screenhack(Display *display, Window window)
1628{
1629  Pixmap gray;
1630  int size, root, generator, this_gen;
1631  XWindowAttributes xgwa;
1632  unsigned long bg, fg, pfg, pbg, sfg, ufg;
1633
1634  size = get_integer_resource ("gridSize", "Dimension");
1635  root = get_boolean_resource("root", "Boolean");
1636  solve_delay = get_integer_resource ("solveDelay", "Integer");
1637  pre_solve_delay = get_integer_resource ("preDelay", "Integer");
1638  post_solve_delay = get_integer_resource ("postDelay", "Integer");
1639  generator = get_integer_resource("generator", "Integer");
1640  max_length = get_integer_resource("maxLength", "Integer");
1641  bridge_p = get_boolean_resource("bridge", "Boolean");
1642  ignorant_p = get_boolean_resource("ignorant", "Boolean");
1643
1644  if (size < 2) size = 7 + (random () % 30);
1645  grid_width = grid_height = size;
1646  bw = (size > 6 ? 3 : (size-1)/2);
1647
1648  dpy = display; win = window; /* the maze stuff uses global variables */
1649
1650  XGetWindowAttributes (dpy, win, &xgwa);
1651
1652  x = 0;
1653  y = 0;
1654
1655  set_maze_sizes (xgwa.width, xgwa.height);
1656
1657  if (! root)
1658    {
1659      XWindowAttributes xgwa;
1660      XGetWindowAttributes (dpy, window, &xgwa);
1661      XSelectInput (dpy, win,
1662                    xgwa.your_event_mask | ExposureMask |
1663                    ButtonPressMask |StructureNotifyMask);
1664    }
1665 
1666  gc  = XCreateGC(dpy, win, 0, 0);
1667  cgc = XCreateGC(dpy, win, 0, 0);
1668  tgc = XCreateGC(dpy, win, 0, 0);
1669  sgc = XCreateGC(dpy, win, 0, 0);
1670  ugc = XCreateGC(dpy, win, 0, 0);
1671  logo_gc = XCreateGC(dpy, win, 0, 0);
1672  erase_gc = XCreateGC(dpy, win, 0, 0);
1673 
1674  gray = XCreateBitmapFromData (dpy,win,gray1_bits,gray1_width,gray1_height);
1675
1676  bg  = get_pixel_resource ("background","Background", dpy, xgwa.colormap);
1677  fg  = get_pixel_resource ("foreground","Foreground", dpy, xgwa.colormap);
1678  pfg = get_pixel_resource ("liveColor", "Foreground", dpy, xgwa.colormap);
1679  pbg = get_pixel_resource ("deadColor", "Foreground", dpy, xgwa.colormap);
1680  sfg = get_pixel_resource ("skipColor", "Foreground", dpy, xgwa.colormap);
1681  ufg = get_pixel_resource ("surroundColor", "Foreground", dpy, xgwa.colormap);
1682
1683  XSetForeground (dpy, gc, fg);
1684  XSetBackground (dpy, gc, bg);
1685  XSetForeground (dpy, cgc, pbg);
1686  XSetBackground (dpy, cgc, bg);
1687  XSetForeground (dpy, tgc, pfg);
1688  XSetBackground (dpy, tgc, bg);
1689  XSetForeground (dpy, sgc, sfg);
1690  XSetBackground (dpy, sgc, bg);
1691  XSetForeground (dpy, ugc, ufg);
1692  XSetBackground (dpy, ugc, bg);
1693  XSetForeground (dpy, logo_gc, fg);
1694  XSetBackground (dpy, logo_gc, bg);
1695  XSetForeground (dpy, erase_gc, bg);
1696  XSetBackground (dpy, erase_gc, bg);
1697
1698  XSetStipple (dpy, cgc, gray);
1699  XSetFillStyle (dpy, cgc, FillOpaqueStippled);
1700  XSetStipple (dpy, sgc, gray);
1701  XSetFillStyle (dpy, sgc, FillOpaqueStippled);
1702  XSetStipple (dpy, ugc, gray);
1703  XSetFillStyle (dpy, ugc, FillOpaqueStippled);
1704 
1705#ifdef XSCREENSAVER_LOGO
1706  {
1707    unsigned long *pixels; /* ignored - unfreed */
1708    int npixels;
1709    logo_map = xscreensaver_logo (xgwa.screen, xgwa.visual, win,
1710                                  xgwa.colormap, bg,
1711                                  &pixels, &npixels, 0,
1712                                  logo_width > 150);
1713  }
1714#else
1715  if  (!(logo_map = XCreateBitmapFromData (dpy, win, logo_bits,
1716                                           logo_width, logo_height)))
1717    {
1718      fprintf (stderr, "Can't create logo pixmap\n");
1719      exit (1);
1720    }
1721#endif
1722  XMapRaised(dpy, win);
1723
1724  restart = root;
1725
1726  sync_p = !(random() % 10);
1727
1728  while (1) {                            /* primary execution loop [ rhess ] */
1729    if (check_events()) continue ;
1730    if (restart || stop) goto pop;
1731    switch (state) {
1732    case 1:
1733      initialize_maze();
1734      break;
1735    case 2:
1736      XClearWindow(dpy, win);
1737      draw_maze_border();
1738      break;
1739    case 3:
1740      this_gen = generator;
1741      if(this_gen<0 || this_gen>2)
1742        this_gen = random()%3;
1743
1744      switch(this_gen)
1745        {
1746        case 0:
1747          create_maze();
1748          break;
1749        case 1:
1750          alt_create_maze();
1751          break;
1752        case 2:
1753          set_create_maze();
1754          break;
1755        }
1756      break;
1757    case 4:
1758      XSync (dpy, False);
1759      usleep (pre_solve_delay);
1760      break;
1761    case 5:
1762      solve_maze();
1763      break;
1764    case 6:
1765      XSync (dpy, False);
1766      usleep (post_solve_delay);
1767      state = 0 ;
1768      erase_full_window(display, window);
1769      break;
1770    default:
1771      abort ();
1772    }
1773    ++state;
1774  pop:
1775    if (restart)
1776      {
1777        static XWindowAttributes wattr;
1778        restart = 0;
1779        stop = 0;
1780        state = 1;
1781        XGetWindowAttributes (dpy, win, &wattr);
1782        set_maze_sizes (wattr.width, wattr.height);
1783        XClearWindow (dpy, win);
1784        XSync (dpy, False);
1785        sync_p = !(random() % 10);
1786      }
1787  }
1788}
Note: See TracBrowser for help on using the repository browser.