# Minesweeper

## Problem

• Program a “simplified” Minesweeper
• “Simplified” means:
• 4 x 4 matrix
• No automatic expansion if you uncover a ‘0’
• No fancy graphics
• User inputs 0-based row, col coordinates for each guess (row/col labels will be printed on matrix for quick reference)
• User wins if every “safe” spot has been guessed

## Problem-solving steps

### Siphon the solution

I build a solution board that the user can’t see

1. I build a guessing board that the user can see
2. Until the user hits a bomb or wins,
1. they guess a position and
2. I fill it in on the guessing board.
3. Check if it’s a bomb/solution
3. I show them the solution
4. I tell them if they win or lose

## Solution

/*
These are general descriptions of test cases. Yours should be a bit more detailed about the exact inputs

Test case 1:
Input: Play until hitting a bomb
Expected Output: YOU LOSE (show board)
Actual:

Test case 2:
Input: Play until expose all safe spots
Expected Output: WINNER! (show board)
Actual:

Test case 3:
Input: Increase the bomb_sparcity
Expected Output: Fewer bombs (more zeroes, easier)
Actual:
*/

#include <iostream>
#include <string>
#include <ctime>

using namespace std;

const int BOARD_DIMENSION = 5;
const int BOMB_SPARCITY = 3;
const string BOMB_STRING = "*";
const string UNKNOWN_STRING = " ";

/*
Function to randomly place bombs on a game board.
Each position has a 1 in BOMB_SPARCITY chance of being a bomb
@param board the uninitialized minesweeper solution board
*/
{
// for every row
for (int row = 0; row < BOARD_DIMENSION; row++)
{
// for every column
for (int col = 0; col < BOARD_DIMENSION; col++)
{
// add bombs, but not every time (just one in BOMB_SPARCITY times)
int bomb_roll = rand() % BOMB_SPARCITY; // generate a random number between 0 and BOMB_SPARCITY
if (bomb_roll == 0)
{
board[row][col] = BOMB_STRING;
}
else
{
board[row][col] = " ";
}
}
}
}

/*
Function to return the bomb count at a particular cell position (i.e., 0 or 1)
If cell position has invalid coordinates, 0 is returned.
@param board solution board with bombs previously placed
@param row row of cell position we want to know about
@param col column of cell position we want to know about
@return 1 or 0, depending on if there is a bomb at the specified position or not (respectively)
*/
int bomb_count_at_row_col(string board[][BOARD_DIMENSION], int row, int col)
{
if (row < 0 || col < 0 || row >= BOARD_DIMENSION || col >= BOARD_DIMENSION)
{
return 0;
}
if (board[row][col] == BOMB_STRING)
{
return 1;
}
else
{
return 0;
}

}

/*
For a particular row and column, this function computes the number of bombs in the
surrounding cell positions, including diagonally
@param board solution board with bombs placed previously
@param row row that we want to calculate the bomb hint for
@param col col that we want to calculate the bomb hint for
@return number of bombs in cells immediately surrounding cell at row, col
*/
int get_hint_for_row_col(string board[][BOARD_DIMENSION], int row, int col)
{
int surround_bombs = 0;

// for each surrounding cell, we add the number of bombs (i.e., 1 or 0) in that cell
surround_bombs += bomb_count_at_row_col(board, row - 1, col - 1); // up left
surround_bombs += bomb_count_at_row_col(board, row - 1, col); // up
surround_bombs += bomb_count_at_row_col(board, row - 1, col + 1); // up right
surround_bombs += bomb_count_at_row_col(board, row + 1, col - 1); // down left
surround_bombs += bomb_count_at_row_col(board, row + 1, col); // down
surround_bombs += bomb_count_at_row_col(board, row + 1, col + 1); // down right
surround_bombs += bomb_count_at_row_col(board, row, col - 1); // left
surround_bombs += bomb_count_at_row_col(board, row, col + 1); // right

return surround_bombs;
}

