1099 lines
29 KiB
C
1099 lines
29 KiB
C
/* -*- Mode: C; tab-width: 4 -*- */
|
|
/* maze --- a varying maze */
|
|
|
|
#if !defined( lint ) && !defined( SABER )
|
|
static const char sccsid[] = "@(#)maze.c 5.00 2000/11/01 xlockmore";
|
|
|
|
#endif
|
|
|
|
/*-
|
|
* Copyright (c) 1988 by Sun Microsystems
|
|
*
|
|
* 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.
|
|
*
|
|
* This file is provided AS IS with no warranties of any kind. The author
|
|
* shall have no liability with respect to the infringement of copyrights,
|
|
* trade secrets or any patents by this file or any part thereof. In no
|
|
* event will the author be liable for any lost revenue or profits or
|
|
* other special, indirect and consequential damages.
|
|
*
|
|
* Revision History:
|
|
* 27-Nov-2001: interactive maze mode Ephraim Yawitz fyawitz@actcom.co.il
|
|
* 01-Nov-2000: Allocation checks
|
|
* 27-Oct-1997: xpm and ras capability added
|
|
* 10-May-1997: Compatible with xscreensaver
|
|
* 27-Feb-1996: Add ModeInfo args to init and callback hooks, use new
|
|
* ModeInfo handle to specify long pauses (eliminate onepause).
|
|
* Ron Hitchens <ron@idiom.com>
|
|
* 20-Jul-1995: minimum size fix Peter Schmitzberger <schmitz@coma.sbg.ac.at>
|
|
* 17-Jun-1995: removed sleep statements
|
|
* 22-Mar-1995: multidisplay fix Caleb Epstein <epstein_caleb@jpmorgan.com>
|
|
* 09-Mar-1995: changed how batchcount is used
|
|
* 27-Feb-1995: patch for VMS
|
|
* 04-Feb-1995: patch to slow down maze Heath Kehoe <hakehoe@icaen.uiowa.edu>
|
|
* 17-Jun-1994: HP ANSI C compiler needs a type cast for gray_bits
|
|
* Richard Lloyd <R.K.Lloyd@csc.liv.ac.uk>
|
|
* 02-Sep-1993: xlock version David Bagley <bagleyd@tux.org>
|
|
* 07-Mar-1993: Good ideas from xscreensaver Jamie Zawinski <jwz@jwz.org>
|
|
* 06-Jun-1985: Martin Weiss Sun Microsystems
|
|
*/
|
|
|
|
/*-
|
|
* original copyright
|
|
* **************************************************************************
|
|
* 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.
|
|
* ***************************************************************************
|
|
*/
|
|
|
|
#ifdef STANDALONE
|
|
#define MODE_maze
|
|
#define PROGCLASS "Maze"
|
|
#define HACK_INIT init_maze
|
|
#define HACK_DRAW draw_maze
|
|
#define maze_opts xlockmore_opts
|
|
#define DEFAULTS "*delay: 1000 \n" \
|
|
"*cycles: 3000 \n" \
|
|
"*size: -40 \n" \
|
|
"*ncolors: 64 \n" \
|
|
"*bitmap: \n" \
|
|
"*trackmouse: False \n"
|
|
#include "xlockmore.h" /* in xscreensaver distribution */
|
|
#else /* STANDALONE */
|
|
#include "xlock.h" /* in xlockmore distribution */
|
|
#include "color.h"
|
|
#endif /* STANDALONE */
|
|
#include "iostuff.h"
|
|
|
|
#ifdef MODE_maze
|
|
|
|
#define DEF_TRACKMOUSE "False"
|
|
static Bool trackmouse;
|
|
|
|
#ifdef DISABLE_INTERACTIVE
|
|
ModeSpecOpt maze_opts =
|
|
{0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
|
|
#else
|
|
static XrmOptionDescRec opts[] =
|
|
{
|
|
{(char *) "-trackmouse", (char *) ".maze.trackmouse", XrmoptionNoArg, (caddr_t) "on"},
|
|
{(char *) "+trackmouse", (char *) ".maze.trackmouse", XrmoptionNoArg, (caddr_t) "off"}
|
|
};
|
|
|
|
static argtype vars[] =
|
|
{
|
|
{(void *) & trackmouse, (char *) "trackmouse", (char *) "TrackMouse", (char *) DEF_TRACKMOUSE, t_Bool}
|
|
};
|
|
|
|
static OptionStruct desc[] =
|
|
{
|
|
{(char *) "-/+trackmouse", (char *) "turn on/off the tracking of the mouse"},
|
|
};
|
|
|
|
ModeSpecOpt maze_opts =
|
|
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
|
|
#endif
|
|
|
|
#ifdef USE_MODULES
|
|
ModStruct maze_description =
|
|
{"maze", "init_maze", "draw_maze", "release_maze",
|
|
"refresh_maze", "init_maze", (char *) NULL, &maze_opts,
|
|
1000, 1, 3000, -40, 64, 1.0, "",
|
|
"Shows a random maze and a depth first search solution", 0, NULL};
|
|
|
|
#endif
|
|
|
|
#include "bitmaps/gray1.xbm"
|
|
|
|
/* aliases for vars defined in the bitmap file */
|
|
#define ICON_WIDTH image_width
|
|
#define ICON_HEIGHT image_height
|
|
#define ICON_BITS image_bits
|
|
|
|
#include "maze.xbm"
|
|
|
|
#ifdef HAVE_XPM
|
|
#define ICON_NAME image_name
|
|
#include "maze.xpm"
|
|
#define DEFAULT_XPM 1
|
|
#endif
|
|
|
|
#define MINGRIDSIZE 3
|
|
#define MINSIZE 8
|
|
|
|
#define WALL_TOP 0x8000
|
|
#define WALL_RIGHT 0x4000
|
|
#define WALL_BOTTOM 0x2000
|
|
#define WALL_LEFT 0x1000
|
|
|
|
#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 DIR_UP 0
|
|
#define DIR_RIGHT 1
|
|
#define DIR_DOWN 2
|
|
#define DIR_LEFT 3
|
|
|
|
#define START_SQUARE 0x2
|
|
#define END_SQUARE 0x1
|
|
|
|
typedef struct {
|
|
unsigned int x;
|
|
unsigned int y;
|
|
char dir;
|
|
} paths;
|
|
|
|
typedef struct {
|
|
int ncols, nrows, maze_size, xb, yb;
|
|
int sqnum, cur_sq_x, cur_sq_y, path_length;
|
|
int start_x, start_y, start_dir, end_x, end_y, end_dir;
|
|
int logo_x, logo_y;
|
|
int width, height;
|
|
int xs, ys, logo_size_x, logo_size_y;
|
|
int solving, current_path, stage;
|
|
int cycles;
|
|
unsigned int *maze;
|
|
paths *move_list;
|
|
paths *save_path, *path;
|
|
GC grayGC;
|
|
Pixmap graypix;
|
|
XImage *logo;
|
|
Colormap cmap;
|
|
unsigned long black, color;
|
|
int graphics_format;
|
|
GC backGC;
|
|
int time;
|
|
} mazestruct;
|
|
|
|
static mazestruct *mazes = (mazestruct *) NULL;
|
|
|
|
static void
|
|
draw_wall(ModeInfo * mi, int i, int j, int dir)
|
|
{ /* draw a single wall */
|
|
Display *display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
mazestruct *mp = &mazes[MI_SCREEN(mi)];
|
|
GC gc = mp->backGC;
|
|
|
|
switch (dir) {
|
|
case 0:
|
|
XDrawLine(display, window, gc,
|
|
mp->xb + mp->xs * i,
|
|
mp->yb + mp->ys * j,
|
|
mp->xb + mp->xs * (i + 1),
|
|
mp->yb + mp->ys * j);
|
|
break;
|
|
case 1:
|
|
XDrawLine(display, window, gc,
|
|
mp->xb + mp->xs * (i + 1),
|
|
mp->yb + mp->ys * j,
|
|
mp->xb + mp->xs * (i + 1),
|
|
mp->yb + mp->ys * (j + 1));
|
|
break;
|
|
case 2:
|
|
XDrawLine(display, window, gc,
|
|
mp->xb + mp->xs * i,
|
|
mp->yb + mp->ys * (j + 1),
|
|
mp->xb + mp->xs * (i + 1),
|
|
mp->yb + mp->ys * (j + 1));
|
|
break;
|
|
case 3:
|
|
XDrawLine(display, window, gc,
|
|
mp->xb + mp->xs * i,
|
|
mp->yb + mp->ys * j,
|
|
mp->xb + mp->xs * i,
|
|
mp->yb + mp->ys * (j + 1));
|
|
break;
|
|
}
|
|
} /* end of draw_wall */
|
|
|
|
static void
|
|
draw_solid_square(ModeInfo * mi, GC gc,
|
|
register int i, register int j, register int dir)
|
|
{ /* draw a solid square in a square */
|
|
Display *display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
mazestruct *mp = &mazes[MI_SCREEN(mi)];
|
|
|
|
switch (dir) {
|
|
case 0:
|
|
XFillRectangle(display, window, gc,
|
|
mp->xb + 3 + mp->xs * i,
|
|
mp->yb - 3 + mp->ys * j,
|
|
mp->xs - 6, mp->ys);
|
|
break;
|
|
case 1:
|
|
XFillRectangle(display, window, gc,
|
|
mp->xb + 3 + mp->xs * i,
|
|
mp->yb + 3 + mp->ys * j,
|
|
mp->xs, mp->ys - 6);
|
|
break;
|
|
case 2:
|
|
XFillRectangle(display, window, gc,
|
|
mp->xb + 3 + mp->xs * i,
|
|
mp->yb + 3 + mp->ys * j,
|
|
mp->xs - 6, mp->ys);
|
|
break;
|
|
case 3:
|
|
XFillRectangle(display, window, gc,
|
|
mp->xb - 3 + mp->xs * i,
|
|
mp->yb + 3 + mp->ys * j,
|
|
mp->xs, mp->ys - 6);
|
|
break;
|
|
}
|
|
|
|
} /* end of draw_solid_square() */
|
|
|
|
static void
|
|
enter_square(ModeInfo * mi, int n)
|
|
{ /* move into a neighboring square */
|
|
mazestruct *mp = &mazes[MI_SCREEN(mi)];
|
|
|
|
draw_solid_square(mi, mp->backGC, (int) mp->path[n].x, (int) mp->path[n].y,
|
|
(int) mp->path[n].dir);
|
|
|
|
mp->path[n + 1].dir = -1;
|
|
switch (mp->path[n].dir) {
|
|
case 0:
|
|
mp->path[n + 1].x = mp->path[n].x;
|
|
mp->path[n + 1].y = mp->path[n].y - 1;
|
|
break;
|
|
case 1:
|
|
mp->path[n + 1].x = mp->path[n].x + 1;
|
|
mp->path[n + 1].y = mp->path[n].y;
|
|
break;
|
|
case 2:
|
|
mp->path[n + 1].x = mp->path[n].x;
|
|
mp->path[n + 1].y = mp->path[n].y + 1;
|
|
break;
|
|
case 3:
|
|
mp->path[n + 1].x = mp->path[n].x - 1;
|
|
mp->path[n + 1].y = mp->path[n].y;
|
|
break;
|
|
}
|
|
|
|
} /* end of enter_square() */
|
|
|
|
static void
|
|
free_path(mazestruct * mp)
|
|
{
|
|
if (mp->maze) {
|
|
free(mp->maze);
|
|
mp->maze = (unsigned int *) NULL;
|
|
}
|
|
if (mp->move_list) {
|
|
free(mp->move_list);
|
|
mp->move_list = (paths *) NULL;
|
|
}
|
|
if (mp->save_path) {
|
|
free(mp->save_path);
|
|
mp->save_path = (paths *) NULL;
|
|
}
|
|
if (mp->path) {
|
|
free(mp->path);
|
|
mp->path = (paths *) NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
free_stuff(Display * display, mazestruct * mp)
|
|
{
|
|
if (mp->cmap != None) {
|
|
XFreeColormap(display, mp->cmap);
|
|
if (mp->backGC != None) {
|
|
XFreeGC(display, mp->backGC);
|
|
mp->backGC = None;
|
|
}
|
|
mp->cmap = None;
|
|
} else
|
|
mp->backGC = None;
|
|
}
|
|
|
|
static void
|
|
free_maze(Display * display, mazestruct * mp)
|
|
{
|
|
free_path(mp);
|
|
if (mp->grayGC != None) {
|
|
XFreeGC(display, mp->grayGC);
|
|
mp->grayGC = None;
|
|
}
|
|
if (mp->graypix != None) {
|
|
XFreePixmap(display, mp->graypix);
|
|
mp->graypix = None;
|
|
}
|
|
free_stuff(display, mp);
|
|
if (mp->logo != None) {
|
|
destroyImage(&mp->logo, &mp->graphics_format);
|
|
mp->logo = None;
|
|
}
|
|
}
|
|
|
|
static Bool
|
|
set_maze_sizes(ModeInfo * mi)
|
|
{
|
|
mazestruct *mp = &mazes[MI_SCREEN(mi)];
|
|
Display *display = MI_DISPLAY(mi);
|
|
int size = MI_SIZE(mi);
|
|
|
|
if (size < -MINSIZE) {
|
|
free_path(mp);
|
|
mp->ys = NRAND(MIN(-size, MAX(MINSIZE, (MIN(mp->width, mp->height) - 1) /
|
|
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
|
|
} else if (size < MINSIZE) {
|
|
if (!size)
|
|
mp->ys = MAX(MINSIZE, (MIN(mp->width, mp->height) - 1) / MINGRIDSIZE);
|
|
else
|
|
mp->ys = MINSIZE;
|
|
} else
|
|
mp->ys = MIN(size, MAX(MINSIZE, (MIN(mp->width, mp->height) - 1) /
|
|
MINGRIDSIZE));
|
|
mp->xs = mp->ys;
|
|
mp->logo_size_x = mp->logo->width / mp->xs + 1;
|
|
mp->logo_size_y = mp->logo->height / mp->ys + 1;
|
|
|
|
mp->ncols = MAX((mp->width - 1) / mp->xs, MINGRIDSIZE);
|
|
mp->nrows = MAX((mp->height - 1) / mp->ys, MINGRIDSIZE);
|
|
|
|
mp->xb = (mp->width - mp->ncols * mp->xs) / 2;
|
|
mp->yb = (mp->height - mp->nrows * mp->ys) / 2;
|
|
mp->maze_size = mp->ncols * mp->nrows;
|
|
if (!mp->maze)
|
|
if ((mp->maze = (unsigned int *) calloc(mp->maze_size,
|
|
sizeof (unsigned int))) == NULL) {
|
|
free_maze(display, mp);
|
|
return False;
|
|
}
|
|
if (!mp->move_list)
|
|
if ((mp->move_list = (paths *) calloc(mp->maze_size,
|
|
sizeof (paths))) == NULL) {
|
|
free_maze(display, mp);
|
|
return False;
|
|
}
|
|
if (!mp->save_path)
|
|
if ((mp->save_path = (paths *) calloc(mp->maze_size,
|
|
sizeof (paths))) == NULL) {
|
|
free_maze(display, mp);
|
|
return False;
|
|
}
|
|
if (!mp->path)
|
|
if (( mp->path = (paths *) calloc(mp->maze_size,
|
|
sizeof (paths))) == NULL) {
|
|
free_maze(display, mp);
|
|
return False;
|
|
}
|
|
return True;
|
|
} /* end of set_maze_sizes */
|
|
|
|
|
|
static void
|
|
initialize_maze(ModeInfo * mi)
|
|
{ /* draw the surrounding wall and start/end squares */
|
|
Display *display = MI_DISPLAY(mi);
|
|
mazestruct *mp = &mazes[MI_SCREEN(mi)];
|
|
register int i, j, wall;
|
|
|
|
if (MI_NPIXELS(mi) <= 2) {
|
|
mp->color = MI_WHITE_PIXEL(mi);
|
|
} else {
|
|
mp->color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
|
|
}
|
|
XSetForeground(display, mp->backGC, mp->color);
|
|
XSetForeground(display, mp->grayGC, mp->color);
|
|
/* initialize all squares */
|
|
for (i = 0; i < mp->ncols; i++) {
|
|
for (j = 0; j < mp->nrows; j++) {
|
|
mp->maze[i * mp->nrows + j] = 0;
|
|
}
|
|
}
|
|
|
|
/* top wall */
|
|
for (i = 0; i < mp->ncols; i++) {
|
|
mp->maze[i * mp->nrows] |= WALL_TOP;
|
|
}
|
|
|
|
/* right wall */
|
|
for (j = 0; j < mp->nrows; j++) {
|
|
mp->maze[(mp->ncols - 1) * mp->nrows + j] |= WALL_RIGHT;
|
|
}
|
|
|
|
/* bottom wall */
|
|
for (i = 0; i < mp->ncols; i++) {
|
|
mp->maze[i * mp->nrows + mp->nrows - 1] |= WALL_BOTTOM;
|
|
}
|
|
|
|
/* left wall */
|
|
for (j = 0; j < mp->nrows; j++) {
|
|
mp->maze[j] |= WALL_LEFT;
|
|
}
|
|
|
|
/* set start square */
|
|
wall = NRAND(4);
|
|
switch (wall) {
|
|
case 0:
|
|
i = NRAND(mp->ncols);
|
|
j = 0;
|
|
break;
|
|
case 1:
|
|
i = mp->ncols - 1;
|
|
j = NRAND(mp->nrows);
|
|
break;
|
|
case 2:
|
|
i = NRAND(mp->ncols);
|
|
j = mp->nrows - 1;
|
|
break;
|
|
case 3:
|
|
i = 0;
|
|
j = NRAND(mp->nrows);
|
|
break;
|
|
}
|
|
mp->maze[i * mp->nrows + j] |= START_SQUARE;
|
|
mp->maze[i * mp->nrows + j] |= (DOOR_IN_TOP >> wall);
|
|
mp->maze[i * mp->nrows + j] &= ~(WALL_TOP >> wall);
|
|
mp->cur_sq_x = i;
|
|
mp->cur_sq_y = j;
|
|
mp->start_x = i;
|
|
mp->start_y = j;
|
|
mp->start_dir = wall;
|
|
mp->sqnum = 0;
|
|
|
|
/* set end square */
|
|
wall = (wall + 2) % 4;
|
|
switch (wall) {
|
|
case 0:
|
|
i = NRAND(mp->ncols);
|
|
j = 0;
|
|
break;
|
|
case 1:
|
|
i = mp->ncols - 1;
|
|
j = NRAND(mp->nrows);
|
|
break;
|
|
case 2:
|
|
i = NRAND(mp->ncols);
|
|
j = mp->nrows - 1;
|
|
break;
|
|
case 3:
|
|
i = 0;
|
|
j = NRAND(mp->nrows);
|
|
break;
|
|
}
|
|
mp->maze[i * mp->nrows + j] |= END_SQUARE;
|
|
mp->maze[i * mp->nrows + j] |= (DOOR_OUT_TOP >> wall);
|
|
mp->maze[i * mp->nrows + j] &= ~(WALL_TOP >> wall);
|
|
mp->end_x = i;
|
|
mp->end_y = j;
|
|
mp->end_dir = wall;
|
|
|
|
/* set logo */
|
|
if ((mp->ncols > mp->logo_size_x + 6) &&
|
|
(mp->nrows > mp->logo_size_y + 6)) {
|
|
mp->logo_x = NRAND(mp->ncols - mp->logo_size_x - 6) + 3;
|
|
mp->logo_y = NRAND(mp->nrows - mp->logo_size_y - 6) + 3;
|
|
|
|
for (i = 0; i < mp->logo_size_x; i++)
|
|
for (j = 0; j < mp->logo_size_y; j++)
|
|
mp->maze[(mp->logo_x + i) * mp->nrows + mp->logo_y + j] |=
|
|
DOOR_IN_TOP;
|
|
} else
|
|
mp->logo_y = mp->logo_x = -1;
|
|
}
|
|
|
|
static int
|
|
choose_door(ModeInfo * mi)
|
|
{ /* pick a new path */
|
|
mazestruct *mp = &mazes[MI_SCREEN(mi)];
|
|
int candidates[3];
|
|
register int num_candidates;
|
|
|
|
num_candidates = 0;
|
|
|
|
/* top wall */
|
|
if ((!(mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] &
|
|
DOOR_IN_TOP)) &&
|
|
(!(mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] &
|
|
DOOR_OUT_TOP)) &&
|
|
(!(mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] &
|
|
WALL_TOP))) {
|
|
if (mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y - 1] &
|
|
DOOR_IN_ANY) {
|
|
mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] |= WALL_TOP;
|
|
mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y - 1] |=
|
|
WALL_BOTTOM;
|
|
} else
|
|
candidates[num_candidates++] = 0;
|
|
}
|
|
/* right wall */
|
|
if ((!(mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] &
|
|
DOOR_IN_RIGHT)) &&
|
|
(!(mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] &
|
|
DOOR_OUT_RIGHT)) &&
|
|
(!(mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] &
|
|
WALL_RIGHT))) {
|
|
if (mp->maze[(mp->cur_sq_x + 1) * mp->nrows + mp->cur_sq_y] &
|
|
DOOR_IN_ANY) {
|
|
mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] |= WALL_RIGHT;
|
|
mp->maze[(mp->cur_sq_x + 1) * mp->nrows + mp->cur_sq_y] |=
|
|
WALL_LEFT;
|
|
} else
|
|
candidates[num_candidates++] = 1;
|
|
}
|
|
/* bottom wall */
|
|
if ((!(mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] &
|
|
DOOR_IN_BOTTOM)) &&
|
|
(!(mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] &
|
|
DOOR_OUT_BOTTOM)) &&
|
|
(!(mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] &
|
|
WALL_BOTTOM))) {
|
|
if (mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y + 1] &
|
|
DOOR_IN_ANY) {
|
|
mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] |= WALL_BOTTOM;
|
|
mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y + 1] |= WALL_TOP;
|
|
} else
|
|
candidates[num_candidates++] = 2;
|
|
}
|
|
/* left wall */
|
|
if ((!(mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] &
|
|
DOOR_IN_LEFT)) &&
|
|
(!(mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] &
|
|
DOOR_OUT_LEFT)) &&
|
|
(!(mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] &
|
|
WALL_LEFT))) {
|
|
if (mp->maze[(mp->cur_sq_x - 1) * mp->nrows + mp->cur_sq_y] &
|
|
DOOR_IN_ANY) {
|
|
mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] |= WALL_LEFT;
|
|
mp->maze[(mp->cur_sq_x - 1) * mp->nrows + mp->cur_sq_y] |=
|
|
WALL_RIGHT;
|
|
} else
|
|
candidates[num_candidates++] = 3;
|
|
}
|
|
/* done wall */
|
|
if (num_candidates == 0)
|
|
return (-1);
|
|
if (num_candidates == 1)
|
|
return (candidates[0]);
|
|
return (candidates[NRAND(num_candidates)]);
|
|
|
|
} /* end of choose_door() */
|
|
|
|
static void
|
|
draw_maze_walls(ModeInfo * mi)
|
|
{ /* pick a new path */
|
|
mazestruct *mp = &mazes[MI_SCREEN(mi)];
|
|
int i, j, isize;
|
|
|
|
MI_IS_DRAWN(mi) = True;
|
|
|
|
for (i = 0; i < mp->ncols; i++) {
|
|
isize = i * mp->nrows;
|
|
for (j = 0; j < mp->nrows; j++) {
|
|
/* Only need to draw half the walls... since they are shared */
|
|
/* top wall */
|
|
if ( /*(!(mp->maze[isize + j] & DOOR_IN_TOP)) &&
|
|
(!(mp->maze[isize + j] & DOOR_OUT_TOP)) && */
|
|
(mp->maze[isize + j] & WALL_TOP))
|
|
draw_wall(mi, i, j, 0);
|
|
/* left wall */
|
|
if ( /*(!(mp->maze[isize + j] & DOOR_IN_RIGHT)) &&
|
|
(!(mp->maze[isize + j] & DOOR_OUT_RIGHT)) && */
|
|
(mp->maze[isize + j] & WALL_RIGHT))
|
|
draw_wall(mi, i, j, 1);
|
|
}
|
|
}
|
|
} /* end of draw_maze_walls() */
|
|
|
|
static int
|
|
backup(mazestruct * mp)
|
|
{ /* back up a move */
|
|
mp->sqnum--;
|
|
if (mp->sqnum >= 0) {
|
|
mp->cur_sq_x = mp->move_list[mp->sqnum].x;
|
|
mp->cur_sq_y = mp->move_list[mp->sqnum].y;
|
|
}
|
|
return (mp->sqnum);
|
|
} /* end of backup() */
|
|
|
|
static void
|
|
create_maze_walls(ModeInfo * mi)
|
|
{ /* create a maze layout given the intiialized maze */
|
|
mazestruct *mp = &mazes[MI_SCREEN(mi)];
|
|
register int i, newdoor;
|
|
|
|
for (;;) {
|
|
mp->move_list[mp->sqnum].x = mp->cur_sq_x;
|
|
mp->move_list[mp->sqnum].y = mp->cur_sq_y;
|
|
mp->move_list[mp->sqnum].dir = -1;
|
|
while ((newdoor = choose_door(mi)) == -1) /* pick a door */
|
|
if (backup(mp) == -1) /* no more doors ... backup */
|
|
return; /* done ... return */
|
|
|
|
/* mark the out door */
|
|
mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] |=
|
|
(DOOR_OUT_TOP >> newdoor);
|
|
|
|
switch (newdoor) {
|
|
case 0:
|
|
mp->cur_sq_y--;
|
|
break;
|
|
case 1:
|
|
mp->cur_sq_x++;
|
|
break;
|
|
case 2:
|
|
mp->cur_sq_y++;
|
|
break;
|
|
case 3:
|
|
mp->cur_sq_x--;
|
|
break;
|
|
}
|
|
mp->sqnum++;
|
|
|
|
/* mark the in door */
|
|
mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] |=
|
|
(DOOR_IN_TOP >> ((newdoor + 2) % 4));
|
|
|
|
/* if end square set path length and save path */
|
|
if (mp->maze[mp->cur_sq_x * mp->nrows + mp->cur_sq_y] & END_SQUARE) {
|
|
mp->path_length = mp->sqnum;
|
|
for (i = 0; i < mp->path_length; i++) {
|
|
mp->save_path[i].x = mp->move_list[i].x;
|
|
mp->save_path[i].y = mp->move_list[i].y;
|
|
mp->save_path[i].dir = mp->move_list[i].dir;
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* end of create_maze_walls() */
|
|
|
|
static void
|
|
draw_maze_border(ModeInfo * mi)
|
|
{ /* draw the maze outline */
|
|
Display *display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
mazestruct *mp = &mazes[MI_SCREEN(mi)];
|
|
GC gc = mp->backGC;
|
|
register int i, j;
|
|
|
|
if (mp->logo_x != -1) {
|
|
(void) XPutImage(display, window, gc, mp->logo,
|
|
0, 0,
|
|
mp->xb + mp->xs * mp->logo_x +
|
|
(mp->xs * mp->logo_size_x - mp->logo->width + 1) / 2,
|
|
mp->yb + mp->ys * mp->logo_y +
|
|
(mp->ys * mp->logo_size_y - mp->logo->height + 1) / 2,
|
|
mp->logo->width, mp->logo->height);
|
|
}
|
|
for (i = 0; i < mp->ncols; i++) {
|
|
if (mp->maze[i * mp->nrows] & WALL_TOP) {
|
|
XDrawLine(display, window, gc,
|
|
mp->xb + mp->xs * i, mp->yb,
|
|
mp->xb + mp->xs * (i + 1), mp->yb);
|
|
}
|
|
if ((mp->maze[i * mp->nrows + mp->nrows - 1] & WALL_BOTTOM)) {
|
|
XDrawLine(display, window, gc,
|
|
mp->xb + mp->xs * i,
|
|
mp->yb + mp->ys * (mp->nrows),
|
|
mp->xb + mp->xs * (i + 1),
|
|
mp->yb + mp->ys * (mp->nrows));
|
|
}
|
|
}
|
|
for (j = 0; j < mp->nrows; j++) {
|
|
if (mp->maze[(mp->ncols - 1) * mp->nrows + j] & WALL_RIGHT) {
|
|
XDrawLine(display, window, gc,
|
|
mp->xb + mp->xs * mp->ncols,
|
|
mp->yb + mp->ys * j,
|
|
mp->xb + mp->xs * mp->ncols,
|
|
mp->yb + mp->ys * (j + 1));
|
|
}
|
|
if (mp->maze[j] & WALL_LEFT) {
|
|
XDrawLine(display, window, gc,
|
|
mp->xb, mp->yb + mp->ys * j,
|
|
mp->xb, mp->yb + mp->ys * (j + 1));
|
|
}
|
|
}
|
|
|
|
draw_solid_square(mi, gc, mp->start_x, mp->start_y, mp->start_dir);
|
|
draw_solid_square(mi, gc, mp->end_x, mp->end_y, mp->end_dir);
|
|
} /* end of draw_maze() */
|
|
|
|
static void
|
|
try_to_move(ModeInfo *mi, int dir)
|
|
{ /* based on solve_maze */
|
|
mazestruct *mp = &mazes[MI_SCREEN(mi)];
|
|
int colliding = mp->maze[mp->path[mp->current_path].x * mp->nrows +
|
|
mp->path[mp->current_path].y] &
|
|
(WALL_TOP >> dir); /* Are we trying to go through a wall? */
|
|
|
|
if (!colliding && ((mp->current_path == 0) ||
|
|
/* We're either on the first path or we're not going backwards */
|
|
((dir != (unsigned char) (mp->path[mp->current_path - 1].dir +
|
|
2) % 4))) ) {
|
|
mp->path[mp->current_path].dir = dir;
|
|
enter_square(mi, mp->current_path);
|
|
mp->current_path++;
|
|
#if 0
|
|
(void) fprintf(stderr,
|
|
"Calling draw_solid_square with params: x=%d, y= %d, dir=%d\n",
|
|
(int) (mp->path[mp->current_path].x),
|
|
(int) (mp->path[mp->current_path].y),
|
|
(int) (dir));
|
|
#endif
|
|
/* The following call is supposed to make things look more
|
|
intutive, i.e., that the square we think we are on is
|
|
filled in.
|
|
The direction has to be reversed to prevent it from
|
|
drawing squares on top of lines.
|
|
*/
|
|
draw_solid_square(mi, mp->backGC,
|
|
(int) (mp->path[mp->current_path].x),
|
|
(int) (mp->path[mp->current_path].y),
|
|
(int) ((dir + 2) % 4));
|
|
if (mp->maze[mp->path[mp->current_path].x * mp->nrows +
|
|
mp->path[mp->current_path].y] & START_SQUARE) {
|
|
mp->solving = 0;
|
|
return;
|
|
}
|
|
} else {
|
|
if (!colliding) {
|
|
draw_solid_square(mi, mp->grayGC,
|
|
(int) (mp->path[mp->current_path].x),
|
|
(int) (mp->path[mp->current_path].y),
|
|
(int) (mp->path[mp->current_path - 1].dir +
|
|
2) % 4);
|
|
mp->path[mp->current_path].dir = 4;
|
|
mp->current_path--;
|
|
draw_solid_square(mi, mp->grayGC,
|
|
(int) (mp->path[mp->current_path].x),
|
|
(int) (mp->path[mp->current_path].y),
|
|
(int) (mp->path[mp->current_path].dir));
|
|
}
|
|
#if 0
|
|
else {
|
|
/* Beep if we can't go in that direction */
|
|
(void) fprintf(stdout, "\a");
|
|
(void) fflush(stdout);
|
|
}
|
|
#endif
|
|
}
|
|
} /* end of try_to_move() */
|
|
|
|
static void
|
|
solve_maze(ModeInfo * mi)
|
|
{ /* solve it with graphical feedback */
|
|
mazestruct *mp = &mazes[MI_SCREEN(mi)];
|
|
|
|
if (!mp->solving) {
|
|
/* plug up the surrounding wall */
|
|
mp->maze[mp->start_x * mp->nrows + mp->start_y] |=
|
|
(WALL_TOP >> mp->start_dir);
|
|
mp->maze[mp->end_x * mp->nrows + mp->end_y] |=
|
|
(WALL_TOP >> mp->end_dir);
|
|
|
|
/* initialize search path */
|
|
mp->current_path = 0;
|
|
mp->path[mp->current_path].x = mp->end_x;
|
|
mp->path[mp->current_path].y = mp->end_y;
|
|
mp->path[mp->current_path].dir = -1;
|
|
|
|
mp->solving = 1;
|
|
}
|
|
if (++mp->path[mp->current_path].dir >= 4) {
|
|
if (mp->current_path == 0) {
|
|
/* this can happen if a person backs up from going
|
|
the right way and then autosolves */
|
|
mp->path[mp->current_path].dir = -1;
|
|
return;
|
|
}
|
|
/* This draw is to fill in the dead ends,
|
|
it ends up drawing more gray boxes then it needs to. */
|
|
draw_solid_square(mi, mp->grayGC,
|
|
(int) (mp->path[mp->current_path].x),
|
|
(int) (mp->path[mp->current_path].y),
|
|
(int) (mp->path[mp->current_path - 1].dir + 2) % 4);
|
|
|
|
mp->current_path--;
|
|
draw_solid_square(mi, mp->grayGC,
|
|
(int) (mp->path[mp->current_path].x),
|
|
(int) (mp->path[mp->current_path].y),
|
|
(int) (mp->path[mp->current_path].dir));
|
|
} else if (!(mp->maze[mp->path[mp->current_path].x * mp->nrows +
|
|
mp->path[mp->current_path].y] &
|
|
(WALL_TOP >> mp->path[mp->current_path].dir)) &&
|
|
((mp->current_path == 0) ||
|
|
((mp->path[mp->current_path].dir !=
|
|
(unsigned char) (mp->path[mp->current_path - 1].dir +
|
|
2) % 4)))) {
|
|
enter_square(mi, mp->current_path);
|
|
mp->current_path++;
|
|
/* This next call is to make the appearance more 'intuitive' */
|
|
draw_solid_square(mi, mp->backGC,
|
|
(int) (mp->path[mp->current_path].x),
|
|
(int) (mp->path[mp->current_path].y),
|
|
(int) (mp->path[mp->current_path - 1].dir + 2) % 4);
|
|
if (mp->maze[mp->path[mp->current_path].x * mp->nrows +
|
|
mp->path[mp->current_path].y] & START_SQUARE) {
|
|
mp->solving = 0;
|
|
return;
|
|
}
|
|
}
|
|
} /* end of solve_maze() */
|
|
|
|
static void
|
|
mouse_maze(ModeInfo * mi)
|
|
{ /* solve it with graphical feedback */
|
|
mazestruct *mp = &mazes[MI_SCREEN(mi)];
|
|
Window r, c;
|
|
int dx, dy, cx, cy;
|
|
unsigned int m;
|
|
|
|
(void) XQueryPointer(MI_DISPLAY(mi), MI_WINDOW(mi),
|
|
&r, &c, &dx, &dy, &cx, &cy, &m);
|
|
dx = cx - (mp->path[mp->current_path].x * mp->xs + mp->xb) - mp->xs / 2;
|
|
dy = cy - (mp->path[mp->current_path].y * mp->ys + mp->yb) - mp->ys / 2;
|
|
if (cx <= mp->xb || cy <= mp->yb || cx > mp->ncols * mp->xs + mp->xb ||
|
|
cy > mp->nrows * mp->ys + mp->yb) {
|
|
solve_maze(mi);
|
|
return;
|
|
}
|
|
if (2 * abs(dx) / (mp->xs + 1) > 2 * abs(dy) / mp->ys) {
|
|
if (dx > 0) {
|
|
try_to_move(mi, DIR_RIGHT);
|
|
} else {
|
|
try_to_move(mi, DIR_LEFT);
|
|
}
|
|
} else if (2 * abs(dx) / mp->xs < 2 * abs(dy) / (mp->ys + 1)) {
|
|
if (dy > 0) {
|
|
try_to_move(mi, DIR_DOWN);
|
|
} else {
|
|
try_to_move(mi, DIR_UP);
|
|
}
|
|
}
|
|
}
|
|
|
|
static Bool
|
|
init_stuff(ModeInfo * mi)
|
|
{
|
|
Display *display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
mazestruct *mp = &mazes[MI_SCREEN(mi)];
|
|
|
|
if (mp->logo == None) {
|
|
getImage(mi, &mp->logo, ICON_WIDTH, ICON_HEIGHT, ICON_BITS,
|
|
#ifdef HAVE_XPM
|
|
DEFAULT_XPM, ICON_NAME,
|
|
#endif
|
|
&mp->graphics_format, &mp->cmap, &mp->black);
|
|
if (mp->logo == None) {
|
|
free_maze(display, mp);
|
|
return False;
|
|
}
|
|
}
|
|
#ifndef STANDALONE
|
|
if (mp->cmap != None) {
|
|
setColormap(display, window, mp->cmap, MI_IS_INWINDOW(mi));
|
|
if (mp->backGC == None) {
|
|
XGCValues xgcv;
|
|
|
|
xgcv.background = mp->black;
|
|
if ((mp->backGC = XCreateGC(display, window, GCBackground,
|
|
&xgcv)) == None) {
|
|
free_maze(display, mp);
|
|
return False;
|
|
}
|
|
}
|
|
} else
|
|
#endif /* STANDALONE */
|
|
{
|
|
mp->black = MI_BLACK_PIXEL(mi);
|
|
mp->backGC = MI_GC(mi);
|
|
}
|
|
return True;
|
|
}
|
|
|
|
void
|
|
init_maze(ModeInfo * mi)
|
|
{
|
|
Display *display = MI_DISPLAY(mi);
|
|
mazestruct *mp;
|
|
|
|
if (mazes == NULL) {
|
|
if ((mazes = (mazestruct *) calloc(MI_NUM_SCREENS(mi),
|
|
sizeof (mazestruct))) == NULL)
|
|
return;
|
|
}
|
|
mp = &mazes[MI_SCREEN(mi)];
|
|
|
|
if (!init_stuff(mi))
|
|
return;
|
|
|
|
mp->width = MI_WIDTH(mi);
|
|
mp->height = MI_HEIGHT(mi);
|
|
mp->width = (mp->width >= 32) ? mp->width : 32;
|
|
mp->height = (mp->height >= 32) ? mp->height : 32;
|
|
|
|
if (mp->graypix == None)
|
|
if ((mp->graypix = XCreateBitmapFromData(display, MI_WINDOW(mi),
|
|
(char *) gray1_bits, gray1_width, gray1_height)) == None) {
|
|
free_maze(display, mp);
|
|
return;
|
|
}
|
|
if (!mp->grayGC) {
|
|
if ((mp->grayGC = XCreateGC(display, MI_WINDOW(mi),
|
|
0L, (XGCValues *) 0)) == None) {
|
|
free_maze(display, mp);
|
|
return;
|
|
}
|
|
XSetBackground(display, mp->grayGC, mp->black);
|
|
XSetFillStyle(display, mp->grayGC, FillOpaqueStippled);
|
|
XSetStipple(display, mp->grayGC, mp->graypix);
|
|
}
|
|
mp->solving = 0;
|
|
mp->stage = 0;
|
|
mp->time = 0;
|
|
mp->cycles = MI_CYCLES(mi);
|
|
if (mp->cycles < 4)
|
|
mp->cycles = 4;
|
|
}
|
|
|
|
void
|
|
draw_maze(ModeInfo * mi)
|
|
{
|
|
mazestruct *mp;
|
|
|
|
if (mazes == NULL)
|
|
return;
|
|
mp = &mazes[MI_SCREEN(mi)];
|
|
if (mp->graypix == None)
|
|
return;
|
|
if (mp->solving) {
|
|
if (trackmouse)
|
|
mouse_maze(mi);
|
|
else
|
|
solve_maze(mi);
|
|
return;
|
|
}
|
|
switch (mp->stage) {
|
|
case 0:
|
|
MI_CLEARWINDOWCOLORMAP(mi, mp->backGC, mp->black);
|
|
|
|
if (!set_maze_sizes(mi))
|
|
return;
|
|
initialize_maze(mi);
|
|
create_maze_walls(mi);
|
|
mp->stage++;
|
|
break;
|
|
case 1:
|
|
draw_maze_border(mi);
|
|
mp->stage++;
|
|
break;
|
|
case 2:
|
|
draw_maze_walls(mi);
|
|
mp->stage++;
|
|
break;
|
|
case 3:
|
|
if (++mp->time > mp->cycles / (4 * (1 + trackmouse)))
|
|
mp->stage++;
|
|
break;
|
|
case 4:
|
|
if (trackmouse) {
|
|
/* Initialize paths and draw a square or two */
|
|
int i;
|
|
for (i = 0; i < 4; i++)
|
|
solve_maze(mi);
|
|
} else {
|
|
solve_maze(mi);
|
|
}
|
|
mp->stage++;
|
|
break;
|
|
case 5:
|
|
if (++mp->time > 3 * mp->cycles / 4)
|
|
init_maze(mi);
|
|
break;
|
|
}
|
|
} /* end of draw_maze() */
|
|
|
|
void
|
|
release_maze(ModeInfo * mi)
|
|
{
|
|
if (mazes != NULL) {
|
|
int screen;
|
|
|
|
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
|
|
free_maze(MI_DISPLAY(mi), &mazes[screen]);
|
|
free(mazes);
|
|
mazes = (mazestruct *) NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
refresh_maze(ModeInfo * mi)
|
|
{
|
|
mazestruct *mp;
|
|
|
|
if (mazes == NULL)
|
|
return;
|
|
mp = &mazes[MI_SCREEN(mi)];
|
|
if (mp->graypix == None)
|
|
return;
|
|
|
|
#ifdef HAVE_XPM
|
|
if (mp->graphics_format >= IS_XPM) {
|
|
/* This is needed when another program changes the colormap. */
|
|
free_maze(MI_DISPLAY(mi), mp);
|
|
init_maze(mi);
|
|
return;
|
|
}
|
|
#endif
|
|
MI_CLEARWINDOWCOLORMAP(mi, mp->backGC, mp->black);
|
|
XSetForeground(MI_DISPLAY(mi), mp->backGC, mp->color);
|
|
if (mp->stage >= 1) {
|
|
mp->stage = 3;
|
|
mp->sqnum = 0;
|
|
mp->cur_sq_x = mp->start_x;
|
|
mp->cur_sq_y = mp->start_y;
|
|
mp->maze[mp->start_x * mp->nrows + mp->start_y] |= START_SQUARE;
|
|
mp->maze[mp->start_x * mp->nrows + mp->start_y] |=
|
|
(DOOR_IN_TOP >> mp->start_dir);
|
|
mp->maze[mp->start_x * mp->nrows + mp->start_y] &=
|
|
~(WALL_TOP >> mp->start_dir);
|
|
mp->maze[mp->end_x * mp->nrows + mp->end_y] |= END_SQUARE;
|
|
mp->maze[mp->end_x * mp->nrows + mp->end_y] |=
|
|
(DOOR_OUT_TOP >> mp->end_dir);
|
|
mp->maze[mp->end_x * mp->nrows + mp->end_y] &=
|
|
~(WALL_TOP >> mp->end_dir);
|
|
draw_maze_border(mi);
|
|
draw_maze_walls(mi);
|
|
}
|
|
mp->solving = 0;
|
|
}
|
|
|
|
#endif /* MODE_maze */
|