/* This program appeared in an article written by Alex Lane in the April
 * 1990 issue of Dr. Dobb's Journal (pg 72).  It is used here with
 * permission of the publisher, M&T Publishing Inc.
 */

/****************************************************************************
File:    Kaboooom.c
Purpose: Allows the user to 'walk' through a minefield; a detector shows
  how many mines are immediately adjacent to you. As you visit a cell, it
  leaves a marker telling you how many were next to you, and you have the
  ability to mark cells with a character (assumably to mark mines).
 Changes:
 11/10/89 (tdeii) If you call the program with "/s" or "/S", it gives
  you a "safer" game, where it does not let you walk on spaces that you
  have marked (whether there is a mine there or not!)
  11/11/89 (tdeii) Allows you to press "?" and get some help starting at your
  current position; will mark mines that it knows (by deducing their position),
  and "visit" places that it knows are safe. This propagates until it cannot
  deduce anything else (see EvaluatePosition).
  10/90 (pharlap) Added switchs to allow number of mines (/n) and random seed
   to (/r) be set on command line.
****************************************************************************/
#include "stdio.h"
#include "stdarg.h"
#include "stdlib.h"
#include "dos.h"
#include "conio.h"
#include "string.h"
#include "kaboom1.h"

#define SCREEN_X 80
#define SCREEN_Y 25
#define GRID_X 15
#define GRID_Y  9
#define TRUE 1
#define FALSE 0

#define bEMPTY    0
#define bVISITED  1
#define bBOMB     2
#define bCURRENT  3
#define bFINISH   4
#define bEXPLODED 5

#define MAKECOLOR(fore,back) ((back)*16+(fore))

typedef int BOOL;
typedef struct tagADJACENCYGROUP {
	int BombCount;         /* Number of bombs located in this adj. group */
	int CellCount;         /* Number of cells filled                     */
	int Cell[8][2];        /* x,y coordinates of up to 8 cells           */
} ADJACENCYGROUP;

int  Board[GRID_X][GRID_Y];           /* Board; see codes above (bXXX)    */
int  UserMark[GRID_X][GRID_Y];        /* User marks; 0 = none, 'M' = mine */
int  nNumMines;                       /* Number of mines on board         */
int  initmines=0;		      /* nMines passed with /n switch     */
int  initrand=0;		      /* init rand seed from command line */
int  UserX, UserY;                    /* Current user X and Y position    */
BOOL bShowBombs;                      /* TRUE if program shows bombs (it  */
/* does this after you win or lose) */
BOOL bSafeGame;                       /* TRUE if program does not let you */
/* walk on mines you have marked    */
ADJACENCYGROUP AdjacencyGroup[GRID_X][GRID_Y]; /* AG for each board pos   */
char szClear[79] = "                                                                              ";
int CountMines(int x, int y)
{
	int i, j;
	int nCount;
	nCount = 0;
	for (i=-1; i<=1; i++) {
		for (j=-1; j<=1; j++) {
			if ((x+i >= 0) && (x+i < GRID_X) &&
			    (y+j >= 0) && (y+j < GRID_Y)) {
				if ((Board[x+i][y+j] == bBOMB) ||
				    (Board[x+i][y+j] == bEXPLODED)) {
					nCount++;
				}
			}
		}
	}
	return (nCount);
}
void DisplayCell(int x, int y)
{
	int Char;
	Char = UserMark[x][y];
	if (Char == 0) {
		Char = 32;
	}
	DisplayChar(x*4+1, y*2+1, Char, MAKECOLOR(14,1));
	DisplayChar(x*4+3, y*2+1, Char, MAKECOLOR(14,1));
	switch (Board[x][y]) {
	case bEMPTY:                                       /** Empty cell   **/
		Char = ' ';
		break;
	case bVISITED:                                     /** Visited cell **/
		Char = '0' + CountMines(x, y);
		break;
	case bBOMB:                                        /** Bomb cell!   **/
		if (bShowBombs) {
			Char = 15;
		} else {
			Char =' ';
		}
		break;
	case bCURRENT:                                     /** Current pos  **/
		Char = 2;
		break;
	case bFINISH:                                      /** Finish cell  **/
		Char = 19;
		break;
	case bEXPLODED:                                    /** Exploded!    **/
		Char = 15;
		break;
	}
	if (Char != 0) {
		DisplayChar(x*4+2, y*2+1, Char, MAKECOLOR(14,1));
	}
}

