9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
* [9fans] draw() question
@ 2002-08-16  7:59 andrey mirtchovski
  0 siblings, 0 replies; 3+ messages in thread
From: andrey mirtchovski @ 2002-08-16  7:59 UTC (permalink / raw)
  To: 9fans

[-- Attachment #1: Type: TEXT/PLAIN, Size: 701 bytes --]

hi,

i'm stuck trying to do the following with the
already existing plan9 functions from draw.h:

in short, i want to do a draw() to an image
using only a specific rectangle in the source
image, i.e. a call to draw which puts at
dst(x, y) the rectangle Rect(x1, y1, x2, y2)
from src..

so far i can clumsily simulate that by setting
src's clipr to the wanted rectangle and then
aligning the source so that src->clipr.min is
at (x, y) in the draw() operation. somehow,
though, i don't think this is the easiest way :)


i'm sure the answer is very simple, andrey

ps: i'm attaching maze.c for your enjoyment,
it'll probably appear on the xscreensaver ports
site in a few days...

[-- Attachment #2: Type: TEXT/PLAIN, Size: 41495 bytes --]

/******************************************************************************
 * [ maze ] ...
 *
 * modified:  [ 1-04-00 ]  Johannes Keukelaar <johannes@nada.kth.se>
 *              Added -ignorant option (not the default) to remove knowlege
 *              of the direction in which the exit lies.
 *
 * modified:  [ 6-28-98 ]  Zack Weinberg <zack@rabi.phys.columbia.edu>
 *
 *              Made the maze-solver somewhat more intelligent.  There are
 *              three optimizations:
 *
 *              - Straight-line lookahead: the solver does not enter dead-end
 *                corridors.  This is a win with all maze generators.
 *
 *              - First order direction choice: the solver knows where the
 *                exit is in relation to itself, and will try paths leading in
 *                that direction first. This is a major win on maze generator 1
 *                which tends to offer direct routes to the exit.
 *
 *              - Dead region elimination: the solver already has a map of
 *                all squares visited.  Whenever it starts to backtrack, it
 *                consults this map and marks off all squares that cannot be
 *                reached from the exit without crossing a square already
 *                visited.  Those squares can never contribute to the path to
 *                the exit, so it doesn't bother checking them.  This helps a
 *                lot with maze generator 2 and somewhat less with generator 1.
 *
 *              Further improvements would require knowledge of the wall map
 *              as well as the position of the exit and the squares visited.
 *              I would consider that to be cheating.  Generator 0 makes
 *              mazes which are remarkably difficult to solve mechanically --
 *              even with these optimizations the solver generally must visit
 *              at least two-thirds of the squares.  This is partially
 *              because generator 0's mazes have longer paths to the exit.
 *
 * modified:  [ 4-10-97 ]  Johannes Keukelaar <johannes@nada.kth.se>
 *              Added multiple maze creators. Robustified solver.
 *              Added bridge option.
 * modified:  [ 8-11-95 ] Ed James <james@mml.mmc.com>
 *              added fill of dead-end box to solve_maze while loop.
 * modified:  [ 3-7-93 ]  Jamie Zawinski <jwz@jwz.org>
 *		added the XRoger logo, cleaned up resources, made
 *		grid size a parameter.
 * modified:  [ 3-3-93 ]  Jim Randell <jmr@mddjmr.fc.hp.com>
 *		Added the colour stuff and integrated it with jwz's
 *		screenhack stuff.  There's still some work that could
 *		be done on this, particularly allowing a resource to
 *		specify how big the squares are.
 * modified:  [ 10-4-88 ]  Richard Hess    ...!uunet!cimshop!rhess  
 *              [ Revised primary execution loop within main()...
 *              [ Extended X event handler, check_events()...
 * modified:  [ 1-29-88 ]  Dave Lemke      lemke@sun.com  
 *              [ Hacked for X11...
 *              [  Note the word "hacked" -- this is extremely ugly, but at 
 *              [   least it does the job.  NOT a good programming example 
 *              [   for X.
 * original:  [ 6/21/85 ]  Martin Weiss    Sun Microsystems  [ SunView ]
 *
 ******************************************************************************
 Copyright 1988 by Sun Microsystems, Inc. Mountain View, CA.
  
 All Rights Reserved
  
 Permission to use, copy, modify, and distribute this software and its
 documentation for any purpose and without fee is hereby granted, 
 provided that the above copyright notice appear in all copies and that
 both that copyright notice and this permission notice appear in 
 supporting documentation, and that the names of Sun or MIT not be
 used in advertising or publicity pertaining to distribution of the
 software without specific prior written permission. Sun and M.I.T. 
 make no representations about the suitability of this software for 
 any purpose. It is provided "as is" without any express or implied warranty.
 
 SUN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 PURPOSE. IN NO EVENT SHALL SUN BE LIABLE FOR ANY SPECIAL, INDIRECT
 OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
 OR PERFORMANCE OF THIS SOFTWARE.
 *****************************************************************************/


/* 
  * ported to Plan 9 by andrey@lanl.gov, 08/02
  */

/* plan9-related stuff */
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>

#define NULL nil
#define XPoint Point
#define NRAND nrand
#define LRAND lrand
#define random rand
#define MAXRAND ((2<<31)-1)
#define MAX(a, b) (((a) > (b))?(a):(b))
#define MIN(a, b) (((a) < (b))?(a):(b))
#define RAND_MAX MAXRAND
#define ABS abs
Image *colors[256];
#define M_PI	PI
#define Bool int
#define True 1
#define False 0

Image *liveColor, *deadColor, *skipColor, *surroundColor;

Image *glenda;

char *buttons[] = 
{
	"exit",
	0
};

Menu menu = 
{
	buttons
};
Mouse m;
/* end of plan9-related defines */


#define XSCREENSAVER_LOGO

static int solve_delay, pre_solve_delay, post_solve_delay;

#define MAX_MAZE_SIZE_X	500
#define MAX_MAZE_SIZE_Y	500

#define MOVE_LIST_SIZE  (MAX_MAZE_SIZE_X * MAX_MAZE_SIZE_Y)

#define NOT_DEAD	0x8000
#define SOLVER_VISIT    0x4000
#define START_SQUARE	0x2000
#define END_SQUARE	0x1000

#define WALL_TOP	0x8
#define WALL_RIGHT	0x4
#define WALL_BOTTOM	0x2
#define WALL_LEFT	0x1
#define WALL_ANY	0xF

#define DOOR_IN_TOP	0x800
#define DOOR_IN_RIGHT	0x400
#define DOOR_IN_BOTTOM	0x200
#define DOOR_IN_LEFT	0x100
#define DOOR_IN_ANY	0xF00

#define DOOR_OUT_TOP	0x80
#define DOOR_OUT_RIGHT	0x40
#define DOOR_OUT_BOTTOM	0x20
#define DOOR_OUT_LEFT	0x10


#define	border_x        (0)
#define	border_y        (0)

#define	get_random(x)	(random() % (x))


static int logo_x, logo_y;

# define logo_width  48
# define logo_height 48

static unsigned short maze[MAX_MAZE_SIZE_X][MAX_MAZE_SIZE_Y];

static struct {
  unsigned char x;
  unsigned char y;
  unsigned char dir, ways;
} move_list[MOVE_LIST_SIZE], save_path[MOVE_LIST_SIZE], path[MOVE_LIST_SIZE];

static int maze_size_x, maze_size_y;
static int sqnum, cur_sq_x, cur_sq_y, path_length;
static int start_x, start_y, start_dir, end_x, end_y, end_dir;
static int grid_width, grid_height;
static int bw;

static int	x = 0, y = 0, restart = 0, stop = 0, state = 1, max_length;
static int      sync_p, bridge_p, ignorant_p;


static void
set_maze_sizes (int width, int height)
{
  maze_size_x = width / grid_width;
  maze_size_y = height / grid_height;
}


static void
initialize_maze (void) /* draw the surrounding wall and start/end squares */
{
  register int i, j, wall;
  int logow = 1 + logo_width / grid_width;
  int logoh = 1 + logo_height / grid_height;
  
  /* initialize all squares */
  for ( i=0; i<maze_size_x; i++) {
    for ( j=0; j<maze_size_y; j++) {
      maze[i][j] = 0;
    }
  }
  
  /* top wall */
  for ( i=0; i<maze_size_x; i++ ) {
    maze[i][0] |= WALL_TOP;
  }
  
  /* right wall */
  for ( j=0; j<maze_size_y; j++ ) {
    maze[maze_size_x-1][j] |= WALL_RIGHT;
  }
  
  /* bottom wall */
  for ( i=0; i<maze_size_x; i++ ) {
    maze[i][maze_size_y-1] |= WALL_BOTTOM;
  }
  
  /* left wall */
  for ( j=0; j<maze_size_y; j++ ) {
    maze[0][j] |= WALL_LEFT;
  }
  
  /* set start square */
  wall = get_random(4);
  switch (wall) {
  case 0:	
    i = get_random(maze_size_x);
    j = 0;
    break;
  case 1:	
    i = maze_size_x - 1;
    j = get_random(maze_size_y);
    break;
  case 2:	
    i = get_random(maze_size_x);
    j = maze_size_y - 1;
    break;
  case 3:	
    i = 0;
    j = get_random(maze_size_y);
    break;
  }
  maze[i][j] |= START_SQUARE;
  maze[i][j] |= ( DOOR_IN_TOP >> wall );
  maze[i][j] &= ~( WALL_TOP >> wall );
  cur_sq_x = i;
  cur_sq_y = j;
  start_x = i;
  start_y = j;
  start_dir = wall;
  sqnum = 0;
  
  /* set end square */
  wall = (wall + 2)%4;
  switch (wall) {
  case 0:
    i = get_random(maze_size_x);
    j = 0;
    break;
  case 1:
    i = maze_size_x - 1;
    j = get_random(maze_size_y);
    break;
  case 2:
    i = get_random(maze_size_x);
    j = maze_size_y - 1;
    break;
  case 3:
    i = 0;
    j = get_random(maze_size_y);
    break;
  }
  maze[i][j] |= END_SQUARE;
  maze[i][j] |= ( DOOR_OUT_TOP >> wall );
  maze[i][j] &= ~( WALL_TOP >> wall );
  end_x = i;
  end_y = j;
  end_dir = wall;
  
  /* set logo */
  if ((maze_size_x-logow >= 6) && (maze_size_y-logoh >= 6))
    {
      /* not closer than 3 grid units from a wall */
      logo_x = get_random (maze_size_x - logow - 5) + 3;
      logo_y = get_random (maze_size_y - logoh - 5) + 3;
      for (i=0; i<logow; i++)
	for (j=0; j<logoh; j++)
	  maze[logo_x + i][logo_y + j] |= DOOR_IN_TOP;
    }
  else
    logo_y = logo_x = -1;
}

static int choose_door (void);
static int backup (void);
static void draw_wall (int, int, int);
static void draw_solid_square (int, int, int, Image*);
/*static void enter_square (int);*/
static void build_wall (int, int, int);
/*static void break_wall (int, int, int);*/

static void join_sets(int, int);

/* For set_create_maze. */
/* The sets that our squares are in. */
static int *sets = 0;
/* The `list' of hedges. */
static int *hedges = 0;


/* Initialise the sets. */
static void 
init_sets(void)
{
  int i, t, r, x, y;

  if(sets)
    free(sets);
  sets = (int *)malloc(maze_size_x*maze_size_y*sizeof(int));
  if(!sets)
    abort();
  for(i = 0; i < maze_size_x*maze_size_y; i++)
    {
      sets[i] = i;
    }
  
  if(hedges)
    free(hedges);
  hedges = (int *)malloc(maze_size_x*maze_size_y*2*sizeof(int));
  if(!hedges)
    abort();
  for(i = 0; i < maze_size_x*maze_size_y*2; i++)
    {
      hedges[i] = i;
    }
  /* Mask out outside walls. */
  for(i = 0; i < maze_size_y; i++)
    {
      hedges[2*((maze_size_x)*i+maze_size_x-1)+1] = -1;
    }
  for(i = 0; i < maze_size_x; i++)
    {
      hedges[2*((maze_size_y-1)*maze_size_x+i)] = -1;
    }
  /* Mask out a possible logo. */
  if(logo_x!=-1)
    {
      int logow = 1 + logo_width / grid_width;
      int logoh = 1 + logo_height / grid_height;
      int bridge_dir, bridge_c;

      if(bridge_p && logoh>=3 && logow>=3)
	{
	  bridge_dir = 1+random()%2;
	  if(bridge_dir==1)
	    {
	      bridge_c = logo_y+random()%(logoh-2)+1;
	    }
	  else
	    {
	      bridge_c = logo_x+random()%(logow-2)+1;
	    }
	}
      else
	{
	  bridge_dir = 0;
	  bridge_c = -1;
	}

      for(x = logo_x; x < logo_x+logow; x++)
	for(y = logo_y; y < logo_y+logoh; y++)
	  {
	    /* I should check for the bridge here, except that I join the
             * bridge together below.
             */
	    hedges[2*(x+maze_size_x*y)+1] = -1;
	    hedges[2*(x+maze_size_x*y)] = -1;
	  }
      for(x = logo_x; x < logo_x+logow; x++)
	{
	  if(!(bridge_dir==2 && x==bridge_c))
	    {
	      build_wall(x, logo_y, 0);
	      build_wall(x, logo_y+logoh, 0);
	    }
	  hedges[2*(x+maze_size_x*(logo_y-1))] = -1;
	  if(bridge_dir==1)
	    {
	      build_wall(x, bridge_c, 0);
	      build_wall(x, bridge_c, 2);
	    }
	}
      for(y = logo_y; y < logo_y+logoh; y++)
	{
	  if(!(bridge_dir==1 && y==bridge_c))
	    {
	      build_wall(logo_x, y, 3);
	      build_wall(logo_x+logow, y, 3);
	    }
	  hedges[2*(logo_x-1+maze_size_x*y)+1] = -1;
	  if(bridge_dir==2)
	    {
	      build_wall(bridge_c, y, 1);
	      build_wall(bridge_c, y, 3);
	    }
	}
      /* Join the whole bridge together. */
      if(bridge_p)
	{
	  if(bridge_dir==1)
	    {
	      x = logo_x-1;
	      y = bridge_c;
	      for(i = logo_x; i < logo_x+logow+1; i++)
		join_sets(x+y*maze_size_x, i+y*maze_size_x);
	    }
	  else
	    {
	      y = logo_y-1;
	      x = bridge_c;
	      for(i = logo_y; i < logo_y+logoh+1; i++)
		join_sets(x+y*maze_size_x, x+i*maze_size_x);
	    }
	}
    }

  for(i = 0; i < maze_size_x*maze_size_y*2; i++)
    {
      t = hedges[i];
      r = random()%(maze_size_x*maze_size_y*2);
      hedges[i] = hedges[r];
      hedges[r] = t;
    }
}

/* Get the representative of a set. */
static int
get_set(int num)
{
  int s;

  if(sets[num]==num)
    return num;
  else
    {
      s = get_set(sets[num]);
      sets[num] = s;
      return s;
    }
}

/* Join two sets together. */
static void
join_sets(num1, num2)
     int num1, num2;
{
  int s1, s2;

  s1 = get_set(num1);
  s2 = get_set(num2);
  
  if(s1<s2)
    sets[s2] = s1;
  else
    sets[s1] = s2;
}

/* Exitialise the sets. */
static void
exit_sets(void)
{
  if(hedges)
    free(hedges);
  hedges = 0;
  if(sets)
    free(sets);
  sets = 0;
}


/* Second alternative maze creator: Put each square in the maze in a 
 * separate set. Also, make a list of all the hedges. Randomize that list.
 * Walk through the list. If, for a certain hedge, the two squares on both
 * sides of it are in different sets, union the sets and remove the hedge.
 * Continue until all hedges have been processed or only one set remains.
 */
static void
set_create_maze(void)
{
  int i, h, x, y, dir, v, w;

  /* Do almost all the setup. */
  init_sets();

  /* Start running through the hedges. */
  for(i = 0; i < 2*maze_size_x*maze_size_y; i++)
    { 
      h = hedges[i];

      /* This one is in the logo or outside border. */
      if(h==-1)
	continue;

      dir = h%2?1:2;
      x = (h>>1)%maze_size_x;
      y = (h>>1)/maze_size_x;

      v = x;
      w = y;
      switch(dir)
	{
	case 1:
	  v++;
	  break;
	case 2:
	  w++;
	  break;
	}


      if(get_set(x+y*maze_size_x)!=get_set(v+w*maze_size_x))
	{

	  join_sets(x+y*maze_size_x, v+w*maze_size_x);
	  /* Don't draw the wall. */
	}
      else
	{

	  /* Don't join the sets. */
	  build_wall(x, y, dir); 
	}
    }

  /* Free some memory. */
  exit_sets();
}

/* First alternative maze creator: Pick a random, empty corner in the maze.
 * Pick a random direction. Draw a wall in that direction, from that corner
 * until we hit a wall. Option: Only draw the wall if it's going to be 
 * shorter than a certain length. Otherwise we get lots of long walls.
 */
static void
alt_create_maze(void)
{
  char *corners;
  int *c_idx;
  int i, j, height, width, open_corners, k, dir, x, y;

  height = maze_size_y+1;
  width = maze_size_x+1;

  /* Allocate and clear some mem. */
  corners = (char *)calloc(height*width, 1);
  if(!corners)
    return;

  /* Set up the indexing array. */
  c_idx = (int *)malloc(sizeof(int)*height*width);
  if(!c_idx)
    {
      free(corners);
      return;
    }
  for(i = 0; i < height*width; i++)
    c_idx[i] = i;
  for(i = 0; i < height*width; i++)
    {
      j = c_idx[i];
      k = random()%(height*width);
      c_idx[i] = c_idx[k];
      c_idx[k] = j;
    }

  /* Set up some initial walls. */
  /* Outside walls. */
  for(i = 0; i < width; i++)
    {
      corners[i] = 1;
      corners[i+width*(height-1)] = 1;
    }
  for(i = 0; i < height; i++)
    {
      corners[i*width] = 1;
      corners[i*width+width-1] = 1;
    }
  /* Walls around logo. In fact, inside the logo, too. */
  /* Also draw the walls. */
  if(logo_x!=-1)
    {
      int logow = 1 + logo_width / grid_width;
      int logoh = 1 + logo_height / grid_height;
      int bridge_dir, bridge_c;

      if(bridge_p && logoh>=3 && logow>=3)
	{
	  bridge_dir = 1+random()%2;
	  if(bridge_dir==1)
	    {
	      bridge_c = logo_y+random()%(logoh-2)+1;
	    }
	  else
	    {
	      bridge_c = logo_x+random()%(logow-2)+1;
	    }
	}
      else
	{
	  bridge_dir = 0;
	  bridge_c = -1;
	}
      for(i = logo_x; i <= logo_x + logow; i++)
	{
	  for(j = logo_y; j <= logo_y + logoh; j++)
	    {
	      corners[i+width*j] = 1;
	    }
	}
      for(x = logo_x; x < logo_x+logow; x++)
	{
	  if(!(bridge_dir==2 && x==bridge_c))
	    {
	      build_wall(x, logo_y, 0);
	      build_wall(x, logo_y+logoh, 0);
	    }
	  if(bridge_dir==1)
	    {
	      build_wall(x, bridge_c, 0);
	      build_wall(x, bridge_c, 2);
	    }
	}
      for(y = logo_y; y < logo_y+logoh; y++)
	{
	  if(!(bridge_dir==1 && y==bridge_c))
	    {
	      build_wall(logo_x, y, 3);
	      build_wall(logo_x+logow, y, 3);
	    }
	  if(bridge_dir==2)
	    {
	      build_wall(bridge_c, y, 1);
	      build_wall(bridge_c, y, 3);
	    }
	}
      /* Connect one wall of the logo with an outside wall. */
      if(bridge_p)
	dir = (bridge_dir+1)%4;
      else
	dir = random()%4;
      switch(dir)
	{
	case 0:
	  x = logo_x+(random()%(logow+1));
	  y = logo_y;
	  break;
	case 1:
	  x = logo_x+logow;
	  y = logo_y+(random()%(logoh+1));
	  break;
	case 2:
	  x = logo_x+(random()%(logow+1));
	  y = logo_y+logoh;
	  break;
	case 3:
	  x = logo_x;
	  y = logo_y+(random()%(logoh+1));
	  break;
	}
      do
	{
	  corners[x+width*y] = 1;
	  switch(dir)
	    {
	    case 0:
	      build_wall(x-1, y-1, 1);
	      y--;
	      break;
	    case 1:
	      build_wall(x, y, 0);
	      x++;
	      break;
	    case 2:
	      build_wall(x, y, 3);
	      y++;
	      break;
	    case 3:
	      build_wall(x-1, y-1, 2);
	      x--;
	      break;	  
	    }
	}
      while(!corners[x+width*y]);
      if(bridge_p)
	{
	  dir = (dir+2)%4;
	  switch(dir)
	    {
	    case 0:
	      x = logo_x+(random()%(logow+1));
	      y = logo_y;
	      break;
	    case 1:
	      x = logo_x+logow;
	      y = logo_y+(random()%(logoh+1));
	      break;
	    case 2:
	      x = logo_x+(random()%(logow+1));
	      y = logo_y+logoh;
	      break;
	    case 3:
	      x = logo_x;
	      y = logo_y+(random()%(logoh+1));
	      break;
	    }
	  do
	    {
	      corners[x+width*y] = 1;
	      switch(dir)
		{
		case 0:
		  build_wall(x-1, y-1, 1);
		  y--;
		  break;
		case 1:
		  build_wall(x, y, 0);
		  x++;
		  break;
		case 2:
		  build_wall(x, y, 3);
		  y++;
		  break;
		case 3:
		  build_wall(x-1, y-1, 2);
		  x--;
		  break;	  
		}
	    }
	  while(!corners[x+width*y]);
	}
    }

  /* Count open gridpoints. */
  open_corners = 0;
  for(i = 0; i < width; i++)
    for(j = 0; j < height; j++)
      if(!corners[i+width*j])
	open_corners++;

  /* Now do actual maze generation. */
  while(open_corners>0)
    {
      for(i = 0; i < width*height; i++)
	{
	  if(!corners[c_idx[i]])
	    {
	      x = c_idx[i]%width;
	      y = c_idx[i]/width;
	      /* Choose a random direction. */
	      dir = random()%4;
	      
	      k = 0;
	      /* Measure the length of the wall we'd draw. */
	      while(!corners[x+width*y])
		{
		  k++;
		  switch(dir)
		    {
		    case 0:
		      y--;
		      break;
		    case 1:
		      x++;
		      break;
		    case 2:
		      y++;
		      break;
		    case 3:
		      x--;
		      break;
		    }
		}
	      
	      if(k<=max_length)
		{
		  x = c_idx[i]%width;
		  y = c_idx[i]/width;
		  
		  /* Draw a wall until we hit something. */
		  while(!corners[x+width*y])
		    {
		      open_corners--;
		      corners[x+width*y] = 1;
		      switch(dir)
			{
			case 0:
			  build_wall(x-1, y-1, 1);
			  y--;
			  break;
			case 1:
			  build_wall(x, y, 0);
			  x++;
			  break;
			case 2:
			  build_wall(x, y, 3);
			  y++;
			  break;
			case 3:
			  build_wall(x-1, y-1, 2);
			  x--;
			  break;
			}
		    }
		}
	    }
	}
    }

  /* Free some memory we used. */
  free(corners);
  free(c_idx);
}

/* The original maze creator. Start somewhere. Take a step in a random 
 * direction. Keep doing this until we hit a wall. Then, backtrack until
 * we find a point where we can go in another direction.
 */
static void
create_maze (void)    /* create a maze layout given the initialized maze */
{
  register int i, newdoor = 0;
  int logow = 1 + logo_width / grid_width;
  int logoh = 1 + logo_height / grid_height;
  
  /* Maybe we should make a bridge? */
  if(bridge_p && logo_x>=0 && logow>=3 && logoh>=3)
    {
      int bridge_dir, bridge_c;

      bridge_dir = 1+random()%2;
      if(bridge_dir==1)
	{
	  if(logoh>=3)
	    bridge_c = logo_y+random()%(logoh-2)+1;
	  else
	    bridge_c = logo_y+random()%logoh;
	}
      else
	{
	  if(logow>=3)
	    bridge_c = logo_x+random()%(logow-2)+1;
	  else
	    bridge_c = logo_x+random()%logow;
	}

      if(bridge_dir==1)
	{
	  for(i = logo_x; i < logo_x+logow; i++)
	    {
	      maze[i][bridge_c] &= ~DOOR_IN_TOP;
	    }
	}
      else
	{
	  for(i = logo_y; i < logo_y+logoh; i++)
	    {
	      maze[bridge_c][i] &= ~DOOR_IN_TOP;
	    }
	}
    }

  do {
    move_list[sqnum].x = cur_sq_x;
    move_list[sqnum].y = cur_sq_y;
    move_list[sqnum].dir = newdoor;
    while ( ( newdoor = choose_door() ) == -1 ) { /* pick a door */
      if ( backup() == -1 ) { /* no more doors ... backup */
	return; /* done ... return */
      }
    }
    
    /* mark the out door */
    maze[cur_sq_x][cur_sq_y] |= ( DOOR_OUT_TOP >> newdoor );
    
    switch (newdoor) {
    case 0: cur_sq_y--;
      break;
    case 1: cur_sq_x++;
      break;
    case 2: cur_sq_y++;
      break;
    case 3: cur_sq_x--;
      break;
    }
    sqnum++;
    
    /* mark the in door */
    maze[cur_sq_x][cur_sq_y] |= ( DOOR_IN_TOP >> ((newdoor+2)%4) );
    
    /* if end square set path length and save path */
    if ( maze[cur_sq_x][cur_sq_y] & END_SQUARE ) {
      path_length = sqnum;
      for ( i=0; i<path_length; i++) {
	save_path[i].x = move_list[i].x;
	save_path[i].y = move_list[i].y;
	save_path[i].dir = move_list[i].dir;
      }
    }
    
  } while (1);
  
}


static int
choose_door (void)                                   /* pick a new path */
{
  int candidates[3];
  register int num_candidates;
  
  num_candidates = 0;
  
  /* top wall */
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_TOP )
    goto rightwall;
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_TOP )
    goto rightwall;
  if ( maze[cur_sq_x][cur_sq_y] & WALL_TOP )
    goto rightwall;
  if ( maze[cur_sq_x][cur_sq_y - 1] & DOOR_IN_ANY ) {
    maze[cur_sq_x][cur_sq_y] |= WALL_TOP;
    maze[cur_sq_x][cur_sq_y - 1] |= WALL_BOTTOM;
    draw_wall(cur_sq_x, cur_sq_y, 0);
    goto rightwall;
  }
  candidates[num_candidates++] = 0;
  
 rightwall:
  /* right wall */
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_RIGHT )
    goto bottomwall;
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_RIGHT )
    goto bottomwall;
  if ( maze[cur_sq_x][cur_sq_y] & WALL_RIGHT )
    goto bottomwall;
  if ( maze[cur_sq_x + 1][cur_sq_y] & DOOR_IN_ANY ) {
    maze[cur_sq_x][cur_sq_y] |= WALL_RIGHT;
    maze[cur_sq_x + 1][cur_sq_y] |= WALL_LEFT;
    draw_wall(cur_sq_x, cur_sq_y, 1);
    goto bottomwall;
  }
  candidates[num_candidates++] = 1;
  
 bottomwall:
  /* bottom wall */
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_BOTTOM )
    goto leftwall;
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_BOTTOM )
    goto leftwall;
  if ( maze[cur_sq_x][cur_sq_y] & WALL_BOTTOM )
    goto leftwall;
  if ( maze[cur_sq_x][cur_sq_y + 1] & DOOR_IN_ANY ) {
    maze[cur_sq_x][cur_sq_y] |= WALL_BOTTOM;
    maze[cur_sq_x][cur_sq_y + 1] |= WALL_TOP;
    draw_wall(cur_sq_x, cur_sq_y, 2);
    goto leftwall;
  }
  candidates[num_candidates++] = 2;
  
 leftwall:
  /* left wall */
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_LEFT )
    goto donewall;
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_LEFT )
    goto donewall;
  if ( maze[cur_sq_x][cur_sq_y] & WALL_LEFT )
    goto donewall;
  if ( maze[cur_sq_x - 1][cur_sq_y] & DOOR_IN_ANY ) {
    maze[cur_sq_x][cur_sq_y] |= WALL_LEFT;
    maze[cur_sq_x - 1][cur_sq_y] |= WALL_RIGHT;
    draw_wall(cur_sq_x, cur_sq_y, 3);
    goto donewall;
  }
  candidates[num_candidates++] = 3;
  
 donewall:
  if (num_candidates == 0)
    return ( -1 );
  if (num_candidates == 1)
    return ( candidates[0] );
  return ( candidates[ get_random(num_candidates) ] );
  
}


