[comp.os.minix] LANDMINE - a game for mini-X graphics

dbell@pdact.pd.necisa.oz.au (David I. Bell) (04/30/91)

Here is a game that uses mini-X graphics.  So now you have a client program
that actually does something useful (assuming ANY game can be useful :-).
Landmine is one of the best deductive style of games I have seen in years.

-dbell-

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  landmine.c landmine.doc
# Wrapped by dbell@elm on Tue Apr 30 12:28:14 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'landmine.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'landmine.c'\"
else
echo shar: Extracting \"'landmine.c'\" \(28343 characters\)
sed "s/^X//" >'landmine.c' <<'END_OF_FILE'
X/*
X * Landmine, the game.
X * Written for mini-X by David I. Bell.
X */
X
X#include <stdio.h>
X#include <stdarg.h>
X#include <graphics.h>
X
X
X#define	MINSIZE		3		/* minimum size of board */
X#define MAXSIZE		30		/* maximum size of board */
X#define SIZE		15		/* default size of playing board */
X#define	MINEPERCENT	15		/* default percentage of mines */
X#define	SAVEFILE	"landmine.save"	/* default save file name */
X#define MAGIC		649351261	/* magic number in save files */
X#define	MAXPARAMS	1000		/* maximum different game parameters */
X
X#define FULLSIZE	(MAXSIZE + 2)	/* board size including borders */
X
X#define	BOARDGAP	10		/* millimeter gap around board */
X#define	RIGHTGAP	15		/* mm gap between board, right side */
X#define	BUTTONGAP	20		/* mm gap between buttons */
X#define	STATUSGAP	35		/* mm gap between buttons and status */
X
X#define	BUTTONWIDTH	80		/* width of buttons (pixels) */
X#define	BUTTONHEIGHT	25		/* height of buttons (pixels) */
X#define	RIGHTSIDE	150		/* pixels to guarantee for right side */
X#define	BOARDBORDER	2		/* border size around board */
X
X#define	BLUE		1		/* colors */
X#define	GREEN		2
X#define	RED		4
X#define	GREY		7
X
X
X/*
X * Print the number of steps taken.
X * This is used twice, and is a macro to guarantee that
X * the two printouts match.
X */
X#define	PRINTSTEPS	printline(2, "Steps: %3d\n", steps)
X
X
X/*
X * Typedefs local to this program.
X */
Xtypedef	unsigned short	CELL;	/* cell value */
Xtypedef	int		POS;	/* cell position */
X
X
X/*
X * For defining bitmaps easily.
X */
X#define	X	((unsigned) 1)
X#define	_	((unsigned) 0)
X
X#define	BITS(a,b,c,d,e,f,g,h,i) \
X	((((((((a*2+b)*2+c)*2+d)*2+e)*2+f)*2+g)*2+h)*2+i << 7)
X
X
Xstatic	GR_BITMAP	twolegs_fg[] = {	/* two legs foreground */
X	BITS(_,_,_,_,_,_,_,_,_),
X	BITS(_,_,_,X,X,X,_,_,_),
X	BITS(_,_,_,X,X,X,_,_,_),
X	BITS(_,_,_,X,X,X,_,_,_),
X	BITS(_,_,_,_,X,_,_,_,_),
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,X,_,X,_,X,_,X,_),
X	BITS(_,X,_,X,X,X,_,X,_),
X	BITS(_,_,_,X,_,X,_,_,_),
X	BITS(_,_,_,X,_,X,_,_,_),
X	BITS(_,_,X,X,_,X,X,_,_),
X	BITS(_,_,_,_,_,_,_,_,_)
X};
X
Xstatic	GR_BITMAP	twolegs_bg[] = {	/* two legs background */
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,X,X,X,X,X,X,X,_),
X	BITS(X,X,X,X,X,X,X,X,X),
X	BITS(X,X,X,X,_,X,X,X,X),
X	BITS(X,X,X,X,X,X,X,X,X),
X	BITS(X,X,X,X,X,X,X,X,X),
X	BITS(_,X,X,X,X,X,X,X,_),
X	BITS(_,X,X,X,X,X,X,X,_),
X	BITS(_,X,X,X,X,X,X,X,_)
X};
X
X
Xstatic	GR_BITMAP	oneleg_fg[] = {		/* one leg foreground */
X	BITS(_,_,_,_,_,_,_,_,_),
X	BITS(_,_,_,X,X,X,_,_,_),
X	BITS(_,_,_,X,X,X,_,_,_),
X	BITS(_,_,_,X,X,X,_,_,_),
X	BITS(_,_,_,_,X,_,_,_,_),
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,X,_,X,_,X,_,X,_),
X	BITS(_,_,_,X,X,X,_,X,_),
X	BITS(_,_,_,_,_,X,_,_,_),
X	BITS(_,_,_,_,_,X,_,_,_),
X	BITS(_,_,_,_,_,X,X,_,_),
X	BITS(_,_,_,_,_,_,_,_,_),
X};
X
X
Xstatic	GR_BITMAP	oneleg_bg[] = {		/* one leg background */
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,X,X,X,X,X,X,X,_),
X	BITS(X,X,X,X,X,X,X,X,X),
X	BITS(X,X,X,X,_,X,X,X,X),
X	BITS(X,X,X,X,X,X,X,X,X),
X	BITS(_,_,X,X,X,X,X,X,X),
X	BITS(_,_,_,_,X,X,X,X,_),
X	BITS(_,_,_,_,X,X,X,X,_),
X	BITS(_,_,_,_,X,X,X,X,_)
X};
X
X
Xstatic	GR_BITMAP	noleg_fg[] = {		/* no legs foreground */
X	BITS(_,_,_,_,_,_,_,_,_),
X	BITS(_,_,_,X,X,X,_,_,_),
X	BITS(_,_,_,X,X,X,_,_,_),
X	BITS(_,_,_,X,X,X,_,_,_),
X	BITS(_,_,_,_,X,_,_,_,_),
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,X,_,X,_,X,_,X,_),
X	BITS(_,_,_,X,X,X,_,_,_),
X	BITS(_,_,_,_,_,_,_,_,_),
X	BITS(_,_,_,_,_,_,_,_,_),
X	BITS(_,_,_,_,_,_,_,_,_),
X	BITS(_,_,_,_,_,_,_,_,_),
X};
X
X
Xstatic	GR_BITMAP	noleg_bg[] = {		/* no legs background */
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,X,X,X,X,X,X,X,_),
X	BITS(X,X,X,X,X,X,X,X,X),
X	BITS(X,X,X,X,_,X,X,X,X),
X	BITS(X,X,X,X,X,X,X,X,X),
X	BITS(_,_,X,X,X,X,X,_,_),
X	BITS(_,_,_,_,_,_,_,_,_),
X	BITS(_,_,_,_,_,_,_,_,_),
X	BITS(_,_,_,_,_,_,_,_,_)
X};
X
X
X/*
X * Components of a cell.
X */
X#define F_EMPTY		' '		/* default value for empty square */
X#define F_REMEMBER	'*'		/* character to remember mine */
X#define F_WRONG		'X'		/* character to remember wrong guess */
X#define F_DISPLAY	0xff		/* character to be displayed here */
X#define F_MINE		0x100		/* TRUE if a mine is here */
X#define F_EDGE		0x200		/* TRUE if this is edge of the world */
X#define F_OLD		0x400		/* TRUE if been at this square before */
X#define F_REACH		0x800		/* TRUE if can reach this square */
X#define F_FLAGS		0xff00		/* all flags */
X
X
X/*
X * The status of the game.
X * This structure is read and written from the save file.
X */
Xstatic	struct status {			/* status of games */
X	long	s_magic;		/* magic number */
X	short	s_playing;		/* TRUE if playing a game */
X	short	s_size;			/* current size of board */
X	short	s_mines;		/* current number of mines on board */
X	short	s_legs;			/* number of legs left */
X	short	s_steps;		/* number of steps taken this game */
X	short	s_index;		/* current game parameter index */
X	short	s_sizeparam[MAXPARAMS];	/* table of size parameters */
X	short	s_mineparam[MAXPARAMS];	/* table of mine parameters */
X	long	s_games0[MAXPARAMS];	/* games finished with no legs */
X	long	s_games1[MAXPARAMS];	/* games finished with one leg */
X	long	s_games2[MAXPARAMS];	/* games finished with two legs */
X	long	s_steps0[MAXPARAMS];	/* steps taken in no leg games */
X	long	s_steps1[MAXPARAMS];	/* steps taken in one leg games */
X	long	s_steps2[MAXPARAMS];	/* steps taken in two leg games */
X	CELL	s_board[FULLSIZE*FULLSIZE];	/* board layout */
X} st;
X
X
X/*
X * Definitions to make structure references easy.
X */
X#define magic		st.s_magic
X#define	playing		st.s_playing
X#define size		st.s_size
X#define mines		st.s_mines
X#define legs		st.s_legs
X#define	steps		st.s_steps
X#define	index		st.s_index
X#define	sizeparam	st.s_sizeparam
X#define	mineparam	st.s_mineparam
X#define games0		st.s_games0
X#define games1		st.s_games1
X#define games2		st.s_games2
X#define	steps0		st.s_steps0
X#define	steps1		st.s_steps1
X#define	steps2		st.s_steps2
X#define board		st.s_board
X
X
X#define boardpos(row, col)	(((row) * FULLSIZE) + (col))
X#define ismine(cell)		((cell) & F_MINE)
X#define isedge(cell)		((cell) & F_EDGE)
X#define isold(cell)		((cell) & F_OLD)
X#define isseen(cell)		(((cell) & F_DISPLAY) == F_REMEMBER)
X#define isknown(cell)		(((cell) & F_DISPLAY) != F_EMPTY)
X#define displaychar(cell)	((cell) & F_DISPLAY)
X#define badsquare(n)		(((n) <= 0) || ((n) > size))
X
X
X/*
X * Offsets for accessing adjacent cells.
X */
Xstatic	POS	steptable[8] = {
X	FULLSIZE, -FULLSIZE, 1, -1, FULLSIZE-1,
X	FULLSIZE+1, -FULLSIZE-1, -FULLSIZE+1
X};
X
X
Xstatic	GR_WINDOW_ID	mainwid;	/* main window id */
Xstatic	GR_WINDOW_ID	boardwid;	/* board window id */
Xstatic	GR_WINDOW_ID	statwid;	/* status display window id */
Xstatic	GR_WINDOW_ID	quitwid;	/* window id for quit button */
Xstatic	GR_WINDOW_ID	savewid;	/* window id for save button */
Xstatic	GR_WINDOW_ID	newgamewid;	/* window id for new game button */
X
Xstatic	GR_GC_ID	boardgc;	/* graphics context for board */
Xstatic	GR_GC_ID	cleargc;	/* GC for clearing cell of board */
Xstatic	GR_GC_ID	redgc;		/* GC for drawing red */
Xstatic	GR_GC_ID	greengc;	/* GC for drawing green */
Xstatic	GR_GC_ID	blackgc;	/* GC for drawing black */
Xstatic	GR_GC_ID	delaygc;	/* GC for delaying */
Xstatic	GR_GC_ID	statgc;		/* GC for status window */
Xstatic	GR_GC_ID	buttongc;	/* GC for drawing buttons */
Xstatic	GR_GC_ID	xorgc;		/* GC for inverting things */
X
Xstatic	GR_SIZE		xp;		/* pixels for x direction per square */
Xstatic	GR_SIZE		yp;		/* pixels for y direction per square */
Xstatic	GR_SIZE		statwidth;	/* width of window drawing text in */
Xstatic	GR_SIZE		statheight;	/* height of window drawing text in */
Xstatic	GR_SIZE		charheight;	/* height of characters */
Xstatic	GR_COORD	charxpos;	/* current X position for characters */
Xstatic	GR_COORD	charypos;	/* current Y position for characters */
X
Xstatic	GR_SCREEN_INFO	si;		/* window information */
Xstatic	GR_FONT_INFO	fi;		/* font information */
X
Xstatic	char	*savefile;		/* filename for saving game */
X
X
X/*
X * Procedures.
X */
Xstatic	void	printline();
Xstatic	void	newline();
Xstatic	void	delay();
Xstatic	void	dokey();
Xstatic	void	readevent();
Xstatic	void	doexposure();
Xstatic	void	dobutton();
Xstatic	void	drawbomb();
Xstatic	void	drawstatus();
Xstatic	void	drawbutton();
Xstatic	void	drawboard();
Xstatic	void	drawcell();
Xstatic	void	cellcenter();
Xstatic	void	clearcell();
Xstatic	void	newgame();
Xstatic	void	moveto();
Xstatic	void	setcursor();
Xstatic	void	togglecell();
Xstatic	void	gameover();
Xstatic	void	readgame();
Xstatic	void	findindex();
Xstatic	POS	findcell();
Xstatic	GR_BOOL	checkpath();
Xstatic	GR_BOOL	writegame();
X
X
X
Xmain(argc, argv)
X	char **argv;
X{
X	GR_COORD	x;
X	GR_COORD	y;
X	GR_SIZE		width;
X	GR_SIZE		height;
X	GR_COORD	rightx;		/* x coordinate for right half stuff */
X	GR_BOOL		setsize;	/* TRUE if size of board is set */
X	GR_BOOL		setmines;	/* TRUE if number of mines is set */
X	GR_SIZE		newsize;	/* desired size of board */
X	GR_COUNT	newmines;	/* desired number of mines */
X
X	setmines = GR_FALSE;
X	setsize = GR_FALSE;
X
X	argc--;
X	argv++;
X	while ((argc > 0) && (**argv == '-')) {
X		switch (argv[0][1]) {
X			case 'm':
X				if (argc <= 0) {
X					fprintf(stderr, "Missing mine count\n");
X					exit(1);
X				}
X				argc--;
X				argv++;
X				newmines = atoi(*argv);
X				setmines = GR_TRUE;
X				break;
X
X			case 's':
X				if (argc <= 0) {
X					fprintf(stderr, "Missing size\n");
X					exit(1);
X				}
X				argc--;
X				argv++;
X				newsize = atoi(*argv);
X				setsize = GR_TRUE;
X				break;
X
X			default:
X				fprintf(stderr, "Unknown option \"-%c\"\n",
X					argv[0][1]);
X				exit(1);
X		}
X		argc--;
X		argv++;
X	}
X	if (argc > 0)
X		savefile = *argv;
X
X	srand(time(0));
X
X	readgame(savefile);
X
X	if (setsize) {
X		if ((newsize < MINSIZE) || (newsize > MAXSIZE)) {
X			fprintf(stderr, "Illegal board size\n");
X			exit(1);
X		}
X		if (newsize != size) {
X			if (steps && playing) {
X				fprintf(stderr,
X					"Cannot change size while game is in progress\n");
X				exit(1);
X			}
X			playing = GR_FALSE;
X			size = newsize;
X			if (!playing)
X				mines = (size * size * MINEPERCENT) / 100;
X		}
X	}
X
X	if (setmines) {
X		if ((newmines <= 0) || ((newmines > (size * size) / 2))) {
X			fprintf(stderr, "Illegal number of mines\n");
X			exit(1);
X		}
X		if (newmines != mines) {
X			if (steps && playing) {
X				fprintf(stderr,
X					"Cannot change mines while game is in progress\n");
X				exit(1);
X			}
X			playing = GR_FALSE;
X			mines = newmines;
X		}
X	}
X
X	findindex();
X
X	/*
X	 * Parameters of the game have been verified.
X	 * Now open the graphics and play the game.
X	 */
X	if (GrOpen() < 0) {
X		fprintf(stderr, "Cannot open graphics\n");
X		exit(1);
X	}
X
X	GrGetScreenInfo(&si);
X	GrGetFontInfo(0, &fi);
X	charheight = fi.height;
X
X	/*
X	 * Create the main window which will contain all the others.
X	 */
X	mainwid = GrNewWindow(GR_ROOT_WINDOW_ID, 0, 0, si.cols, si.rows,
X		0, si.black, si.white);
X
X	/*
X	 * Create the board window which lies at the left side.
X	 * Make the board square, and as large as possible while still
X	 * leaving room to the right side for statistics and buttons.
X	 */
X	width = si.cols - RIGHTSIDE - (si.xdpcm * RIGHTGAP / 10) - BOARDBORDER * 2;
X	height = (((long) width) * si.ydpcm) / si.xdpcm;
X	if (height > si.rows - y * 2) {
X		height = si.rows - BOARDBORDER * 2;
X		width = (((long) height) * si.xdpcm) / si.ydpcm;
X	}
X	xp = width / size;
X	yp = height / size;
X	width = xp * size - 1;
X	height = yp * size - 1;
X	x = BOARDBORDER;
X	y = (si.rows - height) / 2;
X	rightx = x + width + (si.xdpcm * RIGHTGAP / 10);
X	boardwid = GrNewWindow(mainwid, x, y, width, height, BOARDBORDER,
X		BLUE, si.white);
X
X	/*
X	 * Create the buttons.
X	 */
X	x = rightx;
X	y = (si.ydpcm * BOARDGAP / 10);
X	quitwid = GrNewWindow(mainwid, x, y, BUTTONWIDTH, BUTTONHEIGHT,
X		1, RED, si.white);
X
X	y += (si.ydpcm * BUTTONGAP / 10);
X	savewid = GrNewWindow(mainwid, x, y, BUTTONWIDTH, BUTTONHEIGHT,
X		1, GREEN, si.white);
X
X	y += (si.ydpcm * BUTTONGAP / 10);
X	newgamewid = GrNewWindow(mainwid, x, y, BUTTONWIDTH, BUTTONHEIGHT,
X		1, GREEN, si.white);
X
X	/*
X	 * Create the statistics window.
X	 */
X	x = rightx;
X	y += (si.ydpcm * STATUSGAP / 10);
X	width = si.cols - x;
X	height = si.rows - y;
X	statwid = GrNewWindow(mainwid, x, y, width, height, 0,
X		0, 0);
X	statwidth = width;
X	statheight = height;
X
X	/*
X	 * Create the GC for drawing the board.
X	 */
X	boardgc = GrNewGC();
X	cleargc = GrNewGC();
X	delaygc = GrNewGC();
X	redgc = GrNewGC();
X	greengc = GrNewGC();
X	statgc = GrNewGC();
X	blackgc = GrNewGC();
X	buttongc = GrNewGC();
X	xorgc = GrNewGC();
X	GrSetGCBackground(boardgc, BLUE);
X	GrSetGCForeground(cleargc, BLUE);
X	GrSetGCForeground(redgc, RED);
X	GrSetGCForeground(greengc, GREEN);
X	GrSetGCForeground(statgc, GREY);
X	GrSetGCForeground(delaygc, 0);
X	GrSetGCForeground(blackgc, si.black);
X	GrSetGCMode(delaygc, GR_MODE_XOR);
X	GrSetGCMode(xorgc, GR_MODE_XOR);
X	GrSetGCUseBackground(boardgc, GR_FALSE);
X	GrSetGCUseBackground(buttongc, GR_FALSE);
X
X	GrSelectEvents(boardwid, GR_EVENT_MASK_EXPOSURE |
X		GR_EVENT_MASK_BUTTON_DOWN | GR_EVENT_MASK_KEY_DOWN);
X
X	GrSelectEvents(statwid, GR_EVENT_MASK_EXPOSURE);
X
X	GrSelectEvents(quitwid, GR_EVENT_MASK_EXPOSURE |
X		GR_EVENT_MASK_BUTTON_DOWN);
X
X	GrSelectEvents(newgamewid, GR_EVENT_MASK_EXPOSURE |
X		GR_EVENT_MASK_BUTTON_DOWN);
X
X	GrSelectEvents(savewid, GR_EVENT_MASK_EXPOSURE |
X		GR_EVENT_MASK_BUTTON_DOWN);
X
X	setcursor();
X
X	GrMapWindow(mainwid);
X	GrMapWindow(boardwid);
X	GrMapWindow(statwid);
X	GrMapWindow(quitwid);
X	GrMapWindow(savewid);
X	GrMapWindow(newgamewid);
X
X	if (!playing)
X		newgame();
X
X	while (GR_TRUE)
X		readevent();
X}
X
X
X/*
X * Read the next event and handle it.
X */
Xstatic void
Xreadevent()
X{
X	GR_EVENT	event;
X
X	GrGetNextEvent(&event);
X
X	switch (event.type) {
X		case GR_EVENT_TYPE_BUTTON_DOWN:
X			dobutton(&event.button);
X			break;
X
X		case GR_EVENT_TYPE_EXPOSURE:
X			doexposure(&event.exposure);
X			break;
X
X		case GR_EVENT_TYPE_KEY_DOWN:
X			dokey(&event.keystroke);
X			break;
X	}
X}
X
X
X/*
X * Handle exposure events.
X */
Xstatic void
Xdoexposure(ep)
X	GR_EVENT_EXPOSURE	*ep;
X{
X	if (ep->wid == boardwid) {
X		drawboard();
X		return;
X	}
X
X	if (ep->wid == statwid) {
X		drawstatus();
X		return;
X	}
X
X	if (ep->wid == quitwid) {
X		drawbutton(quitwid, "QUIT");
X		return;
X	}
X
X	if (ep->wid == savewid) {
X		drawbutton(savewid, "SAVE GAME");
X		return;
X	}
X
X	if (ep->wid == newgamewid) {
X		drawbutton(newgamewid, "NEW GAME");
X		return;
X	}
X}
X
X
X/*
X * Here when we get a button down event.
X */
Xstatic void
Xdobutton(bp)
X	GR_EVENT_BUTTON		*bp;
X{
X	if (bp->wid == boardwid) {
X		moveto(findcell(bp->x, bp->y));
X		return;
X	}
X
X	if (bp->wid == quitwid) {
X		GrFillRect(quitwid, xorgc, 0, 0, BUTTONWIDTH, BUTTONHEIGHT);
X		GrFlush();
X		if (savefile)
X			writegame(savefile);
X		GrClose();
X		exit(0);
X	}
X
X	if (bp->wid == savewid) {
X		GrFillRect(savewid, xorgc, 0, 0, BUTTONWIDTH, BUTTONHEIGHT);
X		GrFlush();
X		if (savefile == NULL)
X			savefile = SAVEFILE;
X		if (writegame(savefile))
X			write(1, "\007", 1);
X		else
X			delay();
X		GrFillRect(savewid, xorgc, 0, 0, BUTTONWIDTH, BUTTONHEIGHT);
X	}
X
X	if (bp->wid == newgamewid) {
X		GrFillRect(newgamewid, xorgc, 0, 0, BUTTONWIDTH, BUTTONHEIGHT);
X		GrFlush();
X		if (playing)
X			write(1, "\007", 1);
X		else {
X			newgame();
X			delay();
X		}
X		GrFillRect(newgamewid, xorgc, 0, 0, BUTTONWIDTH, BUTTONHEIGHT);
X	}
X}
X
X
X/*
X * Here when we get a keypress in a window.
X */
Xstatic void
Xdokey(kp)
X	GR_EVENT_KEYSTROKE	*kp;
X{
X	if ((kp->wid != boardwid) || !playing)
X		return;
X
X	switch (kp->ch) {
X		case ' ':			/* remember or forget mine */
X			togglecell(findcell(kp->x, kp->y));
X			break;
X	}
X}
X
X
X/*
X * Redraw the board.
X */
Xstatic void
Xdrawboard()
X{
X	GR_COORD	row;
X	GR_COORD	col;
X
X	for (row = 1; row < size; row++) {
X		GrLine(boardwid, boardgc, 0, row * yp - 1, size * xp - 1,
X			row * yp - 1);
X		GrLine(boardwid, boardgc, row * xp - 1, 0,
X			row * xp - 1, size * yp - 1);
X	}
X	for (row = 0; row < FULLSIZE; row++) {
X		for (col = 0; col < FULLSIZE; col++) {
X			drawcell(boardpos(row, col));
X		}
X	}
X}
X
X
X/*
X * Draw a cell on the board.
X */
Xstatic void
Xdrawcell(pos)
X	POS		pos;		/* position to be drawn */
X{
X	GR_COORD	x;
X	GR_COORD	y;
X	GR_SIZE		chwidth;
X	GR_SIZE		chheight;
X	GR_SIZE		chbase;
X	CELL		cell;
X	GR_CHAR		ch;
X
X	cell = board[pos];
X	if (!isknown(cell))
X		return;
X
X	ch = displaychar(cell);
X	if (ch == F_WRONG) {
X		drawbomb(pos, greengc, GR_FALSE);
X		return;
X	}
X
X	if (isold(cell)) {
X		clearcell(pos);
X		cellcenter(pos, &x, &y);
X		GrGetGCTextSize(boardgc, &ch, 1, &chwidth, &chheight,
X			&chbase);
X		GrText(boardwid, boardgc, x - chwidth / 2 + 1,
X			y + chheight / 2, &ch, 1);
X		return;
X	}
X
X	drawbomb(pos, redgc, GR_FALSE);
X}
X
X
X/*
X * Clear a particular cell.
X */
Xstatic void
Xclearcell(pos)
X	POS		pos;		/* position to be cleared */
X{
X	GR_COORD	row;
X	GR_COORD	col;
X
X	row = pos / FULLSIZE;
X	col = pos % FULLSIZE;
X	GrFillRect(boardwid, cleargc, col * xp - xp, row * yp - yp,
X		xp - 1, yp - 1);
X}
X
X
X/*
X * Draw a bomb in a window using the specified GC.
X * The bomb is animated and the terminal is beeped if necessary.
X */
Xstatic void
Xdrawbomb(pos, gc, animate)
X	POS		pos;		/* position to draw bomb at */
X	GR_GC_ID	gc;		/* GC for drawing (red or green) */
X	GR_BOOL		animate;	/* TRUE to animate the bomb */
X{
X	GR_COORD	x;
X	GR_COORD	y;
X	GR_COUNT	count;
X
X	if (animate)
X		write(1, "\007", 1);
X
X	cellcenter(pos, &x, &y);
X
X	count = (animate ? 8 : 1);
X	for (;;) {
X		GrFillEllipse(boardwid, gc, x, y, xp / 2 - 3, yp / 2 - 3);
X		if (--count == 0)
X			return;
X		delay();
X		clearcell(pos);
X		delay();
X	}
X}
X
X
X/*
X * Draw a button which has a specified label string centered in it.
X */
Xstatic void
Xdrawbutton(window, label)
X	GR_WINDOW_ID	window;
X	char		*label;
X{
X	GR_SIZE		width;
X	GR_SIZE		height;
X	GR_SIZE		base;
X
X	GrGetGCTextSize(buttongc, label, strlen(label), &width, &height, &base);
X	GrText(window, buttongc, (BUTTONWIDTH - width) / 2,
X		(BUTTONHEIGHT - height) / 2 + height - 1,
X		label, strlen(label));
X}
X
X
X/*
X * Set the cursor as appropriate.
X * The cursor changes depending on the number of legs left.
X */
Xstatic void
Xsetcursor()
X{
X	GR_BITMAP	*fgbits;	/* bitmap for foreground */
X	GR_BITMAP	*bgbits;	/* bitmap for background */
X
X	switch (legs) {
X		case 0:
X			fgbits = noleg_fg;
X			bgbits = noleg_bg;
X			break;
X		case 1:
X			fgbits = oneleg_fg;
X			bgbits = oneleg_bg;
X			break;
X		default:
X			fgbits = twolegs_fg;
X			bgbits = twolegs_bg;
X			break;
X	}
X	GrSetCursor(boardwid, 9, 12, 4, 6, si.white, si.black, fgbits, bgbits);
X}
X
X
X/*
X * Delay for a while so that something can be seen.
X * This is done by drawing a large rectangle over the window using a mode
X * of XOR with the value of 0, (which does nothing except waste time).
X */
Xstatic void
Xdelay()
X{
X	GR_COUNT	i;
X
X	for (i = 0; i < 3; i++) {
X		GrFillRect(boardwid, delaygc, 0, 0, xp * size - 1,
X			yp * size - 1);
X		GrFlush();
X	}
X}
X
X
X/*
X * Calculate the coordinates of the center of a cell on the board.
X * The coordinates are relative to the origin of the board window.
X */
Xstatic void
Xcellcenter(pos, retx, rety)
X	POS		pos;		/* position to find center of */
X	GR_COORD	*retx;		/* returned X coordinate */
X	GR_COORD	*rety;		/* returned Y coordinate */
X{
X	*retx = (pos % FULLSIZE) * xp - 1 - xp / 2;
X	*rety = (pos / FULLSIZE) * yp - 1 - yp / 2;
X}
X
X
X/*
X * Draw the status information in the status window.
X */
Xstatic void
Xdrawstatus()
X{
X	long	score;
X	long	allsteps;
X	long	games;
X
X	score = 0;
X	games = games0[index];
X	allsteps = steps0[index];
X	score += games1[index];
X	games += games1[index];
X	allsteps += steps1[index];
X	score += games2[index] * 2;
X	games += games2[index];
X	allsteps += steps2[index];
X
X	printline(0, "Size:   %2d\n", size);
X	printline(1, "Mines: %3d\n", mines);
X	PRINTSTEPS;
X	printline(3, "Legs:    %d\n", legs);
X
X	printline(5, "Won games:  %3d\n", games2[index]);
X	printline(6, "1-leg games:%3d\n", games1[index]);
X	printline(7, "Lost games: %3d\n", games0[index]);
X
X	if (games) {
X		printline(9, "Legs/game: %3d.%03d\n", score / games,
X			((score * 1000) / games) % 1000);
X
X		printline(10, "Steps/game:%3d.%03d\n", allsteps / games,
X			((allsteps * 1000) / games) % 1000);
X	}
X}
X
X
X/*
X * Printf routine for windows, which can print at particular lines.
X * A negative line number means continue printing at the previous location.
X * Assumes the status window for output.
X */
Xstatic void
Xprintline(row, fmt)
X	GR_COORD	row;		/* row to print line on (or -1) */
X	char		*fmt;		/* printf-stype format string */
X{
X	va_list		ap;
X	GR_COUNT	cc;
X	GR_SIZE		width;
X	char		*cp;
X	char		buf[256];
X
X	va_start(ap, fmt);
X	vsprintf(buf, fmt, ap);
X	va_end(ap);
X
X	if (row >= 0) {
X		charxpos = 0;
X		charypos = charheight * row + charheight - 1;
X	}
X
X	cp = buf;
X	for (;;) {
X		cc = 0;
X		width = 0;
X		while (*cp >= ' ') {
X			width += fi.widths[*cp++];
X			cc++;
X		}
X		if (width) {
X			GrText(statwid, statgc, charxpos, charypos,
X				cp - cc, cc);
X			charxpos += width;
X		}
X
X		switch (*cp++) {
X			case '\0':
X				return;
X			case '\n':
X				newline();
X				break;
X			case '\r':
X				charxpos = 0;
X				break;
X		}
X	}
X}
X
X
X/*
X * Clear the remainder of the line and move to the next line.
X * This assumes output is in the status window.
X */
Xstatic void
Xnewline()
X{
X	GrFillRect(statwid, blackgc, charxpos, charypos - charheight + 1,
X		statwidth - charxpos, charheight);
X	charxpos = 0;
X	charypos += charheight;
X}
X
X
X/*
X * Translate a board window coordinate into a cell position.
X * If the coordinate is outside of the window, or exactly on one
X * of the interior lines, then a coordinate of 0 is returned.
X */
Xstatic POS
Xfindcell(x, y)
X	GR_COORD	x;
X	GR_COORD	y;
X{
X	GR_COORD	row;
X	GR_COORD	col;
X
X	if (((x % xp) == 0) || ((y % yp) == 0))
X		return 0;
X	row = (y / yp) + 1;
X	col = (x / xp) + 1;
X	if ((row <= 0) || (row > size) || (col <= 0) || (col > size))
X		return 0;
X	return boardpos(row, col);
X}
X
X
X/*
X * Initialize the board for playing
X */
Xstatic void
Xnewgame()
X{
X	GR_COORD	row;
X	GR_COORD	col;
X	GR_COUNT	count;
X	CELL		cell;
X	POS		pos;
X
X	for (row = 0; row < FULLSIZE; row++) {
X		for (col = 0; col < FULLSIZE; col++) {
X			cell = F_EMPTY;
X			if (badsquare(row) || badsquare(col))
X				cell |= F_EDGE;
X			board[boardpos(row, col)] = cell;
X		}
X	}
X
X	playing = GR_TRUE;
X	count = 0;
X	legs = 2;
X	steps = 0;
X	drawstatus();
X	setcursor();
X
X	while (count < mines) {
X		do {
X			row = (rand() / 16) % (size * size + 1);
X		} while (row == (size * size));
X
X		col = (row % size) + 1;
X		row = (row / size) + 1;
X		pos = boardpos(row, col);
X
X		if ((pos == boardpos(1,1)) || (pos == boardpos(1,2)) ||
X			(pos == boardpos(2,1)) || (pos == boardpos(2,2)) ||
X			(pos == boardpos(size,size)))
X				continue;
X
X		if (!ismine(board[pos]) && checkpath(pos))
X			count++;
X	}
X
X	board[boardpos(1,1)] = (F_OLD | '0');
X
X	GrClearWindow(boardwid, GR_TRUE);
X}
X
X
X/*
X * Check to see if there is still a path from the top left corner to the
X * bottom right corner, if a new mine is placed at the indicated position.
X * Returns GR_TRUE if mine was successfully placed.
X */
Xstatic GR_BOOL
Xcheckpath(pos)
X	POS		pos;		/* position to place mine at */
X{
X	CELL		*bp;		/* current board position */
X	CELL		*endbp;		/* ending position */
X	POS		endpos;		/* ending position */
X	GR_COUNT	count;		/* number of neighbors */
X	GR_COUNT	i;		/* loop counter */
X	GR_BOOL		more;		/* GR_TRUE if new square reached */
X
X	/*
X	 * Begin by assuming there is a mine at the specified location,
X	 * and then count neighbors.  If there are less than two other
X	 * mines or edge squares, then there must still be a path.
X	 */
X	board[pos] |= F_MINE;
X
X	count = 0;
X
X	for (i = 7; i >= 0; i--) {
X		if (board[pos + steptable[i]] & (F_MINE | F_EDGE))
X		count++;
X	}
X
X	if (count < 2)
X		return GR_TRUE;
X
X	/*
X	 * Two or more neighbors, so we must do the full check.
X	 * First clear the reach flag, except for the top left corner.
X	 */
X	endpos = boardpos(size, size);
X	bp = &board[endpos];
X	endbp = bp;
X	while (bp != board)
X		*bp-- &= ~F_REACH;
X	board[boardpos(1,1)] |= F_REACH;
X
X	/*
X	 * Now loop looking for new squares next to already reached squares.
X	 * Stop when no more changes are found, or when the lower right
X	 * corner is reached.
X	 */
X	do {
X		more = GR_FALSE;
X		for (bp = &board[boardpos(1,1)]; bp != endbp; bp++) {
X			if (*bp & F_REACH) {
X				for (i = 7; i >= 0; i--) {
X					if ((bp[steptable[i]] & (F_MINE | F_REACH | F_EDGE)) == 0) {
X						bp[steptable[i]] |= F_REACH;
X						more = GR_TRUE;
X					}
X				}
X			}
X		}
X
X		if (board[endpos] & F_REACH)
X			return GR_TRUE;
X	} while (more);
X
X	/*
X	 * Cannot reach the lower right corner, so remove the mine and fail.
X	 */
X	board[pos] &= ~F_MINE;
X
X	return GR_FALSE;
X}
X
X
X/*
X * Move to a particular position and see if we hit a mine.
X * If not, then count the number of mines adjacent to us so it can be seen.
X * If we are stepping onto a location where we remembered a mine is at,
X * then don't do it.  Moving is only allowed to old locations, or to
X * locations adjacent to old ones.
X */
Xstatic void
Xmoveto(newpos)
X	POS		newpos;		/* position to move to */
X{
X	POS		fixpos;		/* position to fix up */
X	CELL		cell;		/* current cell */
X	GR_COUNT	count;		/* count of cells */
X	GR_COUNT	i;		/* index for neighbors */
X
X	if ((newpos < 0) || (newpos >= (FULLSIZE * FULLSIZE)) || !playing)
X		return;
X
X	cell = board[newpos];
X
X	if (isedge(cell) || (isseen(cell)) || isold(cell))
X		return;
X
X	count = isold(cell);
X	for (i = 0; i < 8; i++)
X		if (isold(board[newpos + steptable[i]]))
X			count++;
X
X	if (count <= 0)
X		return;
X
X	cell = (cell & F_FLAGS) | F_OLD;
X	steps++;
X
X	PRINTSTEPS;
X
X	if (ismine(cell)) {		/* we hit a mine */
X		legs--;
X		board[newpos] = (F_REMEMBER | F_MINE);
X		cell = (F_EMPTY | F_OLD);
X		board[newpos] = cell;
X		drawbomb(newpos, redgc, GR_TRUE);
X		clearcell(newpos);
X		setcursor();
X		for (i = 0; i < 8; i++) {
X			fixpos = newpos + steptable[i];
X			if (isold(board[fixpos])) {
X				board[fixpos]--;
X				drawcell(fixpos);
X			}
X		}
X		drawstatus();
X	}
X
X	count = 0;
X	for (i = 0; i < 8; i++)
X		if (ismine(board[newpos + steptable[i]]))
X			count++;
X	board[newpos] = cell | (count + '0');
X
X	drawcell(newpos);
X
X	if ((legs <= 0) || (newpos == boardpos(size,size)))
X		gameover();
X}
X
X
X/*
X * Remember or forget the location of a mine.
X * This is for informational purposes only and does not affect anything.
X */
Xstatic void
Xtogglecell(pos)
X	POS	pos;		/* position to toggle */
X{
X	CELL	cell;
X
X	if ((pos <= 0) || !playing)
X		return;
X
X	cell = board[pos];
X	if (isknown(cell)) {
X		if (!isseen(cell))
X			return;
X		board[pos] = (board[pos] & F_FLAGS) | F_EMPTY;
X		clearcell(pos);
X		return;
X	}
X
X	board[pos] = (board[pos] & F_FLAGS) | F_REMEMBER;
X	drawcell(pos);
X}
X
X
X/*
X * Here when the game is over.
X * Show where the mines are, and give the results.
X */
Xstatic void
Xgameover()
X{
X	POS	pos;
X	CELL	cell;
X
X	playing = GR_FALSE;
X	switch (legs) {
X		case 0:
X			games0[index]++;
X			steps0[index] += steps;
X			break;
X		case 1:
X			games1[index]++;
X			steps1[index] += steps;
X			break;
X		case 2:
X			games2[index]++;
X			steps2[index] += steps;
X			break;
X	}
X
X	for (pos = 0; pos < (FULLSIZE * FULLSIZE); pos++) {
X		cell = board[pos];
X		if (isseen(cell))
X			cell = (cell & F_FLAGS) | F_WRONG;
X		if (ismine(cell))
X			cell = (cell & F_FLAGS) | F_REMEMBER;
X		board[pos] = cell;
X	}
X
X	drawboard();
X	drawstatus();
X}
X
X
X/*
X * Search the game parameter table for the current board size and
X * number of mines, and set the index for those parameters so that
X * the statistics can be accessed.  Allocates a new index if necessary.
X */
Xstatic void
Xfindindex()
X{
X	for (index = 0; index < MAXPARAMS; index++) {
X		if ((sizeparam[index] == size) && (mineparam[index] == mines))
X			return;
X	}
X	for (index = 0; index < MAXPARAMS; index++) {
X		if (sizeparam[index] == 0) {
X			sizeparam[index] = size;
X			mineparam[index] = mines;
X			return;
X		}
X	}
X	fprintf(stderr, "Too many parameters in save file\n");
X	exit(1);
X}
X
X
X/*
X * Read in a saved game if available, otherwise start from scratch.
X * Exits if an error is encountered.
X */
Xstatic void
Xreadgame(name)
X	char	*name;		/* filename */
X{
X	int	fd;
X
X	fd = -1;
X	if (name)
X		fd = open(name, 0);
X
X	if (fd < 0) {
X		magic = MAGIC;
X		size = SIZE;
X		mines = (size * size * MINEPERCENT) / 100;
X		playing = GR_FALSE;
X		return;
X	}
X
X	if (read(fd, &st, sizeof(st)) != sizeof(st))
X		magic = 0;
X	close(fd);
X
X	if ((magic != MAGIC) || (size > MAXSIZE)) {
X		fprintf(stderr, "Save file format is incorrect\n");
X		exit(1);
X	}
X}
X
X
X/*
X * Write the current game to a file.
X * Returns nonzero on an error.
X */
Xstatic GR_BOOL
Xwritegame(name)
X	char	*name;		/* filename */
X{
X	int	fd;
X
X	if (name == NULL)
X		return GR_TRUE;
X
X	fd = creat(name, 0666);
X	if (fd < 0)
X		return GR_TRUE;
X
X	if (write(fd, &st, sizeof(st)) != sizeof(st)) {
X		close(fd);
X		return GR_TRUE;
X	}
X	close(fd);
X	return GR_FALSE;
X}
X
X/* END CODE */
END_OF_FILE
if test 28343 -ne `wc -c <'landmine.c'`; then
    echo shar: \"'landmine.c'\" unpacked with wrong size!
