// Arup Guha
// 9/27/00
// Program Description : Implements a simplified version of the game 
//                       minesweeper. The user must clear off squares
//                       one by one, and is not allowed to mark bombs.
//                       Any time the user hits a bomb, they lose, just
//                       like the original game. Play will halt if all
//                       non-bomb squares have been cleared, and the player
//                       is declared the winner in this situation.

#include <iostream.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>

const int SIDE = 8; // side length of the board
const int NUMBOMBS =10; // number of bombs on the board

void printboard(char[][SIDE]);
int numberbombs(int,int,int[][2]);
bool domove(char[][SIDE], char[][SIDE], int[][2], int, int, int&);
void getbombs(int[][2]);
void getmove(int&,int&);
bool valid(int,int);

void main() {
  
  bool gameover = false; // used to check if the game is over or not
  
  char uncovered_board[SIDE][SIDE]; // board that stores the location of all
                                    // the bombs and numbers of adjacent bombs.

  char current_board[SIDE][SIDE]; // stores the current board as it should be
                                  // printed out.

  int totalmoves; // stores the total moves left for the user before they win.

  int i,j;
  int x,y;

  int listbombs[NUMBOMBS][2]; // stores (x,y) coordinates of all bombs.
  
  srand(time(0)); // Seed the random number generator.

  // Initialize the current board.
  for (i=0;i<SIDE;i++)
    for (j=0;j<SIDE;j++)
      current_board[i][j] = '_';

  // Randomly choose where the bombs will go.
  getbombs(listbombs);
  
  // Place the bombs onto the board.
  for (i=0;i<NUMBOMBS;i++) 
    uncovered_board[listbombs[i][0]][listbombs[i][1]]='*';
    
  totalmoves = SIDE*SIDE - NUMBOMBS;

  
  // Continue playing until the game ends.
  while (!gameover) {
        
    // Get a move from the user.
    cout << "Here is the board : \n";
    printboard(current_board);
    getmove(x,y);
    
    // Execute that move.
    gameover = domove(current_board,uncovered_board,listbombs,x,y,totalmoves);

    // Check if they have won.
    if ((!gameover) && (totalmoves==0)) {
      cout << "Great! You win!!!\n";
      gameover = true;
    }
  }
  
}

// Pre-condition: Nada
// Post-condition: x and y will be set to the values of the coordinates that
//                 the user desires to uncover on the board.
void getmove(int &x,int &y) {

  cout << "To enter a move for row 2 column 3 type 2 3\n";
  cout << "Enter your move, (row column) -> ";
  cin >> x >> y;
  while (!valid(x,y)) {
    cout << "Sorry those aren't valid coordinates. Please try again.\n";
    cout << "Enter your move, (row column) -> ";
    cin >> x >> y;
  }
}

// Pre-condition: The character array passed into the function must be a
//                square 2 dimensional array with each dimension of size
//                SIZE.
// Post-condition: The board passed into the function will be printed out
//                 exactly according to the characters stored in the array.
void printboard(char board[][SIDE]) {

  int i,j;


  // Print out side labels.
  cout << "    ";
  for (i=0;i<SIDE;i++)
    cout << i << " ";
  cout << "\n\n";

  // Print out actual board.
  for (i=0;i<SIDE;i++) {
    cout << i << "   ";
    for (j=0;j<SIDE;j++)
      cout << board[i][j] << " ";
    cout << endl;
  }
}

// Pre-conditions: Both character arrays are squre 2D arrays with each
//                 dimension of size SIZE, the first dimension of the 
//                 list of bombs array should be of size NUMBOMBS,
//                 row and column must represent the row and column of
//                 the player's move, and totalmoves must equal how many
//                 more squares the user must uncover before winning.
// Post-conditions: The procedure will execute a single move in the game
//                  and make the necessary changes to the auxiliary
//                  data structures to ensure the game runs properly following
//                  the move.
bool domove(char board[][SIDE], char realboard[][SIDE],
            int listbombs[][2], int row, int column, int &totalmoves) {

  // Take care of the case that the user loses.
  if (realboard[row][column]=='*') {
    int i;
    board[row][column]='*';
    for (i=0;i<NUMBOMBS;i++) 
      board[listbombs[i][0]][listbombs[i][1]]='*';
    
    printboard(board);
    cout << "Sorry, you lose!\n";
    return true;
  }
  // Take care of the case that the user chooses a square already chosen.
  else if (board[row][column]!='_') {
    cout << "That square as already been cleared, try again!\n";
    return false;
  }
  // Execute a normal non-losing move.
  else {
    int num;
    num = numberbombs(row,column,listbombs);
    totalmoves--;
    board[row][column]=(char)(num+48);

    if (num == 0) {
      for (int i=-1;i<2;i++) {
	for (int j=-1;j<2;j++) {
	  
	  if (valid(row+i,column+j) && (board[row+i][column+j]=='_'))
	      domove(board, realboard, listbombs, row+i, column+j, totalmoves);
	}
      }
	      
    }

    return false;
  }
}

bool valid(int row, int column) {

  if (row < 0)
    return false;
  else if (row >= SIDE)
    return false;
  else if (column < 0)
    return false;
  else if (column >= SIDE)
    return false;
  else
    return true;
}

// Pre-condition: The size of the first dimension of listbombs is NUMBOMBS,
//                and row and column indicate where the user last moved.
// Post-condition: The function will compute how many bombs are adjacent to
//                 the square the user desires to uncover.
int numberbombs(int row ,int column ,int listbombs[][2]) {

  int i;
  int count = 0;
  for (i=0;i<NUMBOMBS;i++) {

    // Check each bomb one by one against the square to uncover.
    if ((fabs(row-listbombs[i][0])<=1) && (fabs(column-listbombs[i][1])<=1))
      count++;
  }
  return count;
}


// Pre-condition : The size of the first dimension of listbombs is NUMBOMBS.
// Post-condition: The function will randomly select the appropriate number
//                 of bombs and place them on the board, storing the 
//                 information in bomblist.
void getbombs(int bomblist[][2]) {
  
  int j=0;
  int i=0;
  int help;
  int curbombs[SIDE*SIDE] = {0};
 
  // Continue until all random bombs have been created.
  while(i<NUMBOMBS) {
    
    help = rand()%(SIDE*SIDE);

    // Only add the bomb if it is not a duplicate.
    if (curbombs[help]==0) {
      bomblist[i][0] = help/SIDE;
      bomblist[i][1] = help%SIDE;
      i++;
      curbombs[help] = 1;
    }
    
  }
 
}