static int
backup (void)                                          /* back up a move */
{
  sqnum--;
  cur_sq_x = move_list[sqnum].x;
  cur_sq_y = move_list[sqnum].y;
  return ( sqnum );
}


static void
draw_maze_border (void)                         /* draw the maze outline */
{
  register int i, j;
 
  
  for ( i=0; i<maze_size_x; i++) {
    if ( maze[i][0] & WALL_TOP ) {
//      XDrawLine(dpy, win, gc,
//		border_x + grid_width * i,
//		border_y,
//		border_x + grid_width * (i+1) - 1,
//		border_y);

	line(screen, addpt(screen->r.min, Pt(border_x + grid_width * i, border_y)),
			addpt(screen->r.min, Pt(border_x + grid_width * (i+1) - 1, border_y)),
			Endsquare, Endsquare, 0, display->white, ZP);
    }
    if ((maze[i][maze_size_y - 1] & WALL_BOTTOM)) {
//      XDrawLine(dpy, win, gc,
//		border_x + grid_width * i,
//		border_y + grid_height * (maze_size_y) - 1,
//		border_x + grid_width * (i+1) - 1,
//		border_y + grid_height * (maze_size_y) - 1);
	line(screen, addpt(screen->r.min, 
				Pt(border_x + grid_width * i, border_y+ grid_height * (maze_size_y) - 1)),
			addpt(screen->r.min, 
				Pt(border_x + grid_width * (i+1) - 1, border_y + grid_height * (maze_size_y) - 1)),
			Endsquare, Endsquare, 0, display->white, ZP);
    }
  }
  for ( j=0; j<maze_size_y; j++) {
    if ( maze[maze_size_x - 1][j] & WALL_RIGHT ) {
//      XDrawLine(dpy, win, gc,
//		border_x + grid_width * maze_size_x - 1,
//		border_y + grid_height * j,
//		border_x + grid_width * maze_size_x - 1,
//		border_y + grid_height * (j+1) - 1);
	line(screen, addpt(screen->r.min, 
				Pt(border_x + grid_width * maze_size_x - 1, border_y + grid_height * j)),
			addpt(screen->r.min, 
				Pt(border_x + grid_width * maze_size_x - 1, border_y + grid_height * (j+1) - 1)),
			Endsquare, Endsquare, 0, display->white, ZP);
    }
    if ( maze[0][j] & WALL_LEFT ) {
//      XDrawLine(dpy, win, gc,
//		border_x,
//		border_y + grid_height * j,
//		border_x,
//		border_y + grid_height * (j+1) - 1);
	line(screen, addpt(screen->r.min, 
				Pt(border_x, border_y + grid_height * j)),
			addpt(screen->r.min, 
				Pt(border_x, border_y + grid_height * (j+1) - 1)),
			Endsquare, Endsquare, 0, display->white, ZP);
    }
  }
  
  if (logo_x != -1)
    {
      unsigned int w= 48, h = 48;
	Point p;

      /* round up to grid size */
      int ww = ((logo_width  / grid_width) + 1)  * grid_width;
      int hh = ((logo_height / grid_height) + 1) * grid_height;

	p = addpt(screen->r.min, Pt(border_x + 1 + grid_width  * logo_x + ((ww - w) / 2), border_y + 1 + grid_height * logo_y + ((hh - h) / 2)));
	

	draw(screen, Rpt(p, addpt(p, Pt(48, 48))), glenda, nil, ZP);
	//draw(screen, screen->r, glenda, nil, ZP);


    }
  draw_solid_square (start_x, start_y, WALL_TOP >> start_dir, liveColor);
  draw_solid_square (end_x, end_y, WALL_TOP >> end_dir, liveColor);
}


