Minesweeper Solver - GeeksforGeeks

28 Sep, 2021
Given a 2D array arr[][] of dimensions N*M, representing a minesweeper matrix, where each cell contains an integer from the range [0, 9], representing the number of mines in itself and all the eight cells adjacent to it, the task is to solve the minesweeper and uncover all the mines in the matrix. Print ‘X’ for the cell containing a mine and ‘_’ for all the other empty cells. If it is not possible to solve the minesweeper, then print “-1”.
Examples:
Input:arr[][] = {{1, 1, 0, 0, 1, 1, 1},                {2, 3, 2, 1, 1, 2, 2},                {3, 5, 3, 2, 1, 2, 2},                {3, 6, 5, 3, 0, 2, 2},                {2, 4, 3, 2, 0, 1, 1},                {2, 3, 3, 2, 1, 2, 1},                {1, 1, 1, 1, 1, 1, 0}}.Output:_ _ _ _ _ _ _ x _ _ _ _ x _ _ x x _ _ _ x  x _ x _ _ _ _ _ x x _ _ _ x _ _ _ _ _ _ _ _ x _ _ x _ _
Input:arr[][] = {{0, 0, 0, 0, 0, 0, 0},                {0, 0, 0, 0, 0, 1, 1},                {0, 0, 0, 0, 0, 1, 1},                {0, 0, 1, 1, 1, 1, 1},                {0, 0, 2, 2, 2, 0, 0},                {0, 0, 2, 2, 2, 0, 0},                {0, 0, 1, 1, 1, 0, 0}}Output:_ _ _ _ _ _ __ _ _ _ _ _ __ _ _ _ _ _ x_ _ _ _ _ _ __ _ _ x _ _ _ _ _ _ x _ _ __ _ _ _ _ _ _
Input Generation: To solve the given minesweeper matrix arr[][], it must be a valid input i.e., the minesweeper matrix must be solvable. Therefore, the input matrix is generated in generateMineField() function. Follow the below steps to generate the input minesweeper matrix:
The inputs for the generation of input are the size of the minefield N and M and also probability P (or density) of the minefield.
A probability P is equal to 0 if there are no mines in the minefield and P is equal to 100 if all the cells are mines in the minefield.
A random number is chosen for each cell and if the random number is less than P, a mine is assigned to the grid and a boolean array mines[][] is generated.
The input to the constraint solver is the status of each grid which counts the mines of itself and the eight cells around it.
Below is the implementation of the above approach:
C++
// C++ program for the above approach#include <bits/stdc++.h>using namespace std; // Stores the number of rows// and columns of the matrixint N, M; // Stores the final generated inputint arr[100][100]; // Direction arraysint dx[9] = { -1, 0, 1, -1, 0, 1, -1, 0, 1 };int dy[9] = { 0, 0, 0, -1, -1, -1, 1, 1, 1 }; // Function to check if the// cell location is validbool isValid(int x, int y){    // Returns true if valid    return (x >= 0 && y >= 0            && x < N && y < M);} // Function to generate a valid minesweeper// matrix of size ROW * COL with P being// the probability of a cell being a minevoid generateMineField(int ROW, int COL, int P){    // Generates the random    // number every time    srand(time(NULL));     int rand_val;     // Stores whether a cell    // contains a mine or not    int mines[ROW][COL];     // Iterate through each cell    // of the matrix mine    for (int x = 0; x < ROW; x++) {        for (int y = 0; y < COL; y++) {            // Generate a random value            // from the range [0, 100]            rand_val = rand() % 100;             // If rand_val is less than P            if (rand_val < P)                 // MArk mines[x][y] as True                mines[x][y] = true;             // Otherwise, mark            // mines[x][y] as False            else                mines[x][y] = false;        }    }     cout << "Generated Input:\n";     // Iterate through each cell (x, y)    for (int x = 0; x < ROW; x++) {        for (int y = 0; y < COL; y++) {            arr[x][y] = 0;             // Count the number of mines            // around the cell (x, y)            // and store in arr[x][y]            for (int k = 0; k < 9; k++) {                // If current adjacent cell is valid                if (isValid(x + dx[k], y + dy[k])                    && (mines[x + dx[k]][y + dy[k]]))                    arr[x][y]++;            }             // Print the value at            // the current cell            cout << arr[x][y] << " ";        }        cout << endl;    }} // Driver Codeint main(){    N = 7, M = 7;    int P = 20;     // Function call to generate    // a valid minesweeper matrix    generateMineField(N, M, 15);}
Generated Input:
0 1 1 1 1 1 1 
1 3 3 3 2 2 1 
1 2 2 2 2 2 1 
1 2 2 2 1 1 0 
1 2 1 1 1 1 1 
1 3 2 2 1 1 1 
1 3 2 2 1 1 1
 
