home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 10 / 10.iso / l / l350 / 3.ddi / EXAMPLES / SB386 / KABOOM1.C < prev    next >
Encoding:
C/C++ Source or Header  |  1993-02-10  |  21.6 KB  |  841 lines

  1. /* This program appeared in an article written by Alex Lane in the April
  2.  * 1990 issue of Dr. Dobb's Journal (pg 72).  It is used here with
  3.  * permission of the publisher, M&T Publishing Inc.
  4.  */
  5.  
  6. /****************************************************************************
  7. File:    Kaboooom.c
  8. Purpose: Allows the user to 'walk' through a minefield; a detector shows
  9.   how many mines are immediately adjacent to you. As you visit a cell, it
  10.   leaves a marker telling you how many were next to you, and you have the
  11.   ability to mark cells with a character (assumably to mark mines).
  12.  Changes:
  13.  11/10/89 (tdeii) If you call the program with "/s" or "/S", it gives
  14.   you a "safer" game, where it does not let you walk on spaces that you
  15.   have marked (whether there is a mine there or not!)
  16.   11/11/89 (tdeii) Allows you to press "?" and get some help starting at your
  17.   current position; will mark mines that it knows (by deducing their position),
  18.   and "visit" places that it knows are safe. This propagates until it cannot
  19.   deduce anything else (see EvaluatePosition).
  20.   10/90 (pharlap) Added switchs to allow number of mines (/n) and random seed
  21.    to (/r) be set on command line.
  22. ****************************************************************************/
  23. #include "stdio.h"
  24. #include "stdarg.h"
  25. #include "stdlib.h"
  26. #include "dos.h"
  27. #include "conio.h"
  28. #include "string.h"
  29. #include "kaboom1.h"
  30.  
  31. #define SCREEN_X 80
  32. #define SCREEN_Y 25
  33. #define GRID_X 15
  34. #define GRID_Y  9
  35. #define TRUE 1
  36. #define FALSE 0
  37.  
  38. #define bEMPTY    0
  39. #define bVISITED  1
  40. #define bBOMB     2
  41. #define bCURRENT  3
  42. #define bFINISH   4
  43. #define bEXPLODED 5
  44.  
  45. #define MAKECOLOR(fore,back) ((back)*16+(fore))
  46.  
  47. typedef int BOOL;
  48. typedef struct tagADJACENCYGROUP {
  49.     int BombCount;         /* Number of bombs located in this adj. group */
  50.     int CellCount;         /* Number of cells filled                     */
  51.     int Cell[8][2];        /* x,y coordinates of up to 8 cells           */
  52. } ADJACENCYGROUP;
  53.  
  54. int  Board[GRID_X][GRID_Y];           /* Board; see codes above (bXXX)    */
  55. int  UserMark[GRID_X][GRID_Y];        /* User marks; 0 = none, 'M' = mine */
  56. int  nNumMines;                       /* Number of mines on board         */
  57. int  initmines=0;              /* nMines passed with /n switch     */
  58. int  initrand=0;              /* init rand seed from command line */
  59. int  UserX, UserY;                    /* Current user X and Y position    */
  60. BOOL bShowBombs;                      /* TRUE if program shows bombs (it  */
  61. /* does this after you win or lose) */
  62. BOOL bSafeGame;                       /* TRUE if program does not let you */
  63. /* walk on mines you have marked    */
  64. ADJACENCYGROUP AdjacencyGroup[GRID_X][GRID_Y]; /* AG for each board pos   */
  65. char szClear[79] = "                                                                              ";
  66. int CountMines(int x, int y)
  67. {
  68.     int i, j;
  69.     int nCount;
  70.     nCount = 0;
  71.     for (i=-1; i<=1; i++) {
  72.         for (j=-1; j<=1; j++) {
  73.             if ((x+i >= 0) && (x+i < GRID_X) &&
  74.                 (y+j >= 0) && (y+j < GRID_Y)) {
  75.                 if ((Board[x+i][y+j] == bBOMB) ||
  76.                     (Board[x+i][y+j] == bEXPLODED)) {
  77.                     nCount++;
  78.                 }
  79.             }
  80.         }
  81.     }
  82.     return (nCount);
  83. }
  84. void DisplayCell(int x, int y)
  85. {
  86.     int Char;
  87.     Char = UserMark[x][y];
  88.     if (Char == 0) {
  89.         Char = 32;
  90.     }
  91.     DisplayChar(x*4+1, y*2+1, Char, MAKECOLOR(14,1));
  92.     DisplayChar(x*4+3, y*2+1, Char, MAKECOLOR(14,1));
  93.     switch (Board[x][y]) {
  94.     case bEMPTY:                                       /** Empty cell   **/
  95.         Char = ' ';
  96.         break;
  97.     case bVISITED:                                     /** Visited cell **/
  98.         Char = '0' + CountMines(x, y);
  99.         break;
  100.     case bBOMB:                                        /** Bomb cell!   **/
  101.         if (bShowBombs) {
  102.             Char = 15;
  103.         } else {
  104.             Char =' ';
  105.         }
  106.         break;
  107.     case bCURRENT:                                     /** Current pos  **/
  108.         Char = 2;
  109.         break;
  110.     case bFINISH:                                      /** Finish cell  **/
  111.         Char = 19;
  112.         break;
  113.     case bEXPLODED:                                    /** Exploded!    **/
  114.         Char = 15;
  115.         break;
  116.     }
  117.     if (Char != 0) {
  118.         DisplayChar(x*4+2, y*2+1, Char, MAKECOLOR(14,1));
  119.     }
  120. }
  121.  
  122. void PaintBoard(void)
  123. {
  124.     int x, y, i;
  125.     for (x=0; x<SCREEN_X; x++) {
  126.         for (y=0; y<SCREEN_Y; y++) {
  127.             DisplayChar(x, y, ' ', MAKECOLOR(14, 1));
  128.         }
  129.     }
  130.     /** Draw left and right sides **/
  131.     DisplayChar(0, 0, 218, MAKECOLOR(14,1));  /*upper left corner */
  132.     DisplayChar(GRID_X*4, 0, 191, MAKECOLOR(14,1)); /*upper right corner */
  133.     for (y=1; y<=GRID_Y; y++) {
  134.         DisplayChar(0, y*2, 195, MAKECOLOR(14,1));  /* left edge */
  135.         DisplayChar(GRID_X*4, y*2, 180, MAKECOLOR(14,1)); /* right edge */
  136.     }
  137.     DisplayChar(0, GRID_Y*2, 192, MAKECOLOR(14,1));  /* lower left corner */
  138.     DisplayChar(GRID_X*4, GRID_Y*2, 217, MAKECOLOR(14,1)); /*lower right */
  139.     /** Draw inside corners **/
  140.     for (x=1; x<GRID_X; x++) {
  141.         DisplayChar(x*4, 0, 194, MAKECOLOR(14,1)); /* top edge */
  142.         for (y=1; y<GRID_Y; y++) {
  143.             DisplayChar(x*4, y*2, 197, MAKECOLOR(14,1)); /* intersections */
  144.         }
  145.         DisplayChar(x*4, GRID_Y*2, 193, MAKECOLOR(14,1)); /* bottom edge */
  146.     }
  147.     /** Draw connecting lines **/
  148.     for (x=0; x<=GRID_X; x++) {
  149.         for (y=0; y<=GRID_Y; y++) {
  150.             if (y != GRID_Y) {
  151.                 DisplayChar(x*4, y*2+1, 179, MAKECOLOR(14,1)); /* verticals  */
  152.             }
  153.             if (x != GRID_X) {
  154.                 for (i=1; i<4; i++) {
  155.                     DisplayChar(x*4+i, y*2, 196 , MAKECOLOR(14,1)); /* horizontals */
  156.                 }
  157.             }
  158.         }
  159.     }
  160.     GotoXY_(0, SCREEN_Y - 2);
  161.     for (x=0; x<GRID_X; x++) {
  162.         for (y=0; y<GRID_Y; y++) {
  163.             DisplayCell(x, y);
  164.         }
  165.     }
  166. }
  167. void SetUpBoard(void)
  168. {
  169.     int i, j;
  170.     int nMines;
  171.     BOOL bDone;
  172.     char cBuffer[80];
  173.     bShowBombs = FALSE;
  174.     /** First, get number of bombs **/
  175.     if(!initmines) {
  176.         nNumMines = 0;
  177.         while ((nNumMines < 10) || (nNumMines > 40)) {
  178.             GotoXY_(0, 24);
  179.             _cprintf("How many bombs do you want? (10-40)?? ");
  180.             fgets(cBuffer, sizeof(cBuffer), stdin);
  181.             sscanf(cBuffer, "%d", &nNumMines);
  182.         }
  183.     } else
  184.         nNumMines = initmines;
  185.  
  186.     /** next, clear out board & user scratchpad **/
  187.     for (i=0; i<GRID_X; i++) {
  188.         for (j=0; j<GRID_Y; j++) {
  189.             Board[i][j] = 0;
  190.             UserMark[i][j] = 0;
  191.         }
  192.     }
  193.     for (nMines=0; nMines<nNumMines; nMines++) {
  194.         bDone = FALSE;
  195.         while (!bDone) {
  196.             i = nRandom(GRID_X);         /* First you roll it, */
  197.             j = nRandom(GRID_Y);         /* Then you pat it,   */
  198.             if ((Board[i][j] == bEMPTY) &&
  199.                 (!((i <= 1) && (j <= 1))) &&
  200.                 (!((i >= GRID_X - 2) && (j >= GRID_Y - 2)))
  201.                 ) {
  202.                 bDone = TRUE;
  203.             }
  204.         }
  205.         Board[i][j] = bBOMB;           /* Then you mark it with a 'B' */
  206.     }
  207.     /* Set user at position 0, 0 */
  208.     UserX = 0;
  209.     UserY = 0;
  210.     Board[0][0] = bCURRENT;
  211.     /* Set finish (hq) at position GRID_X, GRID_Y */
  212.     Board[GRID_X - 1][GRID_Y - 1] = bFINISH;
  213.     /* Display board on screen */
  214.     PaintBoard();
  215. }
  216. BOOL Travel(int dx, int dy)
  217. {
  218.     int NewX, NewY;            /* New X and Y coordinates of user       */
  219.     BOOL bInvalid;             /* TRUE if trying to walk off board      */
  220.     BOOL bAbort;               /* TRUE if user won or lost (abort game) */
  221.     BOOL bBombWalk;            /* TRUE if user tried to walk on a bomb  */
  222.  
  223.     bAbort = FALSE;
  224.     NewX = UserX + dx;
  225.     NewY = UserY + dy;
  226.     bInvalid = FALSE;
  227.     bBombWalk = FALSE;
  228.     if ((NewX < 0) || (NewX >= GRID_X)) {
  229.         bInvalid = TRUE;
  230.     }
  231.     if ((NewY < 0) || (NewY >= GRID_Y)) {
  232.         bInvalid = TRUE;
  233.     }
  234.     if ((!bInvalid) && (bSafeGame) && (UserMark[NewX][NewY] == 'M')) {
  235.         bInvalid = TRUE;
  236.         bBombWalk = TRUE;
  237.     }
  238.     if (bInvalid) {
  239.         GotoXY_(0, SCREEN_Y - 2);
  240.         _cprintf("** INVALID MOVE ** ... press any key...");
  241.         if (bBombWalk) {
  242.             _cprintf("(You must un-mark it.)");
  243.         }
  244.         Pause();
  245.         GotoXY_(0, SCREEN_Y - 2);
  246.         _cprintf(szClear);
  247.     } else {
  248.         if (Board[NewX][NewY] == bBOMB) {
  249.             bAbort = TRUE;
  250.             Board[UserX][UserY] = bVISITED;
  251.             DisplayCell(UserX, UserY);
  252.             Board[NewX][NewY] = bEXPLODED;
  253.             DisplayCell(NewX, NewY);
  254.             GotoXY_(0, 22);
  255.             _cprintf("******** YOU HAVE STEPPED ON A BOMB!! ********");
  256.             Pause();
  257.             GotoXY_(0, 22);
  258.             _cprintf(szClear);
  259.             GotoXY_(0, 22);
  260.         } else {
  261.             if ((NewX == GRID_X-1) && (NewY == GRID_Y-1)) {
  262.                 bAbort = TRUE;
  263.                 Board[UserX][UserY] = bVISITED;
  264.                 DisplayCell(UserX, UserY);
  265.                 Board[NewX][NewY] = bCURRENT;
  266.                 DisplayCell(NewX, NewY);
  267.                 GotoXY_(0, 22);
  268.                 _cprintf("************* YOU HAVE WON!! *************");
  269.                 Pause();
  270.                 GotoXY_(0, 22);
  271.                 _cprintf(szClear);
  272.                 GotoXY_(0, 22);
  273.             } else {
  274.                 Board[UserX][UserY] = bVISITED;
  275.                 DisplayCell(UserX, UserY);
  276.                 UserX = NewX;
  277.                 UserY = NewY;
  278.                 Board[UserX][UserY] = bCURRENT;
  279.                 DisplayCell(UserX, UserY);
  280.             }
  281.         }
  282.     }
  283.     GotoXY_(0, GRID_Y*2+2);
  284.     _cprintf("Number of mines around you: %d", CountMines(UserX, UserY));
  285.     GotoXY_(0, SCREEN_Y - 2);
  286.     return (bAbort);
  287. }
  288. void PlaceUserMark(void)
  289. {
  290.     BOOL bDone, bAbort;
  291.     int  Ch;
  292.     int  NewX, NewY;
  293.     int  dx, dy;
  294.  
  295.     bAbort = FALSE;
  296.     GotoXY_(0, 24);
  297.     _cprintf("Mark in which direction? (ESC=abort)");
  298.     bDone = FALSE;
  299.     while (!bDone) {
  300.         bDone = TRUE;
  301.         Ch = _getch();
  302.         switch (Ch) {
  303.         case 0:
  304.             Ch = _getch();
  305.             switch (Ch) {
  306.             case 71:  /* home */
  307.                 dx = -1;
  308.                 dy = -1;
  309.                 break;
  310.             case 72:  /* up arrow */
  311.                 dx = 0;
  312.                 dy = -1;
  313.                 break;
  314.             case 73:  /* page up */
  315.                 dx = 1;
  316.                 dy = -1;
  317.                 break;
  318.             case 75:  /* left arrow */
  319.                 dx = -1;
  320.                 dy = 0;
  321.                 break;
  322.             case 77:  /* right arrow */
  323.                 dx = 1;
  324.                 dy = 0;
  325.                 break;
  326.             case 79:  /* end */
  327.                 dx = -1;
  328.                 dy = 1;
  329.                 break;
  330.             case 80:  /* down arrow */
  331.                 dx = 0;
  332.                 dy = 1;
  333.                 break;
  334.             case 81:  /* page down */
  335.                 dx = 1;
  336.                 dy = 1;
  337.                 break;
  338.             default:
  339.                 bDone = FALSE;
  340.                 break;
  341.             }
  342.             break;
  343.         case '7':  /* home */
  344.             dx = -1;
  345.             dy = -1;
  346.             break;
  347.         case '8':  /* up arrow */
  348.             dx = 0;
  349.             dy = -1;
  350.             break;
  351.         case '9':  /* page up */
  352.             dx = 1;
  353.             dy = -1;
  354.             break;
  355.         case '4':  /* left arrow */
  356.             dx = -1;
  357.             dy = 0;
  358.             break;
  359.         case '6':  /* right arrow */
  360.             dx = 1;
  361.             dy = 0;
  362.             break;
  363.         case '1':  /* end */
  364.             dx = -1;
  365.             dy = 1;
  366.             break;
  367.         case '2':  /* down arrow */
  368.             dx = 0;
  369.             dy = 1;
  370.             break;
  371.         case '3':  /* page down */
  372.             dx = 1;
  373.             dy = 1;
  374.             break;
  375.         case 27:
  376.         case 13:
  377.         case 10:
  378.         case  8:
  379.             bAbort = TRUE;
  380.             break;
  381.         default:
  382.             bDone = FALSE;
  383.             break;
  384.         }
  385.     }
  386.     GotoXY_(0, 24);
  387.     _cprintf(szClear);
  388.     if (!bAbort) {
  389.         NewX = UserX + dx;
  390.         NewY = UserY + dy;
  391.         if ((NewX < 0) || (NewX >= GRID_X) || (NewY < 0) || (NewY >= GRID_Y)) {
  392.             GotoXY_(0, 24);
  393.             _cprintf("ERROR: Out of bounds!!");
  394.             Pause();
  395.             GotoXY_(0, 24);
  396.             _cprintf(szClear);
  397.         } else {
  398.             GotoXY_(0, 24);
  399.             if (UserMark[NewX][NewY] != 0) {
  400.                 Ch = 0;
  401.             } else {
  402.                 Ch = 'M';
  403.             }
  404.             UserMark[NewX][NewY] = Ch;
  405.             DisplayCell(NewX, NewY);
  406.         }
  407.     }
  408.     GotoXY_(0, 24);
  409. }
  410. void ComputeAdjacency(int x, int y)
  411. {
  412.     int dX, dY;
  413.     int BombCount;
  414.     int Cell;
  415.     if ((x >= 0) && (x < GRID_X) && (y >= 0) && (y < GRID_Y)) {
  416.         if ((Board[x][y] == bVISITED) || (Board[x][y] == bCURRENT)) {
  417.             BombCount = CountMines(x, y);
  418.             Cell = 0;
  419.             for (dX=-1; dX<=1; dX++) {
  420.                 for (dY=-1; dY<=1; dY++) {
  421.                     if (!((dX == 0) && (dY == 0))) {
  422.                         if ((x+dX >= 0) && (x+dX < GRID_X) &&
  423.                             (y+dY >= 0) && (y+dY < GRID_Y)) {
  424.                             if ((Board[x+dX][y+dY] != bVISITED) &&
  425.                                 (Board[x+dX][y+dY] != bCURRENT)) {
  426.                                 if (UserMark[x+dX][y+dY] != 0) {
  427. /* code folded from here */
  428.     BombCount--;
  429. /* unfolding */
  430.                                 } else {
  431. /* code folded from here */
  432.     AdjacencyGroup[x][y].Cell[Cell][0] = x+dX;
  433.     AdjacencyGroup[x][y].Cell[Cell][1] = y+dY;
  434.     Cell++;
  435. /* unfolding */
  436.                                 }
  437.                             }
  438.                         }
  439.                     }
  440.                 }
  441.             }
  442.             AdjacencyGroup[x][y].BombCount = BombCount;
  443.              AdjacencyGroup[x][y].CellCount = Cell;
  444.         } else {
  445.             AdjacencyGroup[x][y].CellCount = 0;
  446.             AdjacencyGroup[x][y].BombCount = -1;   /** Don't look flag */
  447.         }
  448.     }
  449. }
  450. int AddToPositionList(int PositionList[GRID_X * GRID_Y][2],
  451. int PositionListHead, int x, int y)
  452. {
  453.     int  nIndex;
  454.     BOOL bFound;
  455.     ComputeAdjacency(x, y);
  456.     bFound = FALSE;
  457.     for (nIndex=0; (nIndex<PositionListHead) && (!bFound); nIndex++) {
  458.         if ((PositionList[nIndex][0] == x) && (PositionList[nIndex][1] == y)) {
  459.             bFound = TRUE;
  460.         }
  461.     }
  462.     if (!bFound) {
  463.         PositionList[PositionListHead][0] = x;
  464.         PositionList[PositionListHead][1] = y;
  465.         PositionListHead++;
  466.     }
  467.     if (PositionListHead > GRID_X * GRID_Y) {
  468.         GotoXY_(0, 22);
  469.         _cprintf("ERROR! PositionListHead > max (%d)", PositionListHead);
  470.         Pause();
  471.         GotoXY_(0, 22);
  472.         _cprintf(szClear);
  473.         GotoXY_(0, 22);
  474.     }
  475.     return (PositionListHead);
  476. }
  477. int AddSurroundingToPositionList(int PositionList[GRID_X * GRID_Y][2],
  478. int PositionListHead, int x, int y)
  479. {
  480.     int dX, dY;
  481.  
  482.     for (dX=-1; dX<=1; dX++) {
  483.         for (dY=-1; dY<=1; dY++) {
  484.             if ((x+dX >= 0) && (x+dX < GRID_X) && (y+dY >= 0) && (y+dY < GRID_Y)) {
  485.                 if ((Board[x+dX][y+dY] == bVISITED) ||
  486.                     (Board[x+dX][y+dY] == bCURRENT)) {
  487.                     PositionListHead = AddToPositionList(PositionList, PositionListHead,
  488.                         x+dX, y+dY);
  489.                 }
  490.             }
  491.         }
  492.     }
  493.     return (PositionListHead);
  494. }
  495. BOOL FindPositionInAG(ADJACENCYGROUP *pAG, int x, int y)
  496. {
  497.     int  nIndex;
  498.     BOOL bFound;
  499.     bFound = FALSE;
  500.     for (nIndex=0; nIndex<pAG->CellCount; nIndex++) {
  501.         if ((pAG->Cell[nIndex][0] == x) && (pAG->Cell[nIndex][1] == y)) {
  502.             bFound = TRUE;
  503.         }
  504.     }
  505.     return (bFound);
  506. }
  507. void MarkBombCell(int x, int y)
  508. {
  509.     UserMark[x][y] = 'M';
  510.     DisplayCell(x, y);
  511.     if (Board[x][y] != bBOMB) {
  512.         GotoXY_(0, 22);
  513.         _cprintf("LOGIC ERROR: I tagged a phantom bomb @ (%d,%d).", x, y);
  514.         Pause();
  515.         GotoXY_(0, 22);
  516.         _cprintf(szClear);
  517.         GotoXY_(0, 24);
  518.     }
  519. }
  520. void VisitCell(int x, int y)
  521. {
  522.     if (Board[x][y] != bCURRENT) {
  523.         if (Board[x][y] == bBOMB) {
  524.             GotoXY_(0, 22);
  525.             _cprintf("LOGIC ERROR: I walked on a bomb @ (%d,%d).", x, y);
  526.             Pause();
  527.             GotoXY_(0, 22);
  528.             _cprintf(szClear);
  529.             GotoXY_(0, 24);
  530.         }
  531.         Board[x][y] = bVISITED;
  532.         DisplayCell(x, y);
  533.     }
  534. }
  535. int CountCommonCells(ADJACENCYGROUP *pGroup1, ADJACENCYGROUP *pGroup2)
  536. {
  537.     int Cell, nCount;
  538.     nCount = 0;
  539.     for (Cell=0; Cell<pGroup1->CellCount; Cell++) {
  540.         if (FindPositionInAG(pGroup2,
  541.             pGroup1->Cell[Cell][0], pGroup1->Cell[Cell][1])) {
  542.             nCount++;
  543.         }
  544.     }
  545.     return (nCount);
  546. }
  547. BOOL ProcessRule3(ADJACENCYGROUP *pCurrentAG, ADJACENCYGROUP *pTempAG,
  548. int PositionList[GRID_X * GRID_Y][2],
  549. int *pPositionListHead)
  550. {
  551.     int x;
  552.     int BombCount, CellCount;
  553.     int PositionListHead;
  554.     int CellHolder[9][2];
  555.     int CellHolderHead;
  556.     BOOL bRetVal;
  557.     PositionListHead = *pPositionListHead;
  558.     bRetVal = FALSE;
  559.     BombCount = pCurrentAG->BombCount;
  560.     CellCount = pCurrentAG->CellCount;
  561.     if (pTempAG->CellCount == CountCommonCells(pTempAG, pCurrentAG)) {
  562.         BombCount -= pTempAG->BombCount;
  563.         CellCount -= pTempAG->CellCount;
  564.         if ((CellCount > 0) && ((BombCount == CellCount) || (BombCount == 0))) {
  565.             bRetVal = TRUE;
  566.             CellHolderHead = 0;
  567.             CellCount = pCurrentAG->CellCount;
  568.             for (x=0; x<CellCount; x++) {
  569.                 if (!FindPositionInAG(pTempAG, pCurrentAG->Cell[x][0],
  570.                     pCurrentAG->Cell[x][1])) {
  571.                     if (BombCount == 0) {
  572.                         VisitCell(pCurrentAG->Cell[x][0], pCurrentAG->Cell[x][1]);
  573.                     } else {
  574.                         MarkBombCell(pCurrentAG->Cell[x][0], pCurrentAG->Cell[x][1]);
  575.                     }
  576.                     /* Queue up cells to put in position list for later */
  577.                     CellHolder[CellHolderHead][0] = pCurrentAG->Cell[x][0];
  578.                     CellHolder[CellHolderHead][1] = pCurrentAG->Cell[x][1];
  579.                     CellHolderHead++;
  580.                 }
  581.             }
  582.             for (x=0; x<CellHolderHead; x++) {
  583.                 PositionListHead = AddSurroundingToPositionList(
  584.                     PositionList,
  585.                     PositionListHead,
  586.                     CellHolder[x][0],
  587.                     CellHolder[x][1]);
  588.             }
  589.         }
  590.     }
  591.     *pPositionListHead = PositionListHead;
  592.     return (bRetVal);
  593. }
  594. void EvaluatePosition(void)
  595. {
  596.     int  CurrentX, CurrentY;
  597.     int  x, y;
  598.     int  Cell;
  599.     int  dX, dY;
  600.     int  BombCount, CellCount;
  601.     int  PositionList[GRID_X * GRID_Y][2], PositionListHead;
  602.     ADJACENCYGROUP *pTempAG;
  603.     BOOL bDone;
  604.     BOOL bModifiedAny;
  605.     bModifiedAny = TRUE;
  606.     for (x=0; x<GRID_X; x++) {
  607.         for (y=0; y<GRID_Y; y++) {
  608.             ComputeAdjacency(x, y);
  609.         }
  610.     }
  611.     PositionList[0][0] = UserX;
  612.     PositionList[0][1] = UserY;
  613.     PositionListHead = 1;
  614.     while (bModifiedAny) {
  615.         bModifiedAny = FALSE;
  616.         while (PositionListHead > 0) {
  617.             CurrentX = PositionList[0][0];
  618.             CurrentY = PositionList[0][1];
  619.             for (x=0; x<PositionListHead-1; x++) {
  620.                 PositionList[x][0] = PositionList[x+1][0];
  621.                 PositionList[x][1] = PositionList[x+1][1];
  622.             }
  623.             PositionListHead--;
  624.             ComputeAdjacency(CurrentX, CurrentY);
  625.             BombCount = AdjacencyGroup[CurrentX][CurrentY].BombCount;
  626.             CellCount = AdjacencyGroup[CurrentX][CurrentY].CellCount;
  627.             if ((CellCount > 0) && (BombCount > -1)) {
  628.                 /*
  629.            Rule 1: if number of bombs = number of cells, all are bombs!
  630. */
  631.                 if (CellCount == BombCount) {
  632.                     for (Cell=0; Cell<CellCount; Cell++) {
  633.                         x = AdjacencyGroup[CurrentX][CurrentY].Cell[Cell][0];
  634.                         y = AdjacencyGroup[CurrentX][CurrentY].Cell[Cell][1];
  635.                         MarkBombCell(x, y);
  636.                         PositionListHead = AddSurroundingToPositionList(PositionList,
  637.                             PositionListHead,
  638.                             x, y);
  639.                         bModifiedAny = TRUE;
  640.                     }
  641.                 } else {
  642.                     /*
  643.              Rule 2: if number of bombs = 0, all cells are ok!
  644. */
  645.                     if ((BombCount == 0) && (CellCount > 0)) {
  646.                         for (Cell=0; Cell<CellCount; Cell++) {
  647.                             x = AdjacencyGroup[CurrentX][CurrentY].Cell[Cell][0];
  648.                             y = AdjacencyGroup[CurrentX][CurrentY].Cell[Cell][1];
  649.                             VisitCell(x, y);
  650.                             PositionListHead = AddToPositionList(PositionList,
  651.                                 PositionListHead,
  652.                                 x, y);
  653.                             PositionListHead = AddSurroundingToPositionList(PositionList,
  654.                                 PositionListHead,
  655.                                 x, y);
  656.                             bModifiedAny = TRUE;
  657.                         }
  658.                     } else {
  659.                         /*
  660.                Rule 3: if AG completely overlaps another AG, subtract 2nd
  661.                        # of bombs from 1st; check rules 1 & 2. If rule 1 or
  662.                        2 is true in this case, stop looking in rule 3.
  663. */
  664.                         bDone = FALSE;
  665.                         for (Cell=0; (Cell<CellCount) && (!bDone); Cell++) {
  666.                             x = AdjacencyGroup[CurrentX][CurrentY].Cell[Cell][0];
  667.                             y = AdjacencyGroup[CurrentX][CurrentY].Cell[Cell][1];
  668.                             for (dX=-1; (dX<=1) && (!bDone); dX++) {
  669.                                 for (dY=-1; (dY<=1) && (!bDone); dY++) {
  670. /* code folded from here */
  671.     if ((x+dX >= 0) && (x+dX < GRID_X) &&
  672.         (y+dY >= 0) && (y+dY < GRID_Y)) {
  673.         pTempAG = &AdjacencyGroup[x+dX][y+dY];
  674.         if (pTempAG->BombCount > 0) {       /* if == 0, no help! */
  675.             bDone = ProcessRule3(&AdjacencyGroup[CurrentX][CurrentY],
  676.                 pTempAG,
  677.                 PositionList,
  678.                 &PositionListHead);
  679.             if (bDone) {
  680.                 bModifiedAny = TRUE;
  681.             }
  682.         }
  683.     }
  684. /* unfolding */
  685.                                 }
  686.                             }
  687.                         }
  688.                     }
  689.                 }
  690.             }
  691.         }
  692.         if (bModifiedAny) {
  693.             for (x=0; x<GRID_X; x++) {
  694.                 for (y=0; y<GRID_Y; y++) {
  695.                     if ((Board[x][y] == bVISITED) || (Board[x][y] == bCURRENT)) {
  696.                         PositionListHead = AddToPositionList(PositionList,
  697.                             PositionListHead,
  698.                             x, y);
  699.                     }
  700.                 }
  701.             }
  702.         }
  703.     }
  704. }
  705. BOOL LetUserMove(void)
  706. {
  707.     BOOL bDone;
  708.     BOOL bQuit;
  709.     int  Ch;
  710.     bDone = FALSE;
  711.     while (!bDone) {
  712.         Ch = _getch();
  713.         switch (Ch) {
  714.         case 0:
  715.             Ch = _getch();
  716.             switch (Ch) {
  717.             case 71:  /* home */
  718.                 bDone = Travel(-1, -1);
  719.                 break;
  720.             case 72:  /* up arrow */
  721.                 bDone = Travel(0, -1);
  722.                 break;
  723.             case 73:  /* page up */
  724.                 bDone = Travel(1, -1);
  725.                 break;
  726.             case 75:  /* left arrow */
  727.                 bDone = Travel(-1, 0);
  728.                 break;
  729.             case 77:  /* right arrow */
  730.                 bDone = Travel(1, 0);
  731.                 break;
  732.             case 79:  /* end */
  733.                 bDone = Travel(-1, 1);
  734.                 break;
  735.             case 80:  /* down arrow */
  736.                 bDone = Travel(0, 1);
  737.                 break;
  738.             case 81:  /* page down */
  739.                 bDone = Travel(1, 1);
  740.                 break;
  741.             }
  742.             break;
  743.         case '7':  /* home */
  744.             bDone = Travel(-1, -1);
  745.             break;
  746.         case '8':  /* up arrow */
  747.             bDone = Travel(0, -1);
  748.             break;
  749.         case '9':  /* page up */
  750.             bDone = Travel(1, -1);
  751.             break;
  752.         case '4':  /* left arrow */
  753.             bDone = Travel(-1, 0);
  754.             break;
  755.         case '6':  /* right arrow */
  756.             bDone = Travel(1, 0);
  757.             break;
  758.         case '1':  /* end */
  759.             bDone = Travel(-1, 1);
  760.             break;
  761.         case '2':  /* down arrow */
  762.             bDone = Travel(0, 1);
  763.             break;
  764.         case '3':  /* page down */
  765.             bDone = Travel(1, 1);
  766.             break;
  767.         case 'Q':
  768.         case 'q':
  769.         case 27:
  770.             bDone = TRUE;
  771.             break;
  772.         case 'M':
  773.         case 'm':
  774.             PlaceUserMark();
  775.             break;
  776.         case '?':
  777.             EvaluatePosition();
  778.             break;
  779.         }
  780.     }
  781.     bShowBombs = TRUE;
  782.     PaintBoard();
  783.     GotoXY_(0, SCREEN_Y - 2);
  784.     _cprintf("Again (Y/n)? ");
  785.     bDone = FALSE;
  786.     while (!bDone) {
  787.         Ch = _getch();
  788.         if ((Ch == 'Y') || (Ch == 'y') || (Ch == 13) || (Ch == 10)) {
  789.             bDone = TRUE;
  790.             bQuit = FALSE;
  791.             _cprintf("Y\n");
  792.         }
  793.         if ((Ch == 'N') || (Ch == 'n')) {
  794.             bDone = TRUE;
  795.             bQuit = TRUE;
  796.             _cprintf("N\n");
  797.         }
  798.         if (Ch == (int)NULL) {
  799.             _getch();
  800.         }
  801.     }
  802.     return (bQuit);
  803. }
  804. int main(int argc, char *argv[])
  805. {
  806.     BOOL bDone;
  807.     bSafeGame = FALSE;
  808.     for(--argc, ++argv; argc--; ++argv) {
  809.         if((argv[0][0] == '/')
  810.         &&((argv[0][1] == 's') || (argv[0][1] == 'S'))) {
  811.             bSafeGame = TRUE;
  812.             _cprintf("SAFE GAME in effect.\n");
  813.         }
  814.         else if(argv[0][0] == '/' && argv[0][1] == 'n')
  815.             initmines = atoi(&argv[0][2]);
  816.         else if(argv[0][0] == '/' && argv[0][1] == 'r')
  817.             initrand = atoi(&argv[0][2]);
  818.     }
  819.     bDone = FALSE;
  820.     Initialize();
  821.     while (!bDone) {
  822.         SetUpBoard();
  823.         bDone = LetUserMove();
  824.     }
  825.     return (0);
  826. }
  827.  
  828. #if defined(__WATCOMC__)
  829. /* Special 32-bit random number generator; the one in Watcom's library
  830.  * is only 16-bit (0-0x7FFF), and we need 32-bits (0-0x7FFFFFFF) for
  831.  * one of the bugs in KABOOBUG.EXE
  832.  */
  833. long kb_rand()
  834. {
  835.     long retval = 0L;
  836.  
  837.     retval = (rand() << 16) | rand();
  838.     return retval;
  839. }
  840. #endif
  841.