static void
draw_wall(int i, int j, int dir)                /* draw a single wall */
{
  switch (dir) {
  case 0:
//  XDrawLine(dpy, win, gc,
//	      border_x + grid_width * i, 
//	      border_y + grid_height * j,
//	      border_x + grid_width * (i+1), 
//	      border_y + grid_height * j);
	line(screen, addpt(screen->r.min, 
				Pt(border_x + grid_width * i, border_y + grid_height * j)),
			addpt(screen->r.min, 
				Pt(border_x + grid_width * (i+1), border_y + grid_height * j)),
			Endsquare, Endsquare, 0, display->white, ZP);
    break;
  case 1:
//    XDrawLine(dpy, win, gc,
//	      border_x + grid_width * (i+1), 
//	      border_y + grid_height * j,
//	      border_x + grid_width * (i+1), 
//	      border_y + grid_height * (j+1));
	line(screen, addpt(screen->r.min, 
				Pt(border_x + grid_width * (i+1), border_y + grid_height * j)),
			addpt(screen->r.min, 
				Pt(border_x + grid_width * (i+1), border_y + grid_height * (j+1))),
			Endsquare, Endsquare, 0, display->white, ZP);
    break;
  case 2:
//    XDrawLine(dpy, win, gc,
//	      border_x + grid_width * i, 
//	      border_y + grid_height * (j+1),
//	      border_x + grid_width * (i+1), 
//	      border_y + grid_height * (j+1));
	line(screen, addpt(screen->r.min, 
				Pt(border_x + grid_width *i, border_y + grid_height * (j+1))),
			addpt(screen->r.min, 
				Pt(border_x + grid_width * (i+1), border_y + grid_height * (j+1))),
			Endsquare, Endsquare, 0, display->white, ZP);
     break;
  case 3:
//    XDrawLine(dpy, win, gc,
//	      border_x + grid_width * i, 
//	      border_y + grid_height * j,
//	      border_x + grid_width * i, 
//	      border_y + grid_height * (j+1));
	line(screen, addpt(screen->r.min, 
				Pt(border_x + grid_width * i, border_y + grid_height * j)),
			addpt(screen->r.min, 
				Pt(border_x + grid_width * i, border_y + grid_height * (j+1))),
			Endsquare, Endsquare, 0, display->white, ZP);
     break;
  }
  if(sync_p)
    flushimage(display, 1);
}

/* Actually build a wall. */
static void
build_wall(i, j, dir)
     int i, j, dir;
{
  /* Draw it on the screen. */
  draw_wall(i, j, dir);
  /* Put it in the maze. */
  switch(dir)
    {
    case 0:
      maze[i][j] |= WALL_TOP;
      if(j>0)
	maze[i][j-1] |= WALL_BOTTOM;
      break;
    case 1:
      maze[i][j] |= WALL_RIGHT;
      if(i<maze_size_x-1)
	maze[i+1][j] |= WALL_LEFT;
      break;
    case 2:
      maze[i][j] |= WALL_BOTTOM;
      if(j<maze_size_y-1)
	maze[i][j+1] |= WALL_TOP;
      break;
    case 3:
      maze[i][j] |= WALL_LEFT;
      if(i>0)
	maze[i-1][j] |= WALL_RIGHT;
      break;
    }
}



static void
draw_solid_square(int i, int j,          /* draw a solid square in a square */
		  int dir, Image *c)
{
	Rectangle r;
	Point p;

  switch (dir) {
  case WALL_TOP:
//      XFillRectangle(dpy, win, gc,
//		     border_x + bw + grid_width * i, 
//		     border_y - bw + grid_height * j, 
//		     grid_width - (bw+bw), grid_height);

	p = addpt(screen->r.min, 
		Pt(border_x + bw + grid_width * i, border_y - bw + grid_height * j));
	r = Rpt(p, addpt(p, Pt(grid_width - (bw+bw), grid_height)));
	draw(screen, r, c, nil, ZP);

      break;
  case WALL_RIGHT:
//      XFillRectangle(dpy, win, gc,
//		     border_x + bw + grid_width * i, 
//		     border_y + bw + grid_height * j, 
//		     grid_width, grid_height - (bw+bw));
	p = addpt(screen->r.min, 
		Pt(border_x + bw + grid_width * i,  border_y + bw + grid_height * j));
	r = Rpt(p, addpt(p, Pt(grid_width, grid_height - (bw+bw))));
	draw(screen, r, c, nil, ZP);
      break;
  case WALL_BOTTOM:
//      XFillRectangle(dpy, win, gc,
//		     border_x + bw + grid_width * i, 
//		     border_y + bw + grid_height * j, 
//		     grid_width - (bw+bw), grid_height);
	p = addpt(screen->r.min, 
		Pt(border_x + bw + grid_width * i, border_y + bw + grid_height * j));
	r = Rpt(p, addpt(p, Pt(grid_width - (bw+bw), grid_height)));
	draw(screen, r, c, nil, ZP);
      break;
  case WALL_LEFT:
//      XFillRectangle(dpy, win, gc,
//		     border_x - bw + grid_width * i, 
//		     border_y + bw + grid_height * j, 
//		     grid_width, grid_height - (bw+bw));
	p = addpt(screen->r.min, 
		Pt(border_x - bw + grid_width * i, border_y + bw + grid_height * j));
	r = Rpt(p, addpt(p, Pt(grid_width , grid_height- (bw+bw))));
	draw(screen, r, c, nil, ZP);
      break;
  }
  flushimage(display, 1);
}

int
longdeadend_p(int x1, int y1, int x2, int y2, int endwall)
{
    int dx = x2 - x1, dy = y2 - y1;
    int sidewalls;

    sidewalls = endwall | (endwall >> 2 | endwall << 2);
    sidewalls = ~sidewalls & WALL_ANY;

    while((maze[x2][y2] & WALL_ANY) == sidewalls)
    {
	x2 += dx;
	y2 += dy;
    }

    if((maze[x2][y2] & WALL_ANY) == (sidewalls | endwall))
    {
	endwall = (endwall >> 2 | endwall << 2) & WALL_ANY;
	while(x1 != x2 || y1 != y2)
	{
	    x1 += dx;
	    y1 += dy;
	    draw_solid_square(x1, y1, endwall, skipColor);
	    maze[x1][y1] |= SOLVER_VISIT;
	}
	return 1;
    }
    else
	return 0;
}

/* Find all dead regions -- areas from which the goal cannot be reached --
   and mark them visited. */
void
find_dead_regions(void)
{
    int x, y, flipped;

    /* Find all not SOLVER_VISIT squares bordering NOT_DEAD squares
       and mark them NOT_DEAD also.  Repeat until no more such squares. */
    maze[start_x][start_y] |= NOT_DEAD;
    
    do
    {
	flipped = 0;
	for(x = 0; x < maze_size_x; x++)
	    for(y = 0; y < maze_size_y; y++)
		if(!(maze[x][y] & (SOLVER_VISIT | NOT_DEAD))
		   && (   (x && (maze[x-1][y] & NOT_DEAD))
		       || (y && (maze[x][y-1] & NOT_DEAD))))
		{
		    flipped = 1;
		    maze[x][y] |= NOT_DEAD;
		}
	for(x = maze_size_x-1; x >= 0; x--)
	    for(y = maze_size_y-1; y >= 0; y--)
		if(!(maze[x][y] & (SOLVER_VISIT | NOT_DEAD))
		   && (   (x != maze_size_x-1 && (maze[x+1][y] & NOT_DEAD))
		       || (y != maze_size_y-1 && (maze[x][y+1] & NOT_DEAD))))
		{
		    flipped = 1;
		    maze[x][y] |= NOT_DEAD;
		}
    }
    while(flipped);

    for (y = 0; y < maze_size_y; y++)
      for (x = 0; x < maze_size_x; x++)
      {
	if (maze[x][y] & NOT_DEAD)
	  maze[x][y] &= ~NOT_DEAD;
	else if (!(maze[x][y] & SOLVER_VISIT))
	{
	  maze[x][y] |= SOLVER_VISIT;
	  if((x < logo_x || x > logo_x + logo_width / grid_width) ||
	     (y < logo_y || y > logo_y + logo_height / grid_height))
	  {
	    if (!maze[x][y] & WALL_ANY) {
		Point p; Rectangle r;
//	      XFillRectangle(dpy, win, ugc,
//			     border_x + bw + grid_width * x,
//			     border_y + bw + grid_height * y,
//			     grid_width - (bw+bw), grid_height - (bw+bw));
		p = addpt(screen->r.min, 
			Pt(border_x + bw + grid_width * x, border_y + bw + grid_height * y));
		r = Rpt(p, addpt(p, Pt(grid_width - (bw+bw), grid_height- (bw+bw))));
		draw(screen, r, surroundColor, nil, ZP);

	    } else
	    {
	      if (! (maze[x][y] & WALL_LEFT))
		draw_solid_square(x, y, WALL_LEFT, surroundColor);
	      if (! (maze[x][y] & WALL_RIGHT))
		draw_solid_square(x, y, WALL_RIGHT, surroundColor);
	      if (! (maze[x][y] & WALL_TOP))
		draw_solid_square(x, y, WALL_TOP, surroundColor);
	      if (! (maze[x][y] & WALL_BOTTOM))
		draw_solid_square(x, y, WALL_BOTTOM, surroundColor);
	    }
	  }
	}
      }
    flushimage(display, 1);
}