fi
# end of 'landmine.c'
fi
if test -f 'landmine.doc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'landmine.doc'\"
else
echo shar: Extracting \"'landmine.doc'\" \(5909 characters\)
sed "s/^X//" >'landmine.doc' <<'END_OF_FILE'
X			LANDMINE
X
X
XLandmine is a game in which you try to safely move step by step from the
Xtop left corner of a square board to the bottom right corner of the board.
XScattered among the cells of the board are invisible mines which blow up
Xif you step on them.  When you step on a mine, one of your legs is blown
Xoff, and if you lose both legs, then the game is over.
X
XSteps are made one at a time from any cell you have already visited to
Xany of the eight possible cells which are adjacent to it.  At the start
Xof the game you are placed in the top left corner of the board, which is
Xguaranteed to have no mine in it.  There are also no mines immediately
Xadjacent to the starting cell.  There is also a guaranteed path from the
Xstarting cell to the finishing cell which does not encounter any mines.
X
XYou cannot see the mines, however you have a detector which indicates
Xthe presence of the mines.  The detector cannot locate the direction of
Xany individual mine, but it does indicate the number of mines immediately
Xadjacent to your cell.  By using this information from all the cells you
Xhave already visited, you can usually deduce where the mines must be.
XIn this way, you can find your way safely to the destination cell.  Some
Xof the deductions that can be made are very subtle, and experience will
Ximprove your scores dramatically.
X
XWhen landmine is started up under mini-X, the board is displayed at the
Xleft side, some buttons are displayed at the upper right, and some statistics
Xare displayed at the lower right.  To make a step, use the mouse to move the
Xcursor onto the cell that you wish to step on, and press a button.  A number
Xwill appear in that square (if there is no mine there!) indicating now many
Xmines are adjacent to that square.  You can step onto any cell which is
Xadjacent to any cell you have already stepped on.
X
XIn order to help you deduce where the mines are and what steps are safe,
Xyou can mark any non-visited cell as containing a mine.  To do this, move
Xthe mouse to that cell, and then type a space.  A red circle will then
Xappear in that cell to indicate that you think it contains a mine.  A
Xside effect of doing this is that you cannot accidentally step on the
Xsquare while it is marked.  Notice that the cell is marked as you request
Xwhether or not a mine is actually there, thus if you mistakenly mark a
Xcell, you are likely to get confused and later step on a real mine!  If
Xyou think that a cell is marked in error, you can clear the marking by
Xmoving the mouse to the cell and then typing another space.
X
XIf you step on a mine, you will hear a beep, and a red circle will flash
Xon and off for a few seconds to indicate that the mine has exploded.
XThen the mine will be removed, and the counts in the adjacent cells
Xwill be adjusted to reflect the new situation with one less mine.  Your
Xnumber of legs is also reduced by 1, and the cursor shape is changed to
Xindicate this.  If you lose both legs, then the game is lost.  If you
Xsuccessfully make it to the lower right corner, then the game is won.
X
XWhen the game is over, the location of all mines are marked with red circles.
XIn addition, if you had marked cells as containing mines, but the cells were
Xwrongly marked, then those cells are shown with green circles.  After you
Xhave analyzed the results of the game, you can start a new game by using
Xthe NEW GAME button.
X
XThe three buttons on the top right of the screen are QUIT, SAVE GAME,
Xand NEW GAME.  The buttons are activated by moving the cursor to the
Xinterior of the button, and pressing any mouse button.  No confirmation
Xis asked for these actions.
X
XQUIT will immediately quit playing and return you to text mode and exit.
XHowever, if you had started landmine with a filename to restore from,
Xthen the current status will be saved back to that file.  If you had not
Xsupplied a filename on starting, and have not saved the game using the
XSAVE GAME button, then QUIT will quit without saving anything.
X
XSAVE GAME is used to save the current status of the game without exiting.
XThis lets you make sure that the game is saved away when you have been
Xplaying for a long time and are worried about crashes.  If you had given
Xa file for restoring of previous games, then the game will be saved back
Xinto that file.  If you had not specified a filename, then a default name
Xof "landmine.save" will be used.  If the save operation works, the button
Xwill momentarily flash.  If the save operation fails, then a beep will
Xbe sounded.
X
XNEW GAME is used to start a new game.  This button can only be used after
Xa game has just been completed.  That is, after both legs have been blown
Xoff or you have reached the destination cell, you use this button to
Xbegin another game.
X
XWhen starting landmine, you can specify some options on the command line.
XThe options are the following:
X
X	landmine [-s n] [-m n] [savefile]
X
XThe savefile is the filename to save the game into when the SAVE GAME or
XQUIT buttons are used, as described above.
X
XThe -s option sets the size of the board.  The size is the number of cells
Xacross the board, and also down the board.  The board size can be set to
Xany size from 3 to 30.  The default size is 15.
X
XThe -m option sets the number of mines on the board.  This can be set from
X1 to 50% of the number of cells on the board.  The default number of mines
Xis 15% of the number of cells on the board.  For the default board size,
Xthe default number of mines is 33.
X
XThe statistics saved in the save file are kept for each combination of
Xboard size and mines, thus you can play with many different combinations
Xof values and the statistics will be kept separately.  There is a limit
Xof 1000 different combinations that can be saved.
X
XYou cannot change the board size or number of mines while a game is in
Xprogress.  To change the parameters, finish the game, save the game into a
Xfile and exit, then restart landmine giving the new parameters.
END_OF_FILE
if test 5909 -ne `wc -c <'landmine.doc'`; then
    echo shar: \"'landmine.doc'\" unpacked with wrong size!
fi
# end of 'landmine.doc'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0