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

Flow chart

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
*/
void add_bombs(string board[][BOARD_DIMENSION])
{
	// 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])
{
	// randomly add bombs
	add_bombs(board);
 
	// fill in the hints
	fill_hints(board);
}
 
/*
	Display an arbitrary game board with row heading and column heading
	@param board an arbitrary board (be it solution or guessing)
*/
void display_board(string board[][BOARD_DIMENSION])
{
	cout << "  ";
	for (int row_heading = 0; row_heading < BOARD_DIMENSION; row_heading++)
	{
		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;
}
cs-142/minesweeper.txt · Last modified: 2015/05/26 17:49 by cs142ta
Back to top
CC Attribution-Share Alike 4.0 International
chimeric.de = chi`s home Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0