static void
solve_maze (void)                     /* solve it with graphical feedback */
{
    int i, dir, from, x, y, ways, bt = 0;

    /* plug up the surrounding wall */
    maze[end_x][end_y] |= (WALL_TOP >> end_dir);
    
    /* initialize search path */
    i = 0;
    path[i].x = end_x;
    path[i].y = end_y;
    path[i].dir = 0;
    maze[end_x][end_y] |= SOLVER_VISIT;
    
    /* do it */
    while (1)
    {
	if ( maze[path[i].x][path[i].y] & START_SQUARE )
	    return;

	if(restart)
		return;

	if(ecanmouse()) {
		m = emouse();
		if(m.buttons&4) {
			if(emenuhit(3, &m, &menu) == 0) 
				exits(0);
		}
	}
	

	
	if (solve_delay) sleep (solve_delay);
	
	if(!path[i].dir)
	{
	    ways = 0;
	    /* First visit this square.  Which adjacent squares are open? */
	    for(dir = WALL_TOP; dir & WALL_ANY; dir >>= 1)
	    {
		if(maze[path[i].x][path[i].y] & dir)
		    continue;
		
		y = path[i].y - !!(dir & WALL_TOP) + !!(dir & WALL_BOTTOM);
		x = path[i].x + !!(dir & WALL_RIGHT) - !!(dir & WALL_LEFT);
		
		if(maze[x][y] & SOLVER_VISIT)
		    continue;
		
		from = (dir << 2 & WALL_ANY) | (dir >> 2 & WALL_ANY);
		/* don't enter obvious dead ends */
		if(((maze[x][y] & WALL_ANY) | from) != WALL_ANY)
		{
		    if(!longdeadend_p(path[i].x, path[i].y, x, y, dir))
			ways |= dir;
		}
		else
		{
		    draw_solid_square(x, y, from, skipColor);
		    maze[x][y] |= SOLVER_VISIT;
		}
	    }
	}
	else
	    ways = path[i].ways;
	/* ways now has a bitmask of open paths. */
	
	if(!ways)
	    goto backtrack;

	if (!ignorant_p)
	  {
	    x = path[i].x - start_x;
	    y = path[i].y - start_y;
	    /* choice one */
	    if(abs(y) <= abs(x))
	      dir = (x > 0) ? WALL_LEFT : WALL_RIGHT;
	    else
	      dir = (y > 0) ? WALL_TOP : WALL_BOTTOM;
	    
	    if(dir & ways)
	      goto found;
	    
	    /* choice two */
	    switch(dir)
	      {
	      case WALL_LEFT:
	      case WALL_RIGHT:
		dir = (y > 0) ? WALL_TOP : WALL_BOTTOM; break;
	      case WALL_TOP:
	      case WALL_BOTTOM:
		dir = (x > 0) ? WALL_LEFT : WALL_RIGHT;
	      }
	    
	    if(dir & ways)
	      goto found;
	    
	    /* choice three */
	    
	    dir = (dir << 2 & WALL_ANY) | (dir >> 2 & WALL_ANY);
	    if(dir & ways)
	      goto found;
	    
	    /* choice four */
	    dir = ways;
	    if(!dir)
	      goto backtrack;
	    
	  found: ;
	  }
	else
	  {
	    if(ways&WALL_TOP)
	      dir = WALL_TOP;
	    else if(ways&WALL_LEFT)
	      dir = WALL_LEFT;
	    else if(ways&WALL_BOTTOM)
	      dir = WALL_BOTTOM;
	    else if(ways&WALL_RIGHT)
	      dir = WALL_RIGHT;
	    else
	      goto backtrack;
	  }
	bt = 0;
	ways &= ~dir;  /* tried this one */
	
	y = path[i].y - !!(dir & WALL_TOP) + !!(dir & WALL_BOTTOM);
	x = path[i].x + !!(dir & WALL_RIGHT) - !!(dir & WALL_LEFT);
	
	/* advance in direction dir */
	path[i].dir = dir;
	path[i].ways = ways;
	draw_solid_square(path[i].x, path[i].y, dir, liveColor);
	
	i++;
	path[i].dir = 0;
	path[i].ways = 0;
	path[i].x = x;
	path[i].y = y;
	maze[x][y] |= SOLVER_VISIT;
	continue;

    backtrack:
	if(i == 0)
	{
	    print("Unsolvable maze.\n");
	    return;
	}

	if(!bt && !ignorant_p)
	    find_dead_regions();
	bt = 1;
	from = path[i-1].dir;
	from = (from << 2 & WALL_ANY) | (from >> 2 & WALL_ANY);
	
	draw_solid_square(path[i].x, path[i].y, from, deadColor);
	i--;
    }
} 



/*
 *  jmr additions for Jamie Zawinski's <jwz@jwz.org> screensaver stuff,
 *  note that the code above this has probably been hacked about in some
 *  arbitrary way.
 */



void
screenhack(void)
{
  int size, root, generator, this_gen;

  root = 0;
  solve_delay = 5;
  pre_solve_delay = 2000;
  post_solve_delay = 4000;
  generator = -1;
  max_length = 5;
  bridge_p = 0;
  ignorant_p = 0;

  size = 5 + (random () % 20);
  grid_width = grid_height = size;
  bw = (size > 6 ? 3 : (size-1)/2);


  x = 0;
  y = 0;

  set_maze_sizes (Dx(screen->r), Dy(screen->r));

  restart = root;

  sync_p = !(random() % 10);

  while (1) {                            /* primary execution loop [ rhess ] */
	if(ecanmouse()) {
		m = emouse();
		if(m.buttons&4) {
			if(emenuhit(3, &m, &menu) == 0) 
				exits(0);
		}
	}
    if (restart || stop) goto pop;
    switch (state) {
    case 1:
      initialize_maze();
      break;
    case 2:
      draw(screen, screen->r, display->black, nil, ZP);
      draw_maze_border();
	flushimage(display, 1);
      break;
    case 3:
      this_gen = generator;
      if(this_gen<0 || this_gen>2)
	this_gen = random()%3;

      switch(this_gen)
	{
	case 0:
	  create_maze();
	  break;
	case 1:
	  alt_create_maze();
	  break;
	case 2:
	  set_create_maze();
	  break;
	}
	flushimage(display, 1);
      break;
    case 4:
      sleep (pre_solve_delay);
      break;
    case 5:
      solve_maze();
      break;
    case 6:
      sleep (post_solve_delay);
      state = 0 ;
      draw(screen, screen->r, display->black, nil, ZP);
      break;
    default:
      abort ();
    }
    ++state;
  pop:
    if (restart)
      {
	restart = 0;
	stop = 0;
	state = 1;
	set_maze_sizes (Dx(screen->r), Dy(screen->r));

	draw(screen, screen->r, display->black, nil, ZP);
	flushimage(display, 1);
	sync_p = !(random() % 10);
      }
  }
}


void
eresized(int new)
{
	
	if(new && getwindow(display, Refnone) < 0) {
		sysfatal("can't reattach to window");
	}	
	//fprint(2, "sorry, cannot resize\n");
	//exits(0);
	restart = 1;
}


void 
main(int argc, char **argv)
{
	int fd;

	USED(argc, argv);
	srand(time(0));


	if(initdraw(nil, nil, "maze") < 0)
		sysfatal("initdraw failed: %r");

	liveColor = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DGreen);
	deadColor = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DRed);
	skipColor= allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DMagenta);
	surroundColor= allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DPaleblue);

	fd = open("/lib/face/48x48x4/g/glenda.1", OREAD);
	if(fd < 0)
		sysfatal("cannot open /lib/face/48x48x4/g/glenda.1: %r");

	glenda = readimage(display, fd, 0);
	if(glenda == nil)
		sysfatal("cannot load glenda's image: %r");

	draw(screen, screen->r, display->black, nil, ZP);
	flushimage(display, 1);

	einit(Emouse);
	eresized(0);
	screenhack();
}

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [9fans] draw() question
  2002-08-16 12:14 rob pike, esq.
@ 2002-08-17  8:45 ` andrey mirtchovski
  0 siblings, 0 replies; 3+ messages in thread
From: andrey mirtchovski @ 2002-08-17  8:45 UTC (permalink / raw)
  To: 9fans

[-- Attachment #1: Type: TEXT/PLAIN, Size: 699 bytes --]

it's not hard, i'm just (to quote george carlin) minimally exceptional.

XCopyArea, whose behaviour i tried to emulate, had it the other way
around and i was tricked by it :)

included is the end result following your help. getscr contains some
help routines which grab a screenshot and squeeze it into the current
window, decayscreen is another xscreensaver hack, which just mangles
the screen in interesting ways...

8c -FVw decayscreen.c getscr.c; 8l decayscreen.8 getscr.8

andrey

On Fri, 16 Aug 2002, rob pike, esq. wrote:

> Isn't it just
>
> draw(dst, Rect(x, y, x+(x2-x1), y+(y2-y1)), src, nil, Pt(x1, y1)) ?
>
> Why is this hard?  Or what have I missed?
>
> -rob
>

[-- Attachment #2: Type: TEXT/PLAIN, Size: 1947 bytes --]

#include <u.h>
#include <libc.h>
#include <draw.h>
#include "getscr.h"
/*
  * allocates temporary storage for and reads
  * /dev/screen... modified from of lens.c
  */
uchar *
getscr(void)
{
	char 			buf[5*12];
	ulong 		chan;
	uchar		*screenbuf;
	int 			d, screenfd;


	screenfd = open("/dev/screen", OREAD);
	if(screenfd < 0)
		sysfatal("can't open /dev/screen: %r");

	if(read(screenfd, buf, sizeof buf) != sizeof buf)
		sysfatal("can't read /dev/screen: %r");

	chan = strtochan(buf);
	d = chantodepth(chan);
	if(d < 8)
		sysfatal("can't handle screen format %11.11s", buf);

	bypp = d/8;
	screenr.min.x = atoi(buf+1*12);
	screenr.min.y = atoi(buf+2*12);
	screenr.max.x = atoi(buf+3*12);
	screenr.max.y = atoi(buf+4*12);

	screenbuf = malloc(bypp*Dx(screenr)*Dy(screenr));
	if(screenbuf == nil)
		sysfatal("screen buffer malloc failed: %r");

	if(readn(screenfd, screenbuf, bypp*Dx(screenr)*Dy(screenr)) != bypp*Dx(screenr)*Dy(screenr))
		sysfatal("can't read screen: %r");
	
	return screenbuf;
}


/*
  * squeeze the screenshot to fit in the current 
  * window
  */
Image *
squeeze(uchar *buf)
{
	int x, y, xx, yy, i;
	float dx, dy;
	Image *tmp;
	uchar *out;

	if(buf == nil)
		return nil;


	tmp = allocimage(display, screen->r, screen->chan, 0, DBlack);
	if(tmp == nil)
		sysfatal("can't allocate temp image");

	dx = (float)Dx(screenr)/Dx(tmp->r);
	dy = (float)Dy(screenr)/Dy(tmp->r);

	out = (uchar *)malloc(bypp*Dx(tmp->r)*Dy(tmp->r));
	if(out == nil)
		sysfatal("can't allocate temp memory");

	for(y=0; y<Dy(tmp->r); y++){
		for(x=0; x<Dx(tmp->r); x++){
			for(i=0; i<bypp; i++) {
				yy = (int)(dy*y);
				xx = (int)(dx*x);
				out[bypp*(y*Dx(tmp->r)+x) + i] = buf[bypp*(yy*Dx(screenr)+xx)+i];
			}
		}
	}
	
	if(loadimage(tmp, tmp->r, out, bypp*Dx(tmp->r)*Dy(tmp->r)) != bypp*Dx(tmp->r)*Dy(tmp->r)){
				sysfatal("loadimage");
	}
	return(tmp);
}

[-- Attachment #3: Type: TEXT/PLAIN, Size: 85 bytes --]

uchar*	getscr(void);
Image*	squeeze(uchar *);

Rectangle		screenr;
int 			bypp;

[-- Attachment #4: Type: TEXT/PLAIN, Size: 8537 bytes --]

/* xscreensaver, Copyright (c) 1992, 1993, 1994, 1996, 1997 
 * Jamie Zawinski <jwz@jwz.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or 
 * implied warranty.
 */

/* decayscreen
 *
 * Based on slidescreen program from the xscreensaver application and the
 * decay program for Sun framebuffers.  This is the comment from the decay.c
 * file:

 * decay.c
 *   find the screen bitmap for the console and make it "decay" by
 *   randomly shifting random rectangles by one pixelwidth at a time.
 *
 *   by David Wald, 1988
 *        rewritten by Natuerlich!
 *   based on a similar "utility" on the Apollo ring at Yale.

 * X version by
 *
 *  Vivek Khera <khera@cs.duke.edu>
 *  5-AUG-1993
 *
 *  Hacked by jwz, 28-Nov-97 (sped up and added new motion directions)
 
 *  R. Schultz
 *  Added "melt" & "stretch" modes 28-Mar-1999
 *
 */

/* 
  * ported to Plan 9 by andrey@lanl.gov, 08/02
  */

/* plan9-related stuff */
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>

/* screenshot-related stuff */
#include "getscr.h"	
static uchar *scrbuf;
static Image *scrimg;

/* other defines */
#define NULL nil
#define XPoint Point
#define NRAND nrand
#define LRAND lrand
#define random rand
#define MAXRAND ((2<<31)-1)
#define MAX(a, b) (((a) > (b))?(a):(b))
#define MIN(a, b) (((a) < (b))?(a):(b))
#define RAND_MAX MAXRAND
#define ABS abs
Image *colors[256];
#define M_PI	PI
#define Bool int
#define True 1
#define False 0

char *buttons[] = 
{
	"exit",
	0
};

Menu menu = 
{
	buttons
};
Mouse m;



/* end of plan9-related defines */

static int sizex, sizey;
static int delay = 1000;
static int mode;
static int iterations=1;

#define SHUFFLE 0
#define UP 1
#define LEFT 2
#define RIGHT 3
#define DOWN 4
#define UPLEFT 5
#define DOWNLEFT 6
#define UPRIGHT 7
#define DOWNRIGHT 8
#define IN 9
#define OUT 10
#define MELT 11
#define STRETCH 12
#define FUZZ 13

static void
init_decay (void)
{


    mode = random() % (FUZZ+1);

  delay = 10;




  sizex = Dx(screen->r);
  sizey = Dy(screen->r);

  
  if (mode == MELT || mode == STRETCH) {
    /* make sure screen eventually turns background color */
    line(screen, addpt(screen->r.min, Pt(0, 0)), 
			addpt(screen->r.min, Pt(sizex, 1)), 
			Endsquare, Endsquare, 0, display->black, ZP); 

    /* slow down for smoother melting*/
    iterations = 1;
  }

}


/*
 * perform one iteration of decay
 */
static void
decay1 (void)
{
    int left, top, width, height, toleft, totop;
	Rectangle r; Point p, p2;

#define L 101
#define R 102
#define U 103
#define D 104
    static int no_bias[]        = { L,L,L,L, R,R,R,R, U,U,U,U, D,D,D,D };
    static int up_bias[]        = { L,L,L,L, R,R,R,R, U,U,U,U, U,U,D,D };
    static int down_bias[]      = { L,L,L,L, R,R,R,R, U,U,D,D, D,D,D,D };
    static int left_bias[]      = { L,L,L,L, L,L,R,R, U,U,U,U, D,D,D,D };
    static int right_bias[]     = { L,L,R,R, R,R,R,R, U,U,U,U, D,D,D,D };

    static int upleft_bias[]    = { L,L,L,L, L,R,R,R, U,U,U,U, U,D,D,D };
    static int downleft_bias[]  = { L,L,L,L, L,R,R,R, U,U,U,D, D,D,D,D };
    static int upright_bias[]   = { L,L,L,R, R,R,R,R, U,U,U,U, U,D,D,D };
    static int downright_bias[] = { L,L,L,R, R,R,R,R, U,U,U,D, D,D,D,D };
    static int *bias;

    switch (mode) {
      case SHUFFLE:	bias = no_bias; break;
      case UP:		bias = up_bias; break;
      case LEFT:	bias = left_bias; break;
      case RIGHT:	bias = right_bias; break;
      case DOWN:	bias = down_bias; break;
      case UPLEFT:	bias = upleft_bias; break;
      case DOWNLEFT:	bias = downleft_bias; break;
      case UPRIGHT:	bias = upright_bias; break;
      case DOWNRIGHT:	bias = downright_bias; break;
      case IN:		bias = no_bias; break;
      case OUT:		bias = no_bias; break;
      case MELT:	bias = no_bias; break;
      case STRETCH:	bias = no_bias; break;
      case FUZZ:	bias = no_bias; break;
     default: abort();
    }

#define nrnd(x) (random() % (x))

    if (mode == MELT || mode == STRETCH) {
      left = nrnd(sizex/2);
      top = nrnd(sizey);
      width = nrnd( sizex/2 ) + sizex/2 - left;
      height = nrnd(sizey - top);
      toleft = left;
      totop = top+1;

    } else if (mode == FUZZ) {  /* By Vince Levey <vincel@vincel.org>;
                                   inspired by the "melt" mode of the
                                   "scrhack" IrisGL program by Paul Haeberli
                                   circa 1991. */
      static int toggle = 0;

      left = nrnd(sizex - 1);
      top  = nrnd(sizey - 1);
      toggle = !toggle;
      if (toggle)
        {
          totop = top;
          height = 1;
          toleft = nrnd(sizex - 1);
          if (toleft > left)
            {
              width = toleft-left;
              toleft = left;
              left++;
            }
          else
            {
              width = left-toleft;
              left = toleft;
              toleft++;
            }
        }
      else
        {
          toleft = left;
          width = 1;
          totop  = nrnd(sizey - 1);
          if (totop > top)
            {
              height = totop-top;
              totop = top;
              top++;
            }
          else
            {
              height = top-totop;
              top = totop;
              totop++;
            }
        }

    } else {

      left = nrnd(sizex - 1);
      top = nrnd(sizey);
      width = nrnd(sizex - left);
      height = nrnd(sizey - top);
      
      toleft = left;
      totop = top;
      if (mode == IN || mode == OUT) {
	int x = left+(width/2);
	int y = top+(height/2);
	int cx = sizex/2;
	int cy = sizey/2;
	if (mode == IN) {
	  if      (x > cx && y > cy)   bias = upleft_bias;
	  else if (x < cx && y > cy)   bias = upright_bias;
	  else if (x < cx && y < cy)   bias = downright_bias;
	  else /* (x > cx && y < cy)*/ bias = downleft_bias;
	} else {
	  if      (x > cx && y > cy)   bias = downright_bias;
	  else if (x < cx && y > cy)   bias = downleft_bias;
	  else if (x < cx && y < cy)   bias = upleft_bias;
	  else /* (x > cx && y < cy)*/ bias = upright_bias;
	}
      }
      
      switch (bias[random() % (sizeof(no_bias)/sizeof(*no_bias))]) {
      case L: toleft = left-1; break;
      case R: toleft = left+1; break;
      case U: totop = top-1; break;
      case D: totop = top+1; break;
      default: abort(); break;
      }
    }
    
	r = screen->r;
	if (mode == STRETCH) {
		p = addpt(r.min, Pt(0, sizey-top-1));
		p2 = addpt(r.min, Pt(0, sizey-top-2));
		r = Rpt(p, addpt(Pt(sizex, top+1), p));
//		XCopyArea (dpy, window, window, gc, 0, sizey-top-2, sizex, top+1, 
//		0, sizey-top-1); 
   	} else {
		p = addpt(r.min, Pt(toleft, totop));
		p2 = addpt(r.min, Pt(left, top));
		r = Rpt(p, addpt(Pt(width, height), p));
//		XCopyArea (dpy, window, window, gc, left, top, width, height,
//		 toleft, totop);
	}

	draw(screen, r, screen, nil, p2);

#undef nrnd
}


void
screenhack (void)
{
	init_decay ();
	while (1) {
		int i;
		for (i = 0; i < iterations; i++)
			decay1 ();
		flushimage(display, 1);
		if(ecanmouse()) {
			m = emouse();
			if(m.buttons&4) {
				if(emenuhit(3, &m, &menu) == 0) 
					exits(0);
			}
		}
		if (delay) sleep (delay);
	}
}


void
eresized(int new)
{
	
	if(new && getwindow(display, Refnone) < 0) {
		sysfatal("can't reattach to window: %r");
	}

	if(scrimg != nil)
		freeimage(scrimg);
	
	scrimg = squeeze(scrbuf);
	if(scrimg == nil)
		sysfatal("unable to fit screenshot to window: %r");

	init_decay();
	draw(screen, screen->r, scrimg, nil, scrimg->r.min);
}



void 
main(int argc, char **argv)
{

	USED(argc, argv);
	srand(time(0));

	scrbuf = getscr();
	if(scrbuf == nil)
		sysfatal("could not obtain screenshot: %r");

	if(initdraw(nil, nil, "decayscreen") < 0)
		sysfatal("initdraw failed: %r");

	einit(Emouse);
	eresized(0);

	screenhack();
}

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [9fans] draw() question
@ 2002-08-16 12:14 rob pike, esq.
  2002-08-17  8:45 ` andrey mirtchovski
  0 siblings, 1 reply; 3+ messages in thread