void PaintBoard(void)
{
	int x, y, i;
	for (x=0; x<SCREEN_X; x++) {
		for (y=0; y<SCREEN_Y; y++) {
			DisplayChar(x, y, ' ', MAKECOLOR(14, 1));
		}
	}
	/** Draw left and right sides **/
	DisplayChar(0, 0, 218, MAKECOLOR(14,1));  /*upper left corner */
	DisplayChar(GRID_X*4, 0, 191, MAKECOLOR(14,1)); /*upper right corner */
	for (y=1; y<=GRID_Y; y++) {
		DisplayChar(0, y*2, 195, MAKECOLOR(14,1));  /* left edge */
		DisplayChar(GRID_X*4, y*2, 180, MAKECOLOR(14,1)); /* right edge */
	}
	DisplayChar(0, GRID_Y*2, 192, MAKECOLOR(14,1));  /* lower left corner */
	DisplayChar(GRID_X*4, GRID_Y*2, 217, MAKECOLOR(14,1)); /*lower right */
	/** Draw inside corners **/
	for (x=1; x<GRID_X; x++) {
		DisplayChar(x*4, 0, 194, MAKECOLOR(14,1)); /* top edge */
		for (y=1; y<GRID_Y; y++) {
			DisplayChar(x*4, y*2, 197, MAKECOLOR(14,1)); /* intersections */
		}
		DisplayChar(x*4, GRID_Y*2, 193, MAKECOLOR(14,1)); /* bottom edge */
	}
	/** Draw connecting lines **/
	for (x=0; x<=GRID_X; x++) {
		for (y=0; y<=GRID_Y; y++) {
			if (y != GRID_Y) {
				DisplayChar(x*4, y*2+1, 179, MAKECOLOR(14,1)); /* verticals  */
			}
			if (x != GRID_X) {
				for (i=1; i<4; i++) {
					DisplayChar(x*4+i, y*2, 196 , MAKECOLOR(14,1)); /* horizontals */
				}
			}
		}
	}
	GotoXY_(0, SCREEN_Y - 2);
	for (x=0; x<GRID_X; x++) {
		for (y=0; y<GRID_Y; y++) {
			DisplayCell(x, y);
		}
	}
}
void SetUpBoard(void)
{
	int i, j;
	int nMines;
	BOOL bDone;
	char cBuffer[80];
	bShowBombs = FALSE;
	/** First, get number of bombs **/
	if(!initmines) {
		nNumMines = 0;
		while ((nNumMines < 10) || (nNumMines > 40)) {
			GotoXY_(0, 24);
			_cprintf("How many bombs do you want? (10-40)?? ");
			fgets(cBuffer, sizeof(cBuffer), stdin);
			sscanf(cBuffer, "%d", &nNumMines);
		}
	} else
		nNumMines = initmines;

	/** next, clear out board & user scratchpad **/
	for (i=0; i<GRID_X; i++) {
		for (j=0; j<GRID_Y; j++) {
			Board[i][j] = 0;
			UserMark[i][j] = 0;
		}
	}
	for (nMines=0; nMines<nNumMines; nMines++) {
		bDone = FALSE;
		while (!bDone) {
			i = nRandom(GRID_X);         /* First you roll it, */
			j = nRandom(GRID_Y);         /* Then you pat it,   */
			if ((Board[i][j] == bEMPTY) &&
			    (!((i <= 1) && (j <= 1))) &&
			    (!((i >= GRID_X - 2) && (j >= GRID_Y - 2)))
			    ) {
				bDone = TRUE;
			}
		}
		Board[i][j] = bBOMB;           /* Then you mark it with a 'B' */
	}
	/* Set user at position 0, 0 */
	UserX = 0;
	UserY = 0;
	Board[0][0] = bCURRENT;
	/* Set finish (hq) at position GRID_X, GRID_Y */
	Board[GRID_X - 1][GRID_Y - 1] = bFINISH;
	/* Display board on screen */
	PaintBoard();
}
BOOL Travel(int dx, int dy)
{
	int NewX, NewY;            /* New X and Y coordinates of user       */
	BOOL bInvalid;             /* TRUE if trying to walk off board      */
	BOOL bAbort;               /* TRUE if user won or lost (abort game) */
	BOOL bBombWalk;            /* TRUE if user tried to walk on a bomb  */

	bAbort = FALSE;
	NewX = UserX + dx;
	NewY = UserY + dy;
	bInvalid = FALSE;
	bBombWalk = FALSE;
	if ((NewX < 0) || (NewX >= GRID_X)) {
		bInvalid = TRUE;
	}
	if ((NewY < 0) || (NewY >= GRID_Y)) {
		bInvalid = TRUE;
	}
	if ((!bInvalid) && (bSafeGame) && (UserMark[NewX][NewY] == 'M')) {
		bInvalid = TRUE;
		bBombWalk = TRUE;
	}
	if (bInvalid) {
		GotoXY_(0, SCREEN_Y - 2);
		_cprintf("** INVALID MOVE ** ... press any key...");
		if (bBombWalk) {
			_cprintf("(You must un-mark it.)");
		}
		Pause();
		GotoXY_(0, SCREEN_Y - 2);
		_cprintf(szClear);
	} else {
		if (Board[NewX][NewY] == bBOMB) {
			bAbort = TRUE;
			Board[UserX][UserY] = bVISITED;
			DisplayCell(UserX, UserY);
			Board[NewX][NewY] = bEXPLODED;
			DisplayCell(NewX, NewY);
			GotoXY_(0, 22);
			_cprintf("******** YOU HAVE STEPPED ON A BOMB!! ********");
			Pause();
			GotoXY_(0, 22);
			_cprintf(szClear);
			GotoXY_(0, 22);
		} else {
			if ((NewX == GRID_X-1) && (NewY == GRID_Y-1)) {
				bAbort = TRUE;
				Board[UserX][UserY] = bVISITED;
				DisplayCell(UserX, UserY);
				Board[NewX][NewY] = bCURRENT;
				DisplayCell(NewX, NewY);
				GotoXY_(0, 22);
				_cprintf("************* YOU HAVE WON!! *************");
				Pause();
				GotoXY_(0, 22);
				_cprintf(szClear);
				GotoXY_(0, 22);
			} else {
				Board[UserX][UserY] = bVISITED;
				DisplayCell(UserX, UserY);
				UserX = NewX;
				UserY = NewY;
				Board[UserX][UserY] = bCURRENT;
				DisplayCell(UserX, UserY);
			}
		}
	}
	GotoXY_(0, GRID_Y*2+2);
	_cprintf("Number of mines around you: %d", CountMines(UserX, UserY));
	GotoXY_(0, SCREEN_Y - 2);
	return (bAbort);
}
void PlaceUserMark(void)
{
	BOOL bDone, bAbort;
	int  Ch;
	int  NewX, NewY;
	int  dx, dy;

	bAbort = FALSE;
	GotoXY_(0, 24);
	_cprintf("Mark in which direction? (ESC=abort)");
	bDone = FALSE;
	while (!bDone) {
		bDone = TRUE;
		Ch = _getch();
		switch (Ch) {
		case 0:
			Ch = _getch();
			switch (Ch) {
			case 71:  /* home */
				dx = -1;
				dy = -1;
				break;
			case 72:  /* up arrow */
				dx = 0;
				dy = -1;
				break;
			case 73:  /* page up */
				dx = 1;
				dy = -1;
				break;
			case 75:  /* left arrow */
				dx = -1;
				dy = 0;
				break;
			case 77:  /* right arrow */
				dx = 1;
				dy = 0;
				break;
			case 79:  /* end */
				dx = -1;
				dy = 1;
				break;
			case 80:  /* down arrow */
				dx = 0;
				dy = 1;
				break;
			case 81:  /* page down */
				dx = 1;
				dy = 1;
				break;
			default:
				bDone = FALSE;
				break;
			}
			break;
		case '7':  /* home */
			dx = -1;
			dy = -1;
			break;
		case '8':  /* up arrow */
			dx = 0;
			dy = -1;
			break;
		case '9':  /* page up */
			dx = 1;
			dy = -1;
			break;
		case '4':  /* left arrow */
			dx = -1;
			dy = 0;
			break;
		case '6':  /* right arrow */
			dx = 1;
			dy = 0;
			break;
		case '1':  /* end */
			dx = -1;
			dy = 1;
			break;
		case '2':  /* down arrow */
			dx = 0;
			dy = 1;
			break;
		case '3':  /* page down */
			dx = 1;
			dy = 1;
			break;
		case 27:
		case 13:
		case 10:
		case  8:
			bAbort = TRUE;
			break;
		default:
			bDone = FALSE;
			break;
		}
	}
	GotoXY_(0, 24);
	_cprintf(szClear);
	if (!bAbort) {
		NewX = UserX + dx;
		NewY = UserY + dy;
		if ((NewX < 0) || (NewX >= GRID_X) || (NewY < 0) || (NewY >= GRID_Y)) {
			GotoXY_(0, 24);
			_cprintf("ERROR: Out of bounds!!");
			Pause();
			GotoXY_(0, 24);
			_cprintf(szClear);
		} else {
			GotoXY_(0, 24);
			if (UserMark[NewX][NewY] != 0) {
				Ch = 0;
			} else {
				Ch = 'M';
			}
			UserMark[NewX][NewY] = Ch;
			DisplayCell(NewX, NewY);
		}
	}
	GotoXY_(0, 24);
}
void ComputeAdjacency(int x, int y)
{
	int dX, dY;
	int BombCount;
	int Cell;
	if ((x >= 0) && (x < GRID_X) && (y >= 0) && (y < GRID_Y)) {
		if ((Board[x][y] == bVISITED) || (Board[x][y] == bCURRENT)) {
			BombCount = CountMines(x, y);
			Cell = 0;
			for (dX=-1; dX<=1; dX++) {
				for (dY=-1; dY<=1; dY++) {
					if (!((dX == 0) && (dY == 0))) {
						if ((x+dX >= 0) && (x+dX < GRID_X) &&
						    (y+dY >= 0) && (y+dY < GRID_Y)) {
							if ((Board[x+dX][y+dY] != bVISITED) &&
							    (Board[x+dX][y+dY] != bCURRENT)) {
								if (UserMark[x+dX][y+dY] != 0) {
/* code folded from here */
	BombCount--;
/* unfolding */
								} else {
/* code folded from here */
	AdjacencyGroup[x][y].Cell[Cell][0] = x+dX;
	AdjacencyGroup[x][y].Cell[Cell][1] = y+dY;
	Cell++;
/* unfolding */
								}
							}
						}
					}
				}
			}
			AdjacencyGroup[x][y].BombCount = BombCount;
 			AdjacencyGroup[x][y].CellCount = Cell;
		} else {
			AdjacencyGroup[x][y].CellCount = 0;
			AdjacencyGroup[x][y].BombCount = -1;   /** Don't look flag */
		}
	}
}
int AddToPositionList(int PositionList[GRID_X * GRID_Y][2],
int PositionListHead, int x, int y)
{
	int  nIndex;
	BOOL bFound;
	ComputeAdjacency(x, y);
	bFound = FALSE;
	for (nIndex=0; (nIndex<PositionListHead) && (!bFound); nIndex++) {
		if ((PositionList[nIndex][0] == x) && (PositionList[nIndex][1] == y)) {
			bFound = TRUE;
		}
	}
	if (!bFound) {
		PositionList[PositionListHead][0] = x;
		PositionList[PositionListHead][1] = y;
		PositionListHead++;
	}
	if (PositionListHead > GRID_X * GRID_Y) {
		GotoXY_(0, 22);
		_cprintf("ERROR! PositionListHead > max (%d)", PositionListHead);
		Pause();
		GotoXY_(0, 22);
		_cprintf(szClear);
		GotoXY_(0, 22);
	}
	return (PositionListHead);
}
int AddSurroundingToPositionList(int PositionList[GRID_X * GRID_Y][2],
int PositionListHead, int x, int y)
{
	int dX, dY;

	for (dX=-1; dX<=1; dX++) {
		for (dY=-1; dY<=1; dY++) {
			if ((x+dX >= 0) && (x+dX < GRID_X) && (y+dY >= 0) && (y+dY < GRID_Y)) {
				if ((Board[x+dX][y+dY] == bVISITED) ||
				    (Board[x+dX][y+dY] == bCURRENT)) {
					PositionListHead = AddToPositionList(PositionList, PositionListHead,
					    x+dX, y+dY);
				}
			}
		}
	}
	return (PositionListHead);
}
BOOL FindPositionInAG(ADJACENCYGROUP *pAG, int x, int y)
{
	int  nIndex;
	BOOL bFound;
	bFound = FALSE;
	for (nIndex=0; nIndex<pAG->CellCount; nIndex++) {
		if ((pAG->Cell[nIndex][0] == x) && (pAG->Cell[nIndex][1] == y)) {
			bFound = TRUE;
		}
	}
	return (bFound);
}
void MarkBombCell(int x, int y)
{
	UserMark[x][y] = 'M';
	DisplayCell(x, y);
	if (Board[x][y] != bBOMB) {
		GotoXY_(0, 22);
		_cprintf("LOGIC ERROR: I tagged a phantom bomb @ (%d,%d).", x, y);
		Pause();
		GotoXY_(0, 22);
		_cprintf(szClear);
		GotoXY_(0, 24);
	}
}
void VisitCell(int x, int y)
{
	if (Board[x][y] != bCURRENT) {
		if (Board[x][y] == bBOMB) {
			GotoXY_(0, 22);
			_cprintf("LOGIC ERROR: I walked on a bomb @ (%d,%d).", x, y);
			Pause();
			GotoXY_(0, 22);
			_cprintf(szClear);
			GotoXY_(0, 24);
		}
		Board[x][y] = bVISITED;
		DisplayCell(x, y);
	}
}
int CountCommonCells(ADJACENCYGROUP *pGroup1, ADJACENCYGROUP *pGroup2)
{
	int Cell, nCount;
	nCount = 0;
	for (Cell=0; Cell<pGroup1->CellCount; Cell++) {
		if (FindPositionInAG(pGroup2,
		    pGroup1->Cell[Cell][0], pGroup1->Cell[Cell][1])) {
			nCount++;
		}
	}
	return (nCount);
}
BOOL ProcessRule3(ADJACENCYGROUP *pCurrentAG, ADJACENCYGROUP *pTempAG,
int PositionList[GRID_X * GRID_Y][2],
int *pPositionListHead)
{
	int x;
	int BombCount, CellCount;
	int PositionListHead;
	int CellHolder[9][2];
	int CellHolderHead;
	BOOL bRetVal;
	PositionListHead = *pPositionListHead;
	bRetVal = FALSE;
	BombCount = pCurrentAG->BombCount;
	CellCount = pCurrentAG->CellCount;
	if (pTempAG->CellCount == CountCommonCells(pTempAG, pCurrentAG)) {
		BombCount -= pTempAG->BombCount;
		CellCount -= pTempAG->CellCount;
		if ((CellCount > 0) && ((BombCount == CellCount) || (BombCount == 0))) {
			bRetVal = TRUE;
			CellHolderHead = 0;
			CellCount = pCurrentAG->CellCount;
			for (x=0; x<CellCount; x++) {
				if (!FindPositionInAG(pTempAG, pCurrentAG->Cell[x][0],
				    pCurrentAG->Cell[x][1])) {
					if (BombCount == 0) {
						VisitCell(pCurrentAG->Cell[x][0], pCurrentAG->Cell[x][1]);
					} else {
						MarkBombCell(pCurrentAG->Cell[x][0], pCurrentAG->Cell[x][1]);
					}
					/* Queue up cells to put in position list for later */
					CellHolder[CellHolderHead][0] = pCurrentAG->Cell[x][0];
					CellHolder[CellHolderHead][1] = pCurrentAG->Cell[x][1];
					CellHolderHead++;
				}
			}
			for (x=0; x<CellHolderHead; x++) {
				PositionListHead = AddSurroundingToPositionList(
				    PositionList,
				    PositionListHead,
				    CellHolder[x][0],
				    CellHolder[x][1]);
			}
		}
	}
	*pPositionListHead = PositionListHead;
	return (bRetVal);
}
void EvaluatePosition(void)
{
	int  CurrentX, CurrentY;
	int  x, y;
	int  Cell;
	int  dX, dY;
	int  BombCount, CellCount;
	int  PositionList[GRID_X * GRID_Y][2], PositionListHead;
	ADJACENCYGROUP *pTempAG;
	BOOL bDone;
	BOOL bModifiedAny;
	bModifiedAny = TRUE;
	for (x=0; x<GRID_X; x++) {
		for (y=0; y<GRID_Y; y++) {
			ComputeAdjacency(x, y);
		}
	}
	PositionList[0][0] = UserX;
	PositionList[0][1] = UserY;
	PositionListHead = 1;
	while (bModifiedAny) {
		bModifiedAny = FALSE;
		while (PositionListHead > 0) {
			CurrentX = PositionList[0][0];
			CurrentY = PositionList[0][1];
			for (x=0; x<PositionListHead-1; x++) {
				PositionList[x][0] = PositionList[x+1][0];
				PositionList[x][1] = PositionList[x+1][1];
			}
			PositionListHead--;
			ComputeAdjacency(CurrentX, CurrentY);
			BombCount = AdjacencyGroup[CurrentX][CurrentY].BombCount;
			CellCount = AdjacencyGroup[CurrentX][CurrentY].CellCount;
			if ((CellCount > 0) && (BombCount > -1)) {
				/*
           Rule 1: if number of bombs = number of cells, all are bombs!
*/
				if (CellCount == BombCount) {
					for (Cell=0; Cell<CellCount; Cell++) {
						x = AdjacencyGroup[CurrentX][CurrentY].Cell[Cell][0];
						y = AdjacencyGroup[CurrentX][CurrentY].Cell[Cell][1];
						MarkBombCell(x, y);
						PositionListHead = AddSurroundingToPositionList(PositionList,
						    PositionListHead,
						    x, y);
						bModifiedAny = TRUE;
					}
				} else {
					/*
             Rule 2: if number of bombs = 0, all cells are ok!
*/
					if ((BombCount == 0) && (CellCount > 0)) {
						for (Cell=0; Cell<CellCount; Cell++) {
							x = AdjacencyGroup[CurrentX][CurrentY].Cell[Cell][0];
							y = AdjacencyGroup[CurrentX][CurrentY].Cell[Cell][1];
							VisitCell(x, y);
							PositionListHead = AddToPositionList(PositionList,
							    PositionListHead,
							    x, y);
							PositionListHead = AddSurroundingToPositionList(PositionList,
							    PositionListHead,
							    x, y);
							bModifiedAny = TRUE;
						}
					} else {
						/*
               Rule 3: if AG completely overlaps another AG, subtract 2nd
                       # of bombs from 1st; check rules 1 & 2. If rule 1 or
                       2 is true in this case, stop looking in rule 3.
*/
						bDone = FALSE;
						for (Cell=0; (Cell<CellCount) && (!bDone); Cell++) {
							x = AdjacencyGroup[CurrentX][CurrentY].Cell[Cell][0];
							y = AdjacencyGroup[CurrentX][CurrentY].Cell[Cell][1];
							for (dX=-1; (dX<=1) && (!bDone); dX++) {
								for (dY=-1; (dY<=1) && (!bDone); dY++) {
/* code folded from here */
	if ((x+dX >= 0) && (x+dX < GRID_X) &&
	    (y+dY >= 0) && (y+dY < GRID_Y)) {
		pTempAG = &AdjacencyGroup[x+dX][y+dY];
		if (pTempAG->BombCount > 0) {       /* if == 0, no help! */
			bDone = ProcessRule3(&AdjacencyGroup[CurrentX][CurrentY],
			    pTempAG,
			    PositionList,
			    &PositionListHead);
			if (bDone) {
				bModifiedAny = TRUE;
			}
		}
	}
/* unfolding */
								}
							}
						}
					}
				}
			}
		}
		if (bModifiedAny) {
			for (x=0; x<GRID_X; x++) {
				for (y=0; y<GRID_Y; y++) {
					if ((Board[x][y] == bVISITED) || (Board[x][y] == bCURRENT)) {
						PositionListHead = AddToPositionList(PositionList,
						    PositionListHead,
						    x, y);
					}
				}
			}
		}
	}
}
BOOL LetUserMove(void)
{
	BOOL bDone;
	BOOL bQuit;
	int  Ch;
	bDone = FALSE;
	while (!bDone) {
		Ch = _getch();
		switch (Ch) {
		case 0:
			Ch = _getch();
			switch (Ch) {
			case 71:  /* home */
				bDone = Travel(-1, -1);
				break;
			case 72:  /* up arrow */
				bDone = Travel(0, -1);
				break;
			case 73:  /* page up */
				bDone = Travel(1, -1);
				break;
			case 75:  /* left arrow */
				bDone = Travel(-1, 0);
				break;
			case 77:  /* right arrow */
				bDone = Travel(1, 0);
				break;
			case 79:  /* end */
				bDone = Travel(-1, 1);
				break;
			case 80:  /* down arrow */
				bDone = Travel(0, 1);
				break;
			case 81:  /* page down */
				bDone = Travel(1, 1);
				break;
			}
			break;
		case '7':  /* home */
			bDone = Travel(-1, -1);
			break;
		case '8':  /* up arrow */
			bDone = Travel(0, -1);
			break;
		case '9':  /* page up */
			bDone = Travel(1, -1);
			break;
		case '4':  /* left arrow */
			bDone = Travel(-1, 0);
			break;
		case '6':  /* right arrow */
			bDone = Travel(1, 0);
			break;
		case '1':  /* end */
			bDone = Travel(-1, 1);
			break;
		case '2':  /* down arrow */
			bDone = Travel(0, 1);
			break;
		case '3':  /* page down */
			bDone = Travel(1, 1);
			break;
		case 'Q':
		case 'q':
		case 27:
			bDone = TRUE;
			break;
		case 'M':
		case 'm':
			PlaceUserMark();
			break;
		case '?':
			EvaluatePosition();
			break;
		}
	}
	bShowBombs = TRUE;
	PaintBoard();
	GotoXY_(0, SCREEN_Y - 2);
	_cprintf("Again (Y/n)? ");
	bDone = FALSE;
	while (!bDone) {
		Ch = _getch();
		if ((Ch == 'Y') || (Ch == 'y') || (Ch == 13) || (Ch == 10)) {
			bDone = TRUE;
			bQuit = FALSE;
			_cprintf("Y\n");
		}
		if ((Ch == 'N') || (Ch == 'n')) {
			bDone = TRUE;
			bQuit = TRUE;
			_cprintf("N\n");
		}
		if (Ch == (int)NULL) {
			_getch();
		}
	}
	return (bQuit);
}
int main(int argc, char *argv[])
{
	BOOL bDone;
	bSafeGame = FALSE;
	for(--argc, ++argv; argc--; ++argv) {
		if((argv[0][0] == '/')
		&&((argv[0][1] == 's') || (argv[0][1] == 'S'))) {
			bSafeGame = TRUE;
			_cprintf("SAFE GAME in effect.\n");
		}
		else if(argv[0][0] == '/' && argv[0][1] == 'n')
			initmines = atoi(&argv[0][2]);
		else if(argv[0][0] == '/' && argv[0][1] == 'r')
			initrand = atoi(&argv[0][2]);
	}
	bDone = FALSE;
	Initialize();
	while (!bDone) {
		SetUpBoard();
		bDone = LetUserMove();
	}
	return (0);
}

#if defined(__WATCOMC__)
/* Special 32-bit random number generator; the one in Watcom's library
 * is only 16-bit (0-0x7FFF), and we need 32-bits (0-0x7FFFFFFF) for
 * one of the bugs in KABOOBUG.EXE
 */
long kb_rand()
{
	long retval = 0L;

	retval = (rand() << 16) | rand();
	return retval;
}
#endif