/*
Fills in the numeric bomb hints on a board with bombs already placed
@param board solution board with bombs already placed
*/
void fill_hints(string board[][BOARD_DIMENSION])
{
// for each row
for (int row = 0; row < BOARD_DIMENSION; row++)
{
// for each column
for (int col = 0; col < BOARD_DIMENSION; col++)
{
// that is not a bomb
if (board[row][col] != BOMB_STRING)
{
// fill in the hint
board[row][col] = to_string(get_hint_for_row_col(board, row, col));
}
}
}
}

/*
Initialize a solution board by randomly placing bombs and then filling in the hints
@param board uninitialized board
*/
void init_solution(string board[][BOARD_DIMENSION])
{

// fill in the hints
fill_hints(board);
}

/*
@param board an arbitrary board (be it solution or guessing)
*/
void display_board(string board[][BOARD_DIMENSION])
{
cout << "  ";
{
cout << row_heading << " ";
}
cout << endl;
for (int row = 0; row < BOARD_DIMENSION; row++)
{
cout << row << " ";
for (int col = 0; col < BOARD_DIMENSION; col++)
{
cout << board[row][col] << " ";
}
cout << endl;
}
}

/*
Initlializes the guessing board by filling it with UNKNOWN_STRING values
@param uninitialized guessing board
*/
void init_guess_board(string board[][BOARD_DIMENSION])
{
for (int row = 0; row < BOARD_DIMENSION; row++)
{
for (int col = 0; col < BOARD_DIMENSION; col++)
{
board[row][col] = UNKNOWN_STRING;
}
}
}

/*
Checks if the guess_board is fully solved, meaning every "safe" spot has been guessed
@param sol_board solution board to consult for the true solution
@param guess_board guess board to check if it is a solution
@return true if every "safe" spot on the guess board has been
*/
bool is_solution(string sol_board[][BOARD_DIMENSION], string guess_board[][BOARD_DIMENSION])
{
// Key is to assume it is a solution
// Then check each cell for a situation which would make it NOT a solution
for (int row = 0; row < BOARD_DIMENSION; row++)
{
for (int col = 0; col < BOARD_DIMENSION; col++)
{
// It's NOT a solution if: this is a safe spot && the spot isn't guessed yet
if (sol_board[row][col] != BOMB_STRING && guess_board[row][col] == UNKNOWN_STRING)
{
// This would have also worked:
// It's NOT a solution if: solution board is a safe spot at row,col && the guess_spot is different from the solution board
// if (sol_board[row][col] != BOMB_STRING&& guess_board[row][col] != sol_board[row][col])
return false;
}
}
}

return true;
}

int main()
{
srand(time(0));
string user_input;

do
{
// initialize the solution board
string solution_board[BOARD_DIMENSION][BOARD_DIMENSION];
init_solution(solution_board);

// initialize the guessing board
string guessing_board[BOARD_DIMENSION][BOARD_DIMENSION];
init_guess_board(guessing_board);

// print the visible board (get board as string)
display_board(guessing_board);

bool solved = false;
bool bombed = false;

// as long as it's not solved and we haven't hit a bomb
while (!solved && !bombed)
{

// prompt the user for coordinates
cout << "Gimme row col (e.g., 0 0):";
int row, col;
cin >> row >> col;

// get the solution value at the coordinates
string solution_value = solution_board[row][col];

// update the guessing board at the coordinates
guessing_board[row][col] = solution_value;

// check if it's a bomb
bombed = (solution_value == BOMB_STRING);

// otherwise check if it's a solution
solved = is_solution(solution_board, guessing_board);

// print the visible board
display_board(guessing_board);

}

display_board(solution_board);

// print success or fail depending on whether or not we hit a bomb
if (bombed)
{
cout << "YOU LOSE" << endl;
}
else
{
cout << "WINNER! WINNER! WINNER!" << endl;
}

cout << "Wanna play again(Y/N)? ";
cin >> user_input;
} while (user_input == "Y");

system("pause");
return 0;
} 