From: rob pike, esq. @ 2002-08-16 12:14 UTC (permalink / raw)
  To: 9fans

[-- Attachment #1: Type: text/plain, Size: 139 bytes --]

Isn't it just

draw(dst, Rect(x, y, x+(x2-x1), y+(y2-y1)), src, nil, Pt(x1, y1)) ?

Why is this hard?  Or what have I missed?

-rob

[-- Attachment #2: Type: message/rfc822, Size: 59881 bytes --]

[-- Attachment #2.1.1: Type: TEXT/PLAIN, Size: 701 bytes --]

hi,

i'm stuck trying to do the following with the
already existing plan9 functions from draw.h:

in short, i want to do a draw() to an image
using only a specific rectangle in the source
image, i.e. a call to draw which puts at
dst(x, y) the rectangle Rect(x1, y1, x2, y2)
from src..

so far i can clumsily simulate that by setting
src's clipr to the wanted rectangle and then
aligning the source so that src->clipr.min is
at (x, y) in the draw() operation. somehow,
though, i don't think this is the easiest way :)


i'm sure the answer is very simple, andrey

ps: i'm attaching maze.c for your enjoyment,
it'll probably appear on the xscreensaver ports
site in a few days...

[-- Attachment #2.1.2: Type: TEXT/PLAIN, Size: 41495 bytes --]

/******************************************************************************
 * [ maze ] ...
 *
 * modified:  [ 1-04-00 ]  Johannes Keukelaar <johannes@nada.kth.se>
 *              Added -ignorant option (not the default) to remove knowlege
 *              of the direction in which the exit lies.
 *
 * modified:  [ 6-28-98 ]  Zack Weinberg <zack@rabi.phys.columbia.edu>
 *
 *              Made the maze-solver somewhat more intelligent.  There are
 *              three optimizations:
 *
 *              - Straight-line lookahead: the solver does not enter dead-end
 *                corridors.  This is a win with all maze generators.
 *
 *              - First order direction choice: the solver knows where the
 *                exit is in relation to itself, and will try paths leading in
 *                that direction first. This is a major win on maze generator 1
 *                which tends to offer direct routes to the exit.
 *
 *              - Dead region elimination: the solver already has a map of
 *                all squares visited.  Whenever it starts to backtrack, it
 *                consults this map and marks off all squares that cannot be
 *                reached from the exit without crossing a square already
 *                visited.  Those squares can never contribute to the path to
 *                the exit, so it doesn't bother checking them.  This helps a
 *                lot with maze generator 2 and somewhat less with generator 1.
 *
 *              Further improvements would require knowledge of the wall map
 *              as well as the position of the exit and the squares visited.
 *              I would consider that to be cheating.  Generator 0 makes
 *              mazes which are remarkably difficult to solve mechanically --
 *              even with these optimizations the solver generally must visit
 *              at least two-thirds of the squares.  This is partially
 *              because generator 0's mazes have longer paths to the exit.
 *
 * modified:  [ 4-10-97 ]  Johannes Keukelaar <johannes@nada.kth.se>
 *              Added multiple maze creators. Robustified solver.
 *              Added bridge option.
 * modified:  [ 8-11-95 ] Ed James <james@mml.mmc.com>
 *              added fill of dead-end box to solve_maze while loop.
 * modified:  [ 3-7-93 ]  Jamie Zawinski <jwz@jwz.org>
 *		added the XRoger logo, cleaned up resources, made
 *		grid size a parameter.
 * modified:  [ 3-3-93 ]  Jim Randell <jmr@mddjmr.fc.hp.com>
 *		Added the colour stuff and integrated it with jwz's
 *		screenhack stuff.  There's still some work that could
 *		be done on this, particularly allowing a resource to
 *		specify how big the squares are.
 * modified:  [ 10-4-88 ]  Richard Hess    ...!uunet!cimshop!rhess  
 *              [ Revised primary execution loop within main()...
 *              [ Extended X event handler, check_events()...
 * modified:  [ 1-29-88 ]  Dave Lemke      lemke@sun.com  
 *              [ Hacked for X11...
 *              [  Note the word "hacked" -- this is extremely ugly, but at 
 *              [   least it does the job.  NOT a good programming example 
 *              [   for X.
 * original:  [ 6/21/85 ]  Martin Weiss    Sun Microsystems  [ SunView ]
 *
 ******************************************************************************
 Copyright 1988 by Sun Microsystems, Inc. Mountain View, CA.
  
 All Rights Reserved
  
 Permission to use, copy, modify, and distribute this software and its
 documentation for any purpose and without fee is hereby granted, 
 provided that the above copyright notice appear in all copies and that
 both that copyright notice and this permission notice appear in 
 supporting documentation, and that the names of Sun or MIT not be
 used in advertising or publicity pertaining to distribution of the
 software without specific prior written permission. Sun and M.I.T. 
 make no representations about the suitability of this software for 
 any purpose. It is provided "as is" without any express or implied warranty.
 
 SUN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 PURPOSE. IN NO EVENT SHALL SUN BE LIABLE FOR ANY SPECIAL, INDIRECT
 OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
 OR PERFORMANCE OF THIS SOFTWARE.
 *****************************************************************************/


/* 
  * ported to Plan 9 by andrey@lanl.gov, 08/02
  */

/* plan9-related stuff */
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>

#define NULL nil
#define XPoint Point
#define NRAND nrand
#define LRAND lrand
#define random rand
#define MAXRAND ((2<<31)-1)
#define MAX(a, b) (((a) > (b))?(a):(b))
#define MIN(a, b) (((a) < (b))?(a):(b))
#define RAND_MAX MAXRAND
#define ABS abs
Image *colors[256];
#define M_PI	PI
#define Bool int
#define True 1
#define False 0

Image *liveColor, *deadColor, *skipColor, *surroundColor;

Image *glenda;

char *buttons[] = 
{
	"exit",
	0
};

Menu menu = 
{
	buttons
};
Mouse m;
/* end of plan9-related defines */


#define XSCREENSAVER_LOGO

static int solve_delay, pre_solve_delay, post_solve_delay;

#define MAX_MAZE_SIZE_X	500
#define MAX_MAZE_SIZE_Y	500

#define MOVE_LIST_SIZE  (MAX_MAZE_SIZE_X * MAX_MAZE_SIZE_Y)

#define NOT_DEAD	0x8000
#define SOLVER_VISIT    0x4000
#define START_SQUARE	0x2000
#define END_SQUARE	0x1000

#define WALL_TOP	0x8
#define WALL_RIGHT	0x4
#define WALL_BOTTOM	0x2
#define WALL_LEFT	0x1
#define WALL_ANY	0xF

#define DOOR_IN_TOP	0x800
#define DOOR_IN_RIGHT	0x400
#define DOOR_IN_BOTTOM	0x200
#define DOOR_IN_LEFT	0x100
#define DOOR_IN_ANY	0xF00

#define DOOR_OUT_TOP	0x80
#define DOOR_OUT_RIGHT	0x40
#define DOOR_OUT_BOTTOM	0x20
#define DOOR_OUT_LEFT	0x10


#define	border_x        (0)
#define	border_y        (0)

#define	get_random(x)	(random() % (x))


static int logo_x, logo_y;

# define logo_width  48
# define logo_height 48

static unsigned short maze[MAX_MAZE_SIZE_X][MAX_MAZE_SIZE_Y];

static struct {
  unsigned char x;
  unsigned char y;
  unsigned char dir, ways;
} move_list[MOVE_LIST_SIZE], save_path[MOVE_LIST_SIZE], path[MOVE_LIST_SIZE];

static int maze_size_x, maze_size_y;
static int sqnum, cur_sq_x, cur_sq_y, path_length;
static int start_x, start_y, start_dir, end_x, end_y, end_dir;
static int grid_width, grid_height;
static int bw;

static int	x = 0, y = 0, restart = 0, stop = 0, state = 1, max_length;
static int      sync_p, bridge_p, ignorant_p;


static void
set_maze_sizes (int width, int height)
{
  maze_size_x = width / grid_width;
  maze_size_y = height / grid_height;
}


static void
initialize_maze (void) /* draw the surrounding wall and start/end squares */
{
  register int i, j, wall;
  int logow = 1 + logo_width / grid_width;
  int logoh = 1 + logo_height / grid_height;
  
  /* initialize all squares */
  for ( i=0; i<maze_size_x; i++) {
    for ( j=0; j<maze_size_y; j++) {
      maze[i][j] = 0;
    }
  }
  
  /* top wall */
  for ( i=0; i<maze_size_x; i++ ) {
    maze[i][0] |= WALL_TOP;
  }
  
  /* right wall */
  for ( j=0; j<maze_size_y; j++ ) {
    maze[maze_size_x-1][j] |= WALL_RIGHT;
  }
  
  /* bottom wall */
  for ( i=0; i<maze_size_x; i++ ) {
    maze[i][maze_size_y-1] |= WALL_BOTTOM;
  }
  
  /* left wall */
  for ( j=0; j<maze_size_y; j++ ) {
    maze[0][j] |= WALL_LEFT;
  }
  
  /* set start square */
  wall = get_random(4);
  switch (wall) {
  case 0:	
    i = get_random(maze_size_x);
    j = 0;
    break;
  case 1:	
    i = maze_size_x - 1;
    j = get_random(maze_size_y);
    break;
  case 2:	
    i = get_random(maze_size_x);
    j = maze_size_y - 1;
    break;
  case 3:	
    i = 0;
    j = get_random(maze_size_y);
    break;
  }
  maze[i][j] |= START_SQUARE;
  maze[i][j] |= ( DOOR_IN_TOP >> wall );
  maze[i][j] &= ~( WALL_TOP >> wall );
  cur_sq_x = i;
  cur_sq_y = j;
  start_x = i;
  start_y = j;
  start_dir = wall;
  sqnum = 0;
  
  /* set end square */
  wall = (wall + 2)%4;
  switch (wall) {
  case 0:
    i = get_random(maze_size_x);
    j = 0;
    break;
  case 1:
    i = maze_size_x - 1;
    j = get_random(maze_size_y);
    break;
  case 2:
    i = get_random(maze_size_x);
    j = maze_size_y - 1;
    break;
  case 3:
    i = 0;
    j = get_random(maze_size_y);
    break;
  }
  maze[i][j] |= END_SQUARE;
  maze[i][j] |= ( DOOR_OUT_TOP >> wall );
  maze[i][j] &= ~( WALL_TOP >> wall );
  end_x = i;
  end_y = j;
  end_dir = wall;
  
  /* set logo */
  if ((maze_size_x-logow >= 6) && (maze_size_y-logoh >= 6))
    {
      /* not closer than 3 grid units from a wall */
      logo_x = get_random (maze_size_x - logow - 5) + 3;
      logo_y = get_random (maze_size_y - logoh - 5) + 3;
      for (i=0; i<logow; i++)
	for (j=0; j<logoh; j++)
	  maze[logo_x + i][logo_y + j] |= DOOR_IN_TOP;
    }
  else
    logo_y = logo_x = -1;
}

static int choose_door (void);
static int backup (void);
static void draw_wall (int, int, int);
static void draw_solid_square (int, int, int, Image*);
/*static void enter_square (int);*/
static void build_wall (int, int, int);
/*static void break_wall (int, int, int);*/

static void join_sets(int, int);

/* For set_create_maze. */
/* The sets that our squares are in. */
static int *sets = 0;
/* The `list' of hedges. */
static int *hedges = 0;


/* Initialise the sets. */
static void 
init_sets(void)
{
  int i, t, r, x, y;

  if(sets)
    free(sets);
  sets = (int *)malloc(maze_size_x*maze_size_y*sizeof(int));
  if(!sets)
    abort();
  for(i = 0; i < maze_size_x*maze_size_y; i++)
    {
      sets[i] = i;
    }
  
  if(hedges)
    free(hedges);
  hedges = (int *)malloc(maze_size_x*maze_size_y*2*sizeof(int));
  if(!hedges)
    abort();
  for(i = 0; i < maze_size_x*maze_size_y*2; i++)
    {
      hedges[i] = i;
    }
  /* Mask out outside walls. */
  for(i = 0; i < maze_size_y; i++)
    {
      hedges[2*((maze_size_x)*i+maze_size_x-1)+1] = -1;
    }
  for(i = 0; i < maze_size_x; i++)
    {
      hedges[2*((maze_size_y-1)*maze_size_x+i)] = -1;
    }
  /* Mask out a possible logo. */
  if(logo_x!=-1)
    {
      int logow = 1 + logo_width / grid_width;
      int logoh = 1 + logo_height / grid_height;
      int bridge_dir, bridge_c;

      if(bridge_p && logoh>=3 && logow>=3)
	{
	  bridge_dir = 1+random()%2;
	  if(bridge_dir==1)
	    {
	      bridge_c = logo_y+random()%(logoh-2)+1;
	    }
	  else
	    {
	      bridge_c = logo_x+random()%(logow-2)+1;
	    }
	}
      else
	{
	  bridge_dir = 0;
	  bridge_c = -1;
	}

      for(x = logo_x; x < logo_x+logow; x++)
	for(y = logo_y; y < logo_y+logoh; y++)
	  {
	    /* I should check for the bridge here, except that I join the
             * bridge together below.
             */
	    hedges[2*(x+maze_size_x*y)+1] = -1;
	    hedges[2*(x+maze_size_x*y)] = -1;
	  }
      for(x = logo_x; x < logo_x+logow; x++)
	{
	  if(!(bridge_dir==2 && x==bridge_c))
	    {
	      build_wall(x, logo_y, 0);
	      build_wall(x, logo_y+logoh, 0);
	    }
	  hedges[2*(x+maze_size_x*(logo_y-1))] = -1;
	  if(bridge_dir==1)
	    {
	      build_wall(x, bridge_c, 0);
	      build_wall(x, bridge_c, 2);
	    }
	}
      for(y = logo_y; y < logo_y+logoh; y++)
	{
	  if(!(bridge_dir==1 && y==bridge_c))
	    {
	      build_wall(logo_x, y, 3);
	      build_wall(logo_x+logow, y, 3);
	    }
	  hedges[2*(logo_x-1+maze_size_x*y)+1] = -1;
	  if(bridge_dir==2)
	    {
	      build_wall(bridge_c, y, 1);
	      build_wall(bridge_c, y, 3);
	    }
	}
      /* Join the whole bridge together. */
      if(bridge_p)
	{
	  if(bridge_dir==1)
	    {
	      x = logo_x-1;
	      y = bridge_c;
	      for(i = logo_x; i < logo_x+logow+1; i++)
		join_sets(x+y*maze_size_x, i+y*maze_size_x);
	    }
	  else
	    {
	      y = logo_y-1;
	      x = bridge_c;
	      for(i = logo_y; i < logo_y+logoh+1; i++)
		join_sets(x+y*maze_size_x, x+i*maze_size_x);
	    }
	}
    }

  for(i = 0; i < maze_size_x*maze_size_y*2; i++)
    {
      t = hedges[i];
      r = random()%(maze_size_x*maze_size_y*2);
      hedges[i] = hedges[r];
      hedges[r] = t;
    }
}

/* Get the representative of a set. */
static int
get_set(int num)
{
  int s;

  if(sets[num]==num)
    return num;
  else
    {
      s = get_set(sets[num]);
      sets[num] = s;
      return s;
    }
}

/* Join two sets together. */
static void
join_sets(num1, num2)
     int num1, num2;
{
  int s1, s2;

  s1 = get_set(num1);
  s2 = get_set(num2);
  
  if(s1<s2)
    sets[s2] = s1;
  else
    sets[s1] = s2;
}

/* Exitialise the sets. */
static void
exit_sets(void)
{
  if(hedges)
    free(hedges);
  hedges = 0;
  if(sets)
    free(sets);
  sets = 0;
}


/* Second alternative maze creator: Put each square in the maze in a 
 * separate set. Also, make a list of all the hedges. Randomize that list.
 * Walk through the list. If, for a certain hedge, the two squares on both
 * sides of it are in different sets, union the sets and remove the hedge.
 * Continue until all hedges have been processed or only one set remains.
 */
static void
set_create_maze(void)
{
  int i, h, x, y, dir, v, w;

  /* Do almost all the setup. */
  init_sets();

  /* Start running through the hedges. */
  for(i = 0; i < 2*maze_size_x*maze_size_y; i++)
    { 
      h = hedges[i];

      /* This one is in the logo or outside border. */
      if(h==-1)
	continue;

      dir = h%2?1:2;
      x = (h>>1)%maze_size_x;
      y = (h>>1)/maze_size_x;

      v = x;
      w = y;
      switch(dir)
	{
	case 1:
	  v++;
	  break;
	case 2:
	  w++;
	  break;
	}


      if(get_set(x+y*maze_size_x)!=get_set(v+w*maze_size_x))
	{

	  join_sets(x+y*maze_size_x, v+w*maze_size_x);
	  /* Don't draw the wall. */
	}
      else
	{

	  /* Don't join the sets. */
	  build_wall(x, y, dir); 
	}
    }

  /* Free some memory. */
  exit_sets();
}

/* First alternative maze creator: Pick a random, empty corner in the maze.
 * Pick a random direction. Draw a wall in that direction, from that corner
 * until we hit a wall. Option: Only draw the wall if it's going to be 
 * shorter than a certain length. Otherwise we get lots of long walls.
 */
static void
alt_create_maze(void)
{
  char *corners;
  int *c_idx;
  int i, j, height, width, open_corners, k, dir, x, y;

  height = maze_size_y+1;
  width = maze_size_x+1;

  /* Allocate and clear some mem. */
  corners = (char *)calloc(height*width, 1);
  if(!corners)
    return;

  /* Set up the indexing array. */
  c_idx = (int *)malloc(sizeof(int)*height*width);
  if(!c_idx)
    {
      free(corners);
      return;
    }
  for(i = 0; i < height*width; i++)
    c_idx[i] = i;
  for(i = 0; i < height*width; i++)
    {
      j = c_idx[i];
      k = random()%(height*width);
      c_idx[i] = c_idx[k];
      c_idx[k] = j;
    }

  /* Set up some initial walls. */
  /* Outside walls. */
  for(i = 0; i < width; i++)
    {
      corners[i] = 1;
      corners[i+width*(height-1)] = 1;
    }
  for(i = 0; i < height; i++)
    {
      corners[i*width] = 1;
      corners[i*width+width-1] = 1;
    }
  /* Walls around logo. In fact, inside the logo, too. */
  /* Also draw the walls. */
  if(logo_x!=-1)
    {
      int logow = 1 + logo_width / grid_width;
      int logoh = 1 + logo_height / grid_height;
      int bridge_dir, bridge_c;

      if(bridge_p && logoh>=3 && logow>=3)
	{
	  bridge_dir = 1+random()%2;
	  if(bridge_dir==1)
	    {
	      bridge_c = logo_y+random()%(logoh-2)+1;
	    }
	  else
	    {
	      bridge_c = logo_x+random()%(logow-2)+1;
	    }
	}
      else
	{
	  bridge_dir = 0;
	  bridge_c = -1;
	}
      for(i = logo_x; i <= logo_x + logow; i++)
	{
	  for(j = logo_y; j <= logo_y + logoh; j++)
	    {
	      corners[i+width*j] = 1;
	    }
	}
      for(x = logo_x; x < logo_x+logow; x++)
	{
	  if(!(bridge_dir==2 && x==bridge_c))
	    {
	      build_wall(x, logo_y, 0);
	      build_wall(x, logo_y+logoh, 0);
	    }
	  if(bridge_dir==1)
	    {
	      build_wall(x, bridge_c, 0);
	      build_wall(x, bridge_c, 2);
	    }
	}
      for(y = logo_y; y < logo_y+logoh; y++)
	{
	  if(!(bridge_dir==1 && y==bridge_c))
	    {
	      build_wall(logo_x, y, 3);
	      build_wall(logo_x+logow, y, 3);
	    }
	  if(bridge_dir==2)
	    {
	      build_wall(bridge_c, y, 1);
	      build_wall(bridge_c, y, 3);
	    }
	}
      /* Connect one wall of the logo with an outside wall. */
      if(bridge_p)
	dir = (bridge_dir+1)%4;
      else
	dir = random()%4;
      switch(dir)
	{
	case 0:
	  x = logo_x+(random()%(logow+1));
	  y = logo_y;
	  break;
	case 1:
	  x = logo_x+logow;
	  y = logo_y+(random()%(logoh+1));
	  break;
	case 2:
	  x = logo_x+(random()%(logow+1));
	  y = logo_y+logoh;
	  break;
	case 3:
	  x = logo_x;
	  y = logo_y+(random()%(logoh+1));
	  break;
	}
      do
	{
	  corners[x+width*y] = 1;
	  switch(dir)
	    {
	    case 0:
	      build_wall(x-1, y-1, 1);
	      y--;
	      break;
	    case 1:
	      build_wall(x, y, 0);
	      x++;
	      break;
	    case 2:
	      build_wall(x, y, 3);
	      y++;
	      break;
	    case 3:
	      build_wall(x-1, y-1, 2);
	      x--;
	      break;	  
	    }
	}
      while(!corners[x+width*y]);
      if(bridge_p)
	{
	  dir = (dir+2)%4;
	  switch(dir)
	    {
	    case 0:
	      x = logo_x+(random()%(logow+1));
	      y = logo_y;
	      break;
	    case 1:
	      x = logo_x+logow;
	      y = logo_y+(random()%(logoh+1));
	      break;
	    case 2:
	      x = logo_x+(random()%(logow+1));
	      y = logo_y+logoh;
	      break;
	    case 3:
	      x = logo_x;
	      y = logo_y+(random()%(logoh+1));
	      break;
	    }
	  do
	    {
	      corners[x+width*y] = 1;
	      switch(dir)
		{
		case 0:
		  build_wall(x-1, y-1, 1);
		  y--;
		  break;
		case 1:
		  build_wall(x, y, 0);
		  x++;
		  break;
		case 2:
		  build_wall(x, y, 3);
		  y++;
		  break;
		case 3:
		  build_wall(x-1, y-1, 2);
		  x--;
		  break;	  
		}
	    }
	  while(!corners[x+width*y]);
	}
    }

  /* Count open gridpoints. */
  open_corners = 0;
  for(i = 0; i < width; i++)
    for(j = 0; j < height; j++)
      if(!corners[i+width*j])
	open_corners++;

  /* Now do actual maze generation. */
  while(open_corners>0)
    {
      for(i = 0; i < width*height; i++)
	{
	  if(!corners[c_idx[i]])
	    {
	      x = c_idx[i]%width;
	      y = c_idx[i]/width;
	      /* Choose a random direction. */
	      dir = random()%4;
	      
	      k = 0;
	      /* Measure the length of the wall we'd draw. */
	      while(!corners[x+width*y])
		{
		  k++;
		  switch(dir)
		    {
		    case 0:
		      y--;
		      break;
		    case 1:
		      x++;
		      break;
		    case 2:
		      y++;
		      break;
		    case 3:
		      x--;
		      break;
		    }
		}
	      
	      if(k<=max_length)
		{
		  x = c_idx[i]%width;
		  y = c_idx[i]/width;
		  
		  /* Draw a wall until we hit something. */
		  while(!corners[x+width*y])
		    {
		      open_corners--;
		      corners[x+width*y] = 1;
		      switch(dir)
			{
			case 0:
			  build_wall(x-1, y-1, 1);
			  y--;
			  break;
			case 1:
			  build_wall(x, y, 0);
			  x++;
			  break;
			case 2:
			  build_wall(x, y, 3);
			  y++;
			  break;
			case 3:
			  build_wall(x-1, y-1, 2);
			  x--;
			  break;
			}
		    }
		}
	    }
	}
    }

  /* Free some memory we used. */
  free(corners);
  free(c_idx);
}

/* The original maze creator. Start somewhere. Take a step in a random 
 * direction. Keep doing this until we hit a wall. Then, backtrack until
 * we find a point where we can go in another direction.
 */
static void
create_maze (void)    /* create a maze layout given the initialized maze */
{
  register int i, newdoor = 0;
  int logow = 1 + logo_width / grid_width;
  int logoh = 1 + logo_height / grid_height;
  
  /* Maybe we should make a bridge? */
  if(bridge_p && logo_x>=0 && logow>=3 && logoh>=3)
    {
      int bridge_dir, bridge_c;

      bridge_dir = 1+random()%2;
      if(bridge_dir==1)
	{
	  if(logoh>=3)
	    bridge_c = logo_y+random()%(logoh-2)+1;
	  else
	    bridge_c = logo_y+random()%logoh;
	}
      else
	{
	  if(logow>=3)
	    bridge_c = logo_x+random()%(logow-2)+1;
	  else
	    bridge_c = logo_x+random()%logow;
	}

      if(bridge_dir==1)
	{
	  for(i = logo_x; i < logo_x+logow; i++)
	    {
	      maze[i][bridge_c] &= ~DOOR_IN_TOP;
	    }
	}
      else
	{
	  for(i = logo_y; i < logo_y+logoh; i++)
	    {
	      maze[bridge_c][i] &= ~DOOR_IN_TOP;
	    }
	}
    }

  do {
    move_list[sqnum].x = cur_sq_x;
    move_list[sqnum].y = cur_sq_y;
    move_list[sqnum].dir = newdoor;
    while ( ( newdoor = choose_door() ) == -1 ) { /* pick a door */
      if ( backup() == -1 ) { /* no more doors ... backup */
	return; /* done ... return */
      }
    }
    
    /* mark the out door */
    maze[cur_sq_x][cur_sq_y] |= ( DOOR_OUT_TOP >> newdoor );
    
    switch (newdoor) {
    case 0: cur_sq_y--;
      break;
    case 1: cur_sq_x++;
      break;
    case 2: cur_sq_y++;
      break;
    case 3: cur_sq_x--;
      break;
    }
    sqnum++;
    
    /* mark the in door */
    maze[cur_sq_x][cur_sq_y] |= ( DOOR_IN_TOP >> ((newdoor+2)%4) );
    
    /* if end square set path length and save path */
    if ( maze[cur_sq_x][cur_sq_y] & END_SQUARE ) {
      path_length = sqnum;
      for ( i=0; i<path_length; i++) {
	save_path[i].x = move_list[i].x;
	save_path[i].y = move_list[i].y;
	save_path[i].dir = move_list[i].dir;
      }
    }
    
  } while (1);
  
}


static int
choose_door (void)                                   /* pick a new path */
{
  int candidates[3];
  register int num_candidates;
  
  num_candidates = 0;
  
  /* top wall */
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_TOP )
    goto rightwall;
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_TOP )
    goto rightwall;
  if ( maze[cur_sq_x][cur_sq_y] & WALL_TOP )
    goto rightwall;
  if ( maze[cur_sq_x][cur_sq_y - 1] & DOOR_IN_ANY ) {
    maze[cur_sq_x][cur_sq_y] |= WALL_TOP;
    maze[cur_sq_x][cur_sq_y - 1] |= WALL_BOTTOM;
    draw_wall(cur_sq_x, cur_sq_y, 0);
    goto rightwall;
  }
  candidates[num_candidates++] = 0;
  
 rightwall:
  /* right wall */
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_RIGHT )
    goto bottomwall;
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_RIGHT )
    goto bottomwall;
  if ( maze[cur_sq_x][cur_sq_y] & WALL_RIGHT )
    goto bottomwall;
  if ( maze[cur_sq_x + 1][cur_sq_y] & DOOR_IN_ANY ) {
    maze[cur_sq_x][cur_sq_y] |= WALL_RIGHT;
    maze[cur_sq_x + 1][cur_sq_y] |= WALL_LEFT;
    draw_wall(cur_sq_x, cur_sq_y, 1);
    goto bottomwall;
  }
  candidates[num_candidates++] = 1;
  
 bottomwall:
  /* bottom wall */
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_BOTTOM )
    goto leftwall;
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_BOTTOM )
    goto leftwall;
  if ( maze[cur_sq_x][cur_sq_y] & WALL_BOTTOM )
    goto leftwall;
  if ( maze[cur_sq_x][cur_sq_y + 1] & DOOR_IN_ANY ) {
    maze[cur_sq_x][cur_sq_y] |= WALL_BOTTOM;
    maze[cur_sq_x][cur_sq_y + 1] |= WALL_TOP;
    draw_wall(cur_sq_x, cur_sq_y, 2);
    goto leftwall;
  }
  candidates[num_candidates++] = 2;
  
 leftwall:
  /* left wall */
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_IN_LEFT )
    goto donewall;
  if ( maze[cur_sq_x][cur_sq_y] & DOOR_OUT_LEFT )
    goto donewall;
  if ( maze[cur_sq_x][cur_sq_y] & WALL_LEFT )
    goto donewall;
  if ( maze[cur_sq_x - 1][cur_sq_y] & DOOR_IN_ANY ) {
    maze[cur_sq_x][cur_sq_y] |= WALL_LEFT;
    maze[cur_sq_x - 1][cur_sq_y] |= WALL_RIGHT;
    draw_wall(cur_sq_x, cur_sq_y, 3);
    goto donewall;
  }
  candidates[num_candidates++] = 3;
  
 donewall:
  if (num_candidates == 0)
    return ( -1 );
  if (num_candidates == 1)
    return ( candidates[0] );
  return ( candidates[ get_random(num_candidates) ] );
  
}