Approach: The given problem can be solved using Backtracking. The idea is to iterate over each cell of a matrix, based on the information available from the neighboring cells, assign a mine to that cell or not. 
Follow the below steps to solve the given problem:
Initialize a matrix, say grid[][], and visited[][] to store the resultant grid and keep the track of visited cells while traversing the grid. Initialize all grid values as false.
Declare a recursive function solveMineSweeper() to accept arrays arr[][], grid[][], and visited[][] as a parameter.If all the cells are visited and a mine is assigned to the cells satisfying the given input grid[][], then return true for the current recursive call.If all the cells are visited but the solution is not satisfying the input grid[], return false for the current recursive call.If the above two conditions are found to be false, then find an unvisited cell (x, y) and mark (x, y) as visited.If a mine can be assigned to the position (x, y), then perform the following steps:Mark grid[x][y] as true.Decrease the number of mine of the neighboring cells of (x, y) in the matrix arr[][] by 1.Recursively call for solveMineSweeper() with (x, y) having a mine and if it returns true, then a solution exists. Return true for the current recursive call.Otherwise, reset the position (x, y) i.e., mark grid[x][y] as false and increase the number of mines of the neighboring cells of (x, y) in the matrix arr[][] by 1.If the function solveMineSweeper() with (x, y) having no mine, returns true, then it means a solution exists. Return true from the current recursive call.If the recursive call in the above step returns false, that means the solution doesn’t exist. Therefore, return false from the current recursive calls.
If all the cells are visited and a mine is assigned to the cells satisfying the given input grid[][], then return true for the current recursive call.
If all the cells are visited but the solution is not satisfying the input grid[], return false for the current recursive call.
If the above two conditions are found to be false, then find an unvisited cell (x, y) and mark (x, y) as visited.
If a mine can be assigned to the position (x, y), then perform the following steps:Mark grid[x][y] as true.Decrease the number of mine of the neighboring cells of (x, y) in the matrix arr[][] by 1.Recursively call for solveMineSweeper() with (x, y) having a mine and if it returns true, then a solution exists. Return true for the current recursive call.Otherwise, reset the position (x, y) i.e., mark grid[x][y] as false and increase the number of mines of the neighboring cells of (x, y) in the matrix arr[][] by 1.
Mark grid[x][y] as true.
Decrease the number of mine of the neighboring cells of (x, y) in the matrix arr[][] by 1.
Recursively call for solveMineSweeper() with (x, y) having a mine and if it returns true, then a solution exists. Return true for the current recursive call.
Otherwise, reset the position (x, y) i.e., mark grid[x][y] as false and increase the number of mines of the neighboring cells of (x, y) in the matrix arr[][] by 1.
If the function solveMineSweeper() with (x, y) having no mine, returns true, then it means a solution exists. Return true from the current recursive call.
If the recursive call in the above step returns false, that means the solution doesn’t exist. Therefore, return false from the current recursive calls.
If the value returned by the function solveMineSweeper(grid, arr, visited) is true, then a solution exists. Print the matrix grid[][] as the required solution. Otherwise, print “-1”.
Below is the implementation of the above approach:  
C++
// C++ program for the above approach#include <bits/stdc++.h>using namespace std; // Stores the number of rows// and columns in given matrixint N, M; // Maximum number of rows// and columns possible#define MAXM 100#define MAXN 100 // Directional Arraysint dx[9] = { -1, 0, 1, -1, 0, 1, -1, 0, 1 };int dy[9] = { 0, 0, 0, -1, -1, -1, 1, 1, 1 }; // Function to check if the// cell (x, y) is valid or notbool isValid(int x, int y){    return (x >= 0 && y >= 0            && x < N && y < M);} // Function to print the matrix grid[][]void printGrid(bool grid[MAXN][MAXM]){    for (int row = 0; row < N; row++) {        for (int col = 0; col < M; col++) {            if (grid[row][col])                cout << "x ";            else                cout << "_ ";        }        cout << endl;    }} // Function to check if the cell (x, y)// is valid to have a mine or notbool isSafe(int arr[MAXN][MAXM], int x, int y){     // Check if the cell (x, y) is a    // valid cell or not    if (!isValid(x, y))        return false;     // Check if any of the neighbouring cell    // of (x, y) supports (x, y) to have a mine    for (int i = 0; i < 9; i++) {         if (isValid(x + dx[i], y + dy[i])            && (arr[x + dx[i]][y + dy[i]] - 1 < 0))            return (false);    }     // If (x, y) is valid to have a mine    for (int i = 0; i < 9; i++) {        if (isValid(x + dx[i], y + dy[i]))             // Reduce count of mines in            // the neighboring cells            arr[x + dx[i]][y + dy[i]]--;    }     return true;} // Function to check if there// exists any unvisited cell or notbool findUnvisited(bool visited[MAXN][MAXM],                   int& x, int& y){    for (x = 0; x < N; x++)        for (y = 0; y < M; y++)            if (!visited[x][y])                return (true);    return (false);} // Function to check if all the cells// are visited or not and the input array// is satisfied with the mine assignmentsbool isDone(int arr[MAXN][MAXM],            bool visited[MAXN][MAXM]){    bool done = true;    for (int i = 0; i < N; i++) {        for (int j = 0; j < M; j++) {            done                = done && (arr[i][j] == 0)                  && visited[i][j];        }    }     return (done);} // Function to solve the minesweeper matrixbool SolveMinesweeper(bool grid[MAXN][MAXM],                      int arr[MAXN][MAXM],                      bool visited[MAXN][MAXM]){     // Function call to check if each cell    // is visited and the solved grid is    // satisfying the given input matrix    bool done = isDone(arr, visited);     // If the solution exists and    // and all cells are visited    if (done)        return true;     int x, y;     // Function call to check if all    // the cells are visited or not    if (!findUnvisited(visited, x, y))        return false;     // Mark cell (x, y) as visited    visited[x][y] = true;     // Function call to check if it is    // safe to assign a mine at (x, y)    if (isSafe(arr, x, y)) {         // Mark the position with a mine        grid[x][y] = true;         // Recursive call with (x, y) having a mine        if (SolveMinesweeper(grid, arr, visited))             // If solution exists, then return true            return true;         // Reset the position x, y        grid[x][y] = false;        for (int i = 0; i < 9; i++) {            if (isValid(x + dx[i], y + dy[i]))                arr[x + dx[i]][y + dy[i]]++;        }    }     // Recursive call without (x, y) having a mine    if (SolveMinesweeper(grid, arr, visited))         // If solution exists then return true        return true;     // Mark the position as unvisited again    visited[x][y] = false;     // If no solution exists    return false;} void minesweeperOperations(int arr[MAXN][MAXN], int N,                           int M){     // Stores the final result    bool grid[MAXN][MAXM];     // Stores whether the position    // (x, y) is visited or not    bool visited[MAXN][MAXM];     // Initialize grid[][] and    // visited[][] to false    memset(grid, false, sizeof(grid));    memset(visited, false, sizeof(visited));     // If the solution to the input    // minesweeper matrix exists    if (SolveMinesweeper(grid, arr, visited)) {         // Function call to print the grid[][]        printGrid(grid);    }     // No solution exists    else        printf("No solution exists\n");} // Driver Codeint main(){    // Given input    N = 7;    M = 7;    int arr[MAXN][MAXN] = {        { 1, 1, 0, 0, 1, 1, 1 },        { 2, 3, 2, 1, 1, 2, 2 },        { 3, 5, 3, 2, 1, 2, 2 },        { 3, 6, 5, 3, 0, 2, 2 },        { 2, 4, 3, 2, 0, 1, 1 },        { 2, 3, 3, 2, 1, 2, 1 },        { 1, 1, 1, 1, 1, 1, 0 }    };     // Function call to perform    // generate and solve a minesweeper    minesweeperOperations(arr, N, M);     return 0;}
_ _ _ _ _ _ _ 
x _ _ _ _ x _ 
_ x x _ _ _ x 
x _ x _ _ _ _ 
_ x x _ _ _ x 
_ _ _ _ _ _ _ 
_ x _ _ x _ _
 
Time Complexity: O(2N * M * N * M)Auxiliary Space: O(N * M)
 
adnanirshad158
arorakashish0911
BFS
Technical Scripter 2020
Backtracking
Game Theory
Graph
Matrix
Technical Scripter
Matrix
Game Theory
Graph
Backtracking
BFS
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Comments
Old Comments
Tug of War
Difference between Backtracking and Branch-N-Bound technique
Perfect Sum Problem
N-Queen Problem | Local Search using Hill climbing with random neighbour
Find shortest safe route in a path with landmines
Minimax Algorithm in Game Theory | Set 3 (Tic-Tac-Toe AI - Finding optimal move)
Minimax Algorithm in Game Theory | Set 1 (Introduction)
Minimax Algorithm in Game Theory | Set 4 (Alpha-Beta Pruning)
Implementation of Tic-Tac-Toe game
Optimal Strategy for a Game | DP-31