static int
backup (void)                                          /* back up a move */
{
  sqnum--;
  cur_sq_x = move_list[sqnum].x;
  cur_sq_y = move_list[sqnum].y;
  return ( sqnum );
}


static void
draw_maze_border (void)                         /* draw the maze outline */
{
  register int i, j;
 
  
  for ( i=0; i<maze_size_x; i++) {
    if ( maze[i][0] & WALL_TOP ) {
//      XDrawLine(dpy, win, gc,
//		border_x + grid_width * i,
//		border_y,
//		border_x + grid_width * (i+1) - 1,
//		border_y);

	line(screen, addpt(screen->r.min, Pt(border_x + grid_width * i, border_y)),
			addpt(screen->r.min, Pt(border_x + grid_width * (i+1) - 1, border_y)),
			Endsquare, Endsquare, 0, display->white, ZP);
    }
    if ((maze[i][maze_size_y - 1] & WALL_BOTTOM)) {
//      XDrawLine(dpy, win, gc,
//		border_x + grid_width * i,
//		border_y + grid_height * (maze_size_y) - 1,
//		border_x + grid_width * (i+1) - 1,
//		border_y + grid_height * (maze_size_y) - 1);
	line(screen, addpt(screen->r.min, 
				Pt(border_x + grid_width * i, border_y+ grid_height * (maze_size_y) - 1)),
			addpt(screen->r.min, 
				Pt(border_x + grid_width * (i+1) - 1, border_y + grid_height * (maze_size_y) - 1)),
			Endsquare, Endsquare, 0, display->white, ZP);
    }
  }
  for ( j=0; j<maze_size_y; j++) {
    if ( maze[maze_size_x - 1][j] & WALL_RIGHT ) {
//      XDrawLine(dpy, win, gc,
//		border_x + grid_width * maze_size_x - 1,
//		border_y + grid_height * j,
//		border_x + grid_width * maze_size_x - 1,
//		border_y + grid_height * (j+1) - 1);
	line(screen, addpt(screen->r.min, 
				Pt(border_x + grid_width * maze_size_x - 1, border_y + grid_height * j)),
			addpt(screen->r.min, 
				Pt(border_x + grid_width * maze_size_x - 1, border_y + grid_height * (j+1) - 1)),
			Endsquare, Endsquare, 0, display->white, ZP);
    }
    if ( maze[0][j] & WALL_LEFT ) {
//      XDrawLine(dpy, win, gc,
//		border_x,
//		border_y + grid_height * j,
//		border_x,
//		border_y + grid_height * (j+1) - 1);
	line(screen, addpt(screen->r.min, 
				Pt(border_x, border_y + grid_height * j)),
			addpt(screen->r.min, 
				Pt(border_x, border_y + grid_height * (j+1) - 1)),
			Endsquare, Endsquare, 0, display->white, ZP);
    }
  }
  
  if (logo_x != -1)
    {
      unsigned int w= 48, h = 48;
	Point p;

      /* round up to grid size */
      int ww = ((logo_width  / grid_width) + 1)  * grid_width;
      int hh = ((logo_height / grid_height) + 1) * grid_height;

	p = addpt(screen->r.min, Pt(border_x + 1 + grid_width  * logo_x + ((ww - w) / 2), border_y + 1 + grid_height * logo_y + ((hh - h) / 2)));
	

	draw(screen, Rpt(p, addpt(p, Pt(48, 48))), glenda, nil, ZP);
	//draw(screen, screen->r, glenda, nil, ZP);


    }
  draw_solid_square (start_x, start_y, WALL_TOP >> start_dir, liveColor);
  draw_solid_square (end_x, end_y, WALL_TOP >> end_dir, liveColor);
}


static void
draw_wall(int i, int j, int dir)                /* draw a single wall */
{
  switch (dir) {
  case 0:
//  XDrawLine(dpy, win, gc,
//	      border_x + grid_width * i, 
//	      border_y + grid_height * j,
//	      border_x + grid_width * (i+1), 
//	      border_y + grid_height * j);
	line(screen, addpt(screen->r.min, 
				Pt(border_x + grid_width * i, border_y + grid_height * j)),
			addpt(screen->r.min, 
				Pt(border_x + grid_width * (i+1), border_y + grid_height * j)),
			Endsquare, Endsquare, 0, display->white, ZP);
    break;
  case 1:
//    XDrawLine(dpy, win, gc,
//	      border_x + grid_width * (i+1), 
//	      border_y + grid_height * j,
//	      border_x + grid_width * (i+1), 
//	      border_y + grid_height * (j+1));
	line(screen, addpt(screen->r.min, 
				Pt(border_x + grid_width * (i+1), border_y + grid_height * j)),
			addpt(screen->r.min, 
				Pt(border_x + grid_width * (i+1), border_y + grid_height * (j+1))),
			Endsquare, Endsquare, 0, display->white, ZP);
    break;
  case 2:
//    XDrawLine(dpy, win, gc,
//	      border_x + grid_width * i, 
//	      border_y + grid_height * (j+1),
//	      border_x + grid_width * (i+1), 
//	      border_y + grid_height * (j+1));
	line(screen, addpt(screen->r.min, 
				Pt(border_x + grid_width *i, border_y + grid_height * (j+1))),
			addpt(screen->r.min, 
				Pt(border_x + grid_width * (i+1), border_y + grid_height * (j+1))),
			Endsquare, Endsquare, 0, display->white, ZP);
     break;
  case 3:
//    XDrawLine(dpy, win, gc,
//	      border_x + grid_width * i, 
//	      border_y + grid_height * j,
//	      border_x + grid_width * i, 
//	      border_y + grid_height * (j+1));
	line(screen, addpt(screen->r.min, 
				Pt(border_x + grid_width * i, border_y + grid_height * j)),
			addpt(screen->r.min, 
				Pt(border_x + grid_width * i, border_y + grid_height * (j+1))),
			Endsquare, Endsquare, 0, display->white, ZP);
     break;
  }
  if(sync_p)
    flushimage(display, 1);
}

/* Actually build a wall. */
static void
build_wall(i, j, dir)
     int i, j, dir;
{
  /* Draw it on the screen. */
  draw_wall(i, j, dir);
  /* Put it in the maze. */
  switch(dir)
    {
    case 0:
      maze[i][j] |= WALL_TOP;
      if(j>0)
	maze[i][j-1] |= WALL_BOTTOM;
      break;
    case 1:
      maze[i][j] |= WALL_RIGHT;
      if(i<maze_size_x-1)
	maze[i+1][j] |= WALL_LEFT;
      break;
    case 2:
      maze[i][j] |= WALL_BOTTOM;
      if(j<maze_size_y-1)
	maze[i][j+1] |= WALL_TOP;
      break;
    case 3:
      maze[i][j] |= WALL_LEFT;
      if(i>0)
	maze[i-1][j] |= WALL_RIGHT;
      break;
    }
}



static void
draw_solid_square(int i, int j,          /* draw a solid square in a square */
		  int dir, Image *c)
{
	Rectangle r;
	Point p;

  switch (dir) {
  case WALL_TOP:
//      XFillRectangle(dpy, win, gc,
//		     border_x + bw + grid_width * i, 
//		     border_y - bw + grid_height * j, 
//		     grid_width - (bw+bw), grid_height);

	p = addpt(screen->r.min, 
		Pt(border_x + bw + grid_width * i, border_y - bw + grid_height * j));
	r = Rpt(p, addpt(p, Pt(grid_width - (bw+bw), grid_height)));
	draw(screen, r, c, nil, ZP);

      break;
  case WALL_RIGHT:
//      XFillRectangle(dpy, win, gc,
//		     border_x + bw + grid_width * i, 
//		     border_y + bw + grid_height * j, 
//		     grid_width, grid_height - (bw+bw));
	p = addpt(screen->r.min, 
		Pt(border_x + bw + grid_width * i,  border_y + bw + grid_height * j));
	r = Rpt(p, addpt(p, Pt(grid_width, grid_height - (bw+bw))));
	draw(screen, r, c, nil, ZP);
      break;
  case WALL_BOTTOM:
//      XFillRectangle(dpy, win, gc,
//		     border_x + bw + grid_width * i, 
//		     border_y + bw + grid_height * j, 
//		     grid_width - (bw+bw), grid_height);
	p = addpt(screen->r.min, 
		Pt(border_x + bw + grid_width * i, border_y + bw + grid_height * j));
	r = Rpt(p, addpt(p, Pt(grid_width - (bw+bw), grid_height)));
	draw(screen, r, c, nil, ZP);
      break;
  case WALL_LEFT:
//      XFillRectangle(dpy, win, gc,
//		     border_x - bw + grid_width * i, 
//		     border_y + bw + grid_height * j, 
//		     grid_width, grid_height - (bw+bw));
	p = addpt(screen->r.min, 
		Pt(border_x - bw + grid_width * i, border_y + bw + grid_height * j));
	r = Rpt(p, addpt(p, Pt(grid_width , grid_height- (bw+bw))));
	draw(screen, r, c, nil, ZP);
      break;
  }
  flushimage(display, 1);
}

int
longdeadend_p(int x1, int y1, int x2, int y2, int endwall)
{
    int dx = x2 - x1, dy = y2 - y1;
    int sidewalls;

    sidewalls = endwall | (endwall >> 2 | endwall << 2);
    sidewalls = ~sidewalls & WALL_ANY;

    while((maze[x2][y2] & WALL_ANY) == sidewalls)
    {
	x2 += dx;
	y2 += dy;
    }

    if((maze[x2][y2] & WALL_ANY) == (sidewalls | endwall))
    {
	endwall = (endwall >> 2 | endwall << 2) & WALL_ANY;
	while(x1 != x2 || y1 != y2)
	{
	    x1 += dx;
	    y1 += dy;
	    draw_solid_square(x1, y1, endwall, skipColor);
	    maze[x1][y1] |= SOLVER_VISIT;
	}
	return 1;
    }
    else
	return 0;
}

/* Find all dead regions -- areas from which the goal cannot be reached --
   and mark them visited. */
void
find_dead_regions(void)
{
    int x, y, flipped;

    /* Find all not SOLVER_VISIT squares bordering NOT_DEAD squares
       and mark them NOT_DEAD also.  Repeat until no more such squares. */
    maze[start_x][start_y] |= NOT_DEAD;
    
    do
    {
	flipped = 0;
	for(x = 0; x < maze_size_x; x++)
	    for(y = 0; y < maze_size_y; y++)
		if(!(maze[x][y] & (SOLVER_VISIT | NOT_DEAD))
		   && (   (x && (maze[x-1][y] & NOT_DEAD))
		       || (y && (maze[x][y-1] & NOT_DEAD))))
		{
		    flipped = 1;
		    maze[x][y] |= NOT_DEAD;
		}
	for(x = maze_size_x-1; x >= 0; x--)
	    for(y = maze_size_y-1; y >= 0; y--)
		if(!(maze[x][y] & (SOLVER_VISIT | NOT_DEAD))
		   && (   (x != maze_size_x-1 && (maze[x+1][y] & NOT_DEAD))
		       || (y != maze_size_y-1 && (maze[x][y+1] & NOT_DEAD))))
		{
		    flipped = 1;
		    maze[x][y] |= NOT_DEAD;
		}
    }
    while(flipped);

    for (y = 0; y < maze_size_y; y++)
      for (x = 0; x < maze_size_x; x++)
      {
	if (maze[x][y] & NOT_DEAD)
	  maze[x][y] &= ~NOT_DEAD;
	else if (!(maze[x][y] & SOLVER_VISIT))
	{
	  maze[x][y] |= SOLVER_VISIT;
	  if((x < logo_x || x > logo_x + logo_width / grid_width) ||
	     (y < logo_y || y > logo_y + logo_height / grid_height))
	  {
	    if (!maze[x][y] & WALL_ANY) {
		Point p; Rectangle r;
//	      XFillRectangle(dpy, win, ugc,
//			     border_x + bw + grid_width * x,
//			     border_y + bw + grid_height * y,
//			     grid_width - (bw+bw), grid_height - (bw+bw));
		p = addpt(screen->r.min, 
			Pt(border_x + bw + grid_width * x, border_y + bw + grid_height * y));
		r = Rpt(p, addpt(p, Pt(grid_width - (bw+bw), grid_height- (bw+bw))));
		draw(screen, r, surroundColor, nil, ZP);

	    } else
	    {
	      if (! (maze[x][y] & WALL_LEFT))
		draw_solid_square(x, y, WALL_LEFT, surroundColor);
	      if (! (maze[x][y] & WALL_RIGHT))
		draw_solid_square(x, y, WALL_RIGHT, surroundColor);
	      if (! (maze[x][y] & WALL_TOP))
		draw_solid_square(x, y, WALL_TOP, surroundColor);
	      if (! (maze[x][y] & WALL_BOTTOM))
		draw_solid_square(x, y, WALL_BOTTOM, surroundColor);
	    }
	  }
	}
      }
    flushimage(display, 1);
}

static void
solve_maze (void)                     /* solve it with graphical feedback */
{
    int i, dir, from, x, y, ways, bt = 0;

    /* plug up the surrounding wall */
    maze[end_x][end_y] |= (WALL_TOP >> end_dir);
    
    /* initialize search path */
    i = 0;
    path[i].x = end_x;
    path[i].y = end_y;
    path[i].dir = 0;
    maze[end_x][end_y] |= SOLVER_VISIT;
    
    /* do it */
    while (1)
    {
	if ( maze[path[i].x][path[i].y] & START_SQUARE )
	    return;

	if(restart)
		return;

	if(ecanmouse()) {
		m = emouse();
		if(m.buttons&4) {
			if(emenuhit(3, &m, &menu) == 0) 
				exits(0);
		}
	}
	

	
	if (solve_delay) sleep (solve_delay);
	
	if(!path[i].dir)
	{
	    ways = 0;
	    /* First visit this square.  Which adjacent squares are open? */
	    for(dir = WALL_TOP; dir & WALL_ANY; dir >>= 1)
	    {
		if(maze[path[i].x][path[i].y] & dir)
		    continue;
		
		y = path[i].y - !!(dir & WALL_TOP) + !!(dir & WALL_BOTTOM);
		x = path[i].x + !!(dir & WALL_RIGHT) - !!(dir & WALL_LEFT);
		
		if(maze[x][y] & SOLVER_VISIT)
		    continue;
		
		from = (dir << 2 & WALL_ANY) | (dir >> 2 & WALL_ANY);
		/* don't enter obvious dead ends */
		if(((maze[x][y] & WALL_ANY) | from) != WALL_ANY)
		{
		    if(!longdeadend_p(path[i].x, path[i].y, x, y, dir))
			ways |= dir;
		}
		else
		{
		    draw_solid_square(x, y, from, skipColor);
		    maze[x][y] |= SOLVER_VISIT;
		}
	    }
	}
	else
	    ways = path[i].ways;
	/* ways now has a bitmask of open paths. */
	
	if(!ways)
	    goto backtrack;

	if (!ignorant_p)
	  {
	    x = path[i].x - start_x;
	    y = path[i].y - start_y;
	    /* choice one */
	    if(abs(y) <= abs(x))
	      dir = (x > 0) ? WALL_LEFT : WALL_RIGHT;
	    else
	      dir = (y > 0) ? WALL_TOP : WALL_BOTTOM;
	    
	    if(dir & ways)
	      goto found;
	    
	    /* choice two */
	    switch(dir)
	      {
	      case WALL_LEFT:
	      case WALL_RIGHT:
		dir = (y > 0) ? WALL_TOP : WALL_BOTTOM; break;
	      case WALL_TOP:
	      case WALL_BOTTOM:
		dir = (x > 0) ? WALL_LEFT : WALL_RIGHT;
	      }
	    
	    if(dir & ways)
	      goto found;
	    
	    /* choice three */
	    
	    dir = (dir << 2 & WALL_ANY) | (dir >> 2 & WALL_ANY);
	    if(dir & ways)
	      goto found;
	    
	    /* choice four */
	    dir = ways;
	    if(!dir)
	      goto backtrack;
	    
	  found: ;
	  }
	else
	  {
	    if(ways&WALL_TOP)
	      dir = WALL_TOP;
	    else if(ways&WALL_LEFT)
	      dir = WALL_LEFT;
	    else if(ways&WALL_BOTTOM)
	      dir = WALL_BOTTOM;
	    else if(ways&WALL_RIGHT)
	      dir = WALL_RIGHT;
	    else
	      goto backtrack;
	  }
	bt = 0;
	ways &= ~dir;  /* tried this one */
	
	y = path[i].y - !!(dir & WALL_TOP) + !!(dir & WALL_BOTTOM);
	x = path[i].x + !!(dir & WALL_RIGHT) - !!(dir & WALL_LEFT);
	
	/* advance in direction dir */
	path[i].dir = dir;
	path[i].ways = ways;
	draw_solid_square(path[i].x, path[i].y, dir, liveColor);
	
	i++;
	path[i].dir = 0;
	path[i].ways = 0;
	path[i].x = x;
	path[i].y = y;
	maze[x][y] |= SOLVER_VISIT;
	continue;

    backtrack:
	if(i == 0)
	{
	    print("Unsolvable maze.\n");
	    return;
	}

	if(!bt && !ignorant_p)
	    find_dead_regions();
	bt = 1;
	from = path[i-1].dir;
	from = (from << 2 & WALL_ANY) | (from >> 2 & WALL_ANY);
	
	draw_solid_square(path[i].x, path[i].y, from, deadColor);
	i--;
    }
} 



/*
 *  jmr additions for Jamie Zawinski's <jwz@jwz.org> screensaver stuff,
 *  note that the code above this has probably been hacked about in some
 *  arbitrary way.
 */



void
screenhack(void)
{
  int size, root, generator, this_gen;

  root = 0;
  solve_delay = 5;
  pre_solve_delay = 2000;
  post_solve_delay = 4000;
  generator = -1;
  max_length = 5;
  bridge_p = 0;
  ignorant_p = 0;

  size = 5 + (random () % 20);
  grid_width = grid_height = size;
  bw = (size > 6 ? 3 : (size-1)/2);


  x = 0;
  y = 0;

  set_maze_sizes (Dx(screen->r), Dy(screen->r));

  restart = root;

  sync_p = !(random() % 10);

  while (1) {                            /* primary execution loop [ rhess ] */
	if(ecanmouse()) {
		m = emouse();
		if(m.buttons&4) {
			if(emenuhit(3, &m, &menu) == 0) 
				exits(0);
		}
	}
    if (restart || stop) goto pop;
    switch (state) {
    case 1:
      initialize_maze();
      break;
    case 2:
      draw(screen, screen->r, display->black, nil, ZP);
      draw_maze_border();
	flushimage(display, 1);
      break;
    case 3:
      this_gen = generator;
      if(this_gen<0 || this_gen>2)
	this_gen = random()%3;

      switch(this_gen)
	{
	case 0:
	  create_maze();
	  break;
	case 1:
	  alt_create_maze();
	  break;
	case 2:
	  set_create_maze();
	  break;
	}
	flushimage(display, 1);
      break;
    case 4:
      sleep (pre_solve_delay);
      break;
    case 5:
      solve_maze();
      break;
    case 6:
      sleep (post_solve_delay);
      state = 0 ;
      draw(screen, screen->r, display->black, nil, ZP);
      break;
    default:
      abort ();
    }
    ++state;
  pop:
    if (restart)
      {
	restart = 0;
	stop = 0;
	state = 1;
	set_maze_sizes (Dx(screen->r), Dy(screen->r));

	draw(screen, screen->r, display->black, nil, ZP);
	flushimage(display, 1);
	sync_p = !(random() % 10);
      }
  }
}


void
eresized(int new)
{
	
	if(new && getwindow(display, Refnone) < 0) {
		sysfatal("can't reattach to window");
	}	
	//fprint(2, "sorry, cannot resize\n");
	//exits(0);
	restart = 1;
}


void 
main(int argc, char **argv)
{
	int fd;

	USED(argc, argv);
	srand(time(0));


	if(initdraw(nil, nil, "maze") < 0)
		sysfatal("initdraw failed: %r");

	liveColor = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DGreen);
	deadColor = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DRed);
	skipColor= allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DMagenta);
	surroundColor= allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DPaleblue);

	fd = open("/lib/face/48x48x4/g/glenda.1", OREAD);
	if(fd < 0)
		sysfatal("cannot open /lib/face/48x48x4/g/glenda.1: %r");

	glenda = readimage(display, fd, 0);
	if(glenda == nil)
		sysfatal("cannot load glenda's image: %r");

	draw(screen, screen->r, display->black, nil, ZP);
	flushimage(display, 1);

	einit(Emouse);
	eresized(0);
	screenhack();
}

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2002-08-17  8:45 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-08-16  7:59 [9fans] draw() question andrey mirtchovski
2002-08-16 12:14 rob pike, esq.
2002-08-17  8:45 ` andrey mirtchovski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).