Struggling with C++ Minesweeper Game

Pages: 1234
Deleted to make space
Last edited on
When I did a program similar to this, I had a function who's sole purpose was displaying the board after each update made to it. This made debugging much easier. Right now, I'm having trouble even finding the part of your code that is in charge of displaying your board.
I agree with you I have missed the board display out, tried putting cout<<rows<<columns<<endl; but that doesnt change the display of the board?

Sorry about this I promise once I get the hang of this I will lay my code out correctly.
Last edited on
Well think about this way, you know the exact size your board is. And you know that each piece of the board only holds one char of info. No matter what happens in a game of minesweeper, the board will always be the same size and each piece will display one char. Do you think you could devise a function that handles this?
closed account (o3hC5Di1)
@ResidentBiscuit - thanks for your input on the double board thing. I aggree it would be good practice, but I'm quite a fan of (if struggling) stripping thins down to bare minimums and going from there. As for your remark on the userinput, in the most recent code posted lines 38/39:

1
2
cout<<"Enter M to make move, B to mark as bomb"<<endl;
cin>>userchoice;


@hillkidstr - I'll have a quick look at your code again and see if I can clean it up a bit for you and comment it so it will make more sense to you.
closed account (o3hC5Di1)
Hi there,

So I looked at your code and stripped out some unnecessary things.
Also, I put the displaying of the board and the actual playing in separate functions, this way they can be re-used for different levels without having to copy all the code.

The Beginner level is working now.

Have a look at the comments and see if you understand them.

Let us know if you have any questions.

All the best,
NwN


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149

// Minesweeper 4.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
using namespace std;

/* we take out the functionality of displaying the board
** this way we can display beginner/intermediate/... boards
** with just this function.*/
void display_board(char** board, int rows, int columns)
{
    for (rows=0; rows<8; rows++) 
    {
        for (columns=0; columns<8; columns++)
        {
            if (board[rows][columns] == 'e')
            { cout<<"- ";} //dash if guessed and no bomb
            else if (board[rows][columns] == 'x')
            {cout<<"X ";} // X marks a marked bomb
            else
            { cout<<". ";} //point if not guessed, or if bomb
        }
        
        cout<<endl;
    }
}

/* we take out the functionality of playing i.e. marking fields,
** again so that this can apply to all levels */
void play_board(char** board, int rows, int columns, int mines)
{
    int guessed_mines = 0; //keep track of number of correctly guessed mines
    int X, Y;   //user specified X/Y coordinates
    char userchoice;
    
    //we need to do the following in a loop until either the player hits a bomb
    //or the player finds the mines and wins the game
    do
    {
        display_board(board, rows, columns);

        cout<<"Enter X Co-ordinate (>=0)"<<endl;
    	cin>>X;
    	cout<<"Enter Y Co-ordinate (>=0)"<<endl;
    	cin>>Y;
    	cout<<"Enter M to make move, B to mark as bomb"<<endl;
    	cin>>userchoice;
    	if (Y>columns||X>rows)
    	    cout << "You have entered the wrong Co-ordinates, Please Retry" << endl;

        //if user choses Move and hits a bomb, game ends
	    if (userchoice=='M' && board[X][Y] == 'b')
	    {
            cout << "Boom!";
            break;
        }
        //if user choses mark Bomb on a bomb field, set value to x
	    else if (userchoice=='B' && board[X][Y] == 'b') 
	    {
            board[X][Y] = 'x';
            guessed_mines++;
        }         
        //possibly you may want to add a check when user marks bomb but field is empty.
	    //if field is empty and not already marked as bomb, mark as [e]mpty.
        else
	    {
            if (board[X][Y] != 'x') board[X][Y] = 'e';
        }
    } while (guessed_mines < mines);
    
    //if the number of guessed mines is indeed the number of mines in the board, win
    if (guessed_mines == mines)
    {
        cout << "You won!" << endl;
        //you can insert a question here like "play again? next level?"
    }
}

void beginner() { //Beginner

    int rows=9, columns=9;

    //declare and fill the board matrix, can't use regular array as we're passing
    //this to functions, don't worry about it now, just know this is an ugly quickfix.
	char** beginnerboard = new char *[rows];  
    for (int i=0;i<rows;i++)
    {
        beginnerboard[i] = new char[columns];
    }
    //assign bombs by putting the letter "b" in certain fields of our board
	beginnerboard[2][3] = 'b';
	beginnerboard[4][1] = 'b';
	beginnerboard[1][1] = 'b';
	beginnerboard[5][3] = 'b';
    
    //play the game
    play_board(beginnerboard, rows, columns, 4);
    system ("pause");
    delete[] beginnerboard;
}

void leaguetable() //Table
{
	system("cls");
	cout << "This is the League Table" << endl;
	system ("pause");
}

void displayMenu() //Menu
{
        char choice;

        system("cls");
        cout << "Welcome to Minesweeper" << endl;
        cout << "1.Beginner" << endl;
        cout << "2.Intermediate" << endl;
        cout << "3.Expert" << endl;
        cout << "4.League Table" << endl;
        cout << "5.Exit" << endl;
        cout << "Please enter your menu choice: ";
        cin >> choice;

        switch (choice)
        { 
                case '1' : 
                    beginner(); 
                    break;
                case '2' : break;
                case '3' : break;
                case '4' : leaguetable();
                           break;
                case '5' : cout << "Thanks for playing minesweeper" << endl;
                           break;

                default :
                       cout << "Please enter a value between 1 and 5" << endl;
                       system ("pause");
                          
        }
}

int main()
{
    displayMenu();
    system("pause");
    return 0;
}
Last edited on
I cant thank you enough NwN, I read through everything that you put and it now makes sense :D as i think I was struggling to work out what parts of my code did what.

Right so to add a counter with a score I would do an if statement within the user choice with something like,

if (userchoice=='m' && board[X][Y] == '??')
{
counter++
}

something along them lines?

and then to get a league table would I ask the user for a name and save the score and name within a file that I call on when entering the league table?

thanks for all this.
closed account (o3hC5Di1)
Hi there,

You're most welcome - it's helping me learn too. :)

hillkidstr wrote:

Right so to add a counter with a score I would do an if statement within the user choice with something like,

if (userchoice=='m' && board[X][Y] == '??')
{
counter++
}


Well you would first have to think about how you want to implement scoring.
Do you want a point assigned for every correctly guessed mine, for every solved level, ... that will determine where the score-counter will go.

hillkidstr wrote:

and then to get a league table would I ask the user for a name and save the score and name within a file that I call on when entering the league table?


Yes indeed, now maybe that's a good exercise for you to put that part in a separate function and call it at the right moment.

Let us know if you need any further help.

All the best,
NwN
Dont go just yet NwN lol

Right would it be at all possible to do the following, for every empty square guessed 1 point is added to the score and for every correctly guessed mine 10 points is added?

Also how would I go about adding numbers to the X and Y axis?

Also sorry for being a pain is there any possibility of added visual lines to represent a grid?

Thanks :D
closed account (o3hC5Di1)
Let's take it one step at a time - functionality before graphics.

All right, let's think about your scoring system.

First we need to define a variable that keeps the score.
This variable need to be accessible by all the functions and throughout all the levels, so where would we best declare it?

Once the variable is declared in the right spot, we can increment it.
Where are the "empty square guessed" and "correctly guessed mine" parts?

NwN
Thanks :D

Right I think that the variable should be declared within the play board, as this is where it takes in the userchoice to determine the correct output,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (userchoice=='m' && board[X][Y] == 'b')
	    {
            cout << "Boom!";
            break;
        }
        //if user choses mark Bomb on a bomb field, set value to x
	    else if (userchoice=='b' && board[X][Y] == 'b') 
	    {
            board[X][Y] = 'x';
            guessed_mines++;
        }         
        //possibly you may want to add a check when user marks bomb but field is empty.
	    //if field is empty and not already marked as bomb, mark as [e]mpty.
        else
	    {
            if (board[X][Y] != 'x') board[X][Y] = 'e';
        }
    } while (guessed_mines < mines);
    


within the above code is where it determines whether the square contains a mine or is blank so an additional if statement would need to be added here , something like,

if userchoice = e then + 1 else if x then + 10?

sorry I cant code it properly but I think I am understanding this a lot more (or at least i think I am)
closed account (o3hC5Di1)
Hi,

you're getting close!

Declaring a variable means telling the program "ok program, listen up! i want you to create a space in the computer memory, which will contain data of <datatype>, so it can be used to store data, pronto!"

In code this is:

1
2
//declare variable "score" as of type "integer" (meaning a number) and assign value 0 to it for now
int score = 0;


Now, the best place to declare the variable (for this project) would be outside of any function. doing this it will be in global scope, which means it can be accessed from within any function.

So for instance, at the top of your file, under using namespace std; you could declare your score variable.


Now, as for increasing the score variable, you can use following code:
score += 1; //or score += 10; which is the same as saying score = score + 1; //or score = score + 10; the often used shortcut for score +=1; is ++score;.

Knowing these things (and perhaps having checked some info on them) try and implement the scores where you think they are right :)

All the best,

NwN
Deleted to make space
Last edited on
closed account (o3hC5Di1)
Hi there,

Looks good, well done!

For randomizing the mines it would be best to make another function, again so you can reuse that at every level regardless of what level you are at.

I'll give you the function declaration and steps to use to get you started:

1
2
3
4
5
6
7
void set_mines(char** board, int columns, int rows, int mines)
{ 
    /* pseudocode
    ** while some counter variable is smaller than the amount of mines
    ** make up a random X and Y, between 0 and columns / rows respectively
    ** if the random coordinate is not already assigned a bomb then place it
} */


The functions you will need for the random number generating are following:

http://cplusplus.com/reference/clibrary/cstdlib/srand/
http://cplusplus.com/reference/clibrary/cstdlib/rand/

See what you can come up with :)

All the best,
NwN
Right I have come up with something that doesn't run due to redefinition of formal parameter 'mines', this is what I have added to the code.

Thanks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Minesweeper 4.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
using namespace std;

int score = 0;
int X,Y;

/* we take out the functionality of displaying the board
** this way we can display beginner/intermediate/... boards
** with just this function.*/
void display_board(char** board, int rows, int columns)
{
    for (rows=0; rows<8; rows++) 
    {
        for (columns=0; columns<8; columns++)
        {
            if (board[rows][columns] == 'e')
            { cout<<"- ";} //dash if guessed and no bomb
            else if (board[rows][columns] == 'x')
            {cout<<"X ";} // X marks a marked bomb
            else
            { cout<<". ";} //point if not guessed, or if bomb
        }
        
        cout<<endl;
    }
}

/* we take out the functionality of playing i.e. marking fields,
** again so that this can apply to all levels */
void play_board(char** board, int rows, int columns, int mines)
{
    int guessed_mines = 0; //keep track of number of correctly guessed mines
    int X, Y;   //user specified X/Y coordinates
    char userchoice;
    
    //we need to do the following in a loop until either the player hits a bomb
    //or the player finds the mines and wins the game
    do
    {
        display_board(board, rows, columns);

        cout<<"Enter X Co-ordinate (1-8)"<<endl;
    	cin>>X;
    	cout<<"Enter Y Co-ordinate (1-8)"<<endl;
    	cin>>Y;
    	cout<<"Enter M to make move, B to mark as bomb"<<endl;
    	cin>>userchoice;
    	if (Y>columns||X>rows)
    	    cout << "You have entered the wrong Co-ordinates, Please Retry" << endl;

        //if user choses Move and hits a bomb, game ends
	    if (userchoice=='m' && board[X][Y] == 'b')
	    {
            cout << "Boom! Sorry, you lost this game. Better luck next time!" <<endl;
			cout << "Your total score for this game is : ";
            break;
        }
        //if user choses mark Bomb on a bomb field, set value to x
	    else if (userchoice=='b' && board[X][Y] == 'b') 
	    {
            board[X][Y] = 'x';
            guessed_mines++;
			score = score + 10;
        }         
        //possibly you may want to add a check when user marks bomb but field is empty.
	    //if field is empty and not already marked as bomb, mark as [e]mpty.
        else
	    {
            if (board[X][Y] != 'x') board[X][Y] = 'e';
			++score;
        }
    } while (guessed_mines < mines);
    
    //if the number of guessed mines is indeed the number of mines in the board, win
    if (guessed_mines == mines)
    {
        cout << "You won!" << endl;
        //you can insert a question here like "play again? next level?"
    }
}

void set_mines(char** board, int columns, int rows, int mines)
{ 
    int mines;
do
	{
		do
		{
			X = rand() %8; //I think this gets a random X axis number
			Y = rand() %8; //Random Y axis number
		}while (board[X][Y] !=0 );
		board[X][Y] = 1; // Plants a mine?
		mines++; // increases number of mines
	}while (mines < 10); // repeat until 10 mines?
}

void beginner() { //Beginner

    int rows=9, columns=9;

	cout << "You have selected to play the Beginner level of minesweeper, Good Luck!" << endl;
	//declare and fill the board matrix, can't use regular array as we're passing
    //this to functions, don't worry about it now, just know this is an ugly quickfix.
	char** beginnerboard = new char *[rows];  
    for (int i=0;i<rows;i++)
    {
        beginnerboard[i] = new char[columns];
    }
    //assign bombs by putting the letter "b" in certain fields of our board
	
	cout<<set_mines;
    
    //play the game
    play_board(beginnerboard, rows, columns, 4);
	cout<<score<<endl;
    system ("pause");
    delete[] beginnerboard;
}

void leaguetable() //Table
{
	system("cls");
	cout << "This is the League Table" << endl;
	system ("pause");
}

void displayMenu() //Menu
{
        char choice;

        system("cls");
        cout << "Welcome to Minesweeper" << endl;
        cout << "1.Beginner" << endl;
        cout << "2.Intermediate" << endl;
        cout << "3.Expert" << endl;
        cout << "4.League Table" << endl;
        cout << "5.Exit" << endl;
        cout << "Please enter your menu choice: ";
        cin >> choice;

        switch (choice)
        { 
                case '1' : 
                    beginner(); 
                    break;
                case '2' : break;
                case '3' : break;
                case '4' : leaguetable();
                           break;
                case '5' : cout << "Thanks for playing minesweeper" << endl;
                           break;

                default :
                       cout << "Please enter a value between 1 and 5" << endl;
                       system ("pause");
                          
        }
}

int main()
{
    displayMenu();
    system("pause");
    return 0;
}
closed account (o3hC5Di1)
Hello there,

Very good attempt, here's your code corrected with comments so you can understand.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void set_mines(char** board, int columns, int rows, int mines)
{ 
    /* mines was already declared in the function definition ^
    **so no need for it to be declared again, this way we can pass how
    **how many mines are set to the board regardless of which level we're at
    ** however, you do want a counter for the mines*/
    int mines;
    int minecounter = 0;
    do
	{
		/* nearly correct, but you'll need to "seed" the rand function
                 ** by using srand(time()); somewhere. You can best do this 
                 ** once in the main function. Also, to make the function work 
                 ** regardless of level, you should use %rows and %columns
                 ** in stead of the constants you used */
                 X = rand() %8; //I think this gets a random X axis number
		 Y = rand() %8; //Random Y axis number
                 
                 //if the coordinate was not by chance already made into a bomb
                 if (board[X][Y] != 'b')
                 {
		     board[X][Y] = 1; // Plants a mine? - 'b' is a mine, not 1
                     minecounter++;
                 }
	} while (minecounter < mines); // repeat until 10 mines? - repeat until number of mines is reached.
}
...

int main()
{
    srand(time()); // for this you will need to #include <ctime>
    ...
}


Hope that makes sense to you, again you got the general idea, just some small implementation issues.

All the best,
NwN
Last edited on
I cant thank you enough :D

The only issue I am having is it is saying too few arguments in function call?
I have underlined it and made it bold.

Also I understand what you have written but the one part I cant work out is how do I declare the total number of mines to generate?

Thanks

1
2
3
4
5
6
7
int main() 
{
    srand(time());
    displayMenu();
    system("pause");
    return 0;
}
Last edited on
closed account (o3hC5Di1)
Hi,

Sorry, that should be srand(time(NULL)) , my bad.

hillkidstr wrote:
the one part I cant work out is how do I declare the total number of mines to generate?


You already have more or less, this is the function declaration of play_board:
void play_board(char** board, int rows, int columns, int mines)

You call this in the function beginner as such:
play_board(beginnerboard, rows, columns, 4);

So 4 is your number of mines for the beginner level.

You could make that more transparent by changing line 103 to:
int rows=9, columns=9, mines=4; //level parameters

And consequently calling the function as such:
play_board(beginnerboard, rows, columns, mines);


Hope that makes sense?

All the best,
NwN
Last edited on
Thanks NwN it does make sense the problem I have now is that when I start the beginners level with 10 mines I get 00D112D0 and then my grid?

Up on revealing all squares there are no mines?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// Minesweeper 4.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <ctime>
using namespace std;

int score = 0;
int X,Y;

/* we take out the functionality of displaying the board
** this way we can display beginner/intermediate/... boards
** with just this function.*/
void display_board(char** board, int rows, int columns)
{
    for (rows=0; rows<8; rows++) 
    {
        for (columns=0; columns<8; columns++)
        {
            if (board[rows][columns] == 'e')
            { cout<<"- ";} //dash if guessed and no bomb
            else if (board[rows][columns] == 'x')
            {cout<<"X ";} // X marks a marked bomb
            else
            { cout<<". ";} //point if not guessed, or if bomb
        }
        
        cout<<endl;
    }
}

/* we take out the functionality of playing i.e. marking fields,
** again so that this can apply to all levels */
void play_board(char** board, int rows, int columns, int mines)
{
    int guessed_mines = 0; //keep track of number of correctly guessed mines
    int X, Y;   //user specified X/Y coordinates
    char userchoice;
    
    //we need to do the following in a loop until either the player hits a bomb
    //or the player finds the mines and wins the game
    do
    {
        display_board(board, rows, columns);

        cout<<"Enter X Co-ordinate (1-8)"<<endl;
    	cin>>X;
    	cout<<"Enter Y Co-ordinate (1-8)"<<endl;
    	cin>>Y;
    	cout<<"Enter M to make move, B to mark as bomb"<<endl;
    	cin>>userchoice;
    	if (Y>columns||X>rows)
    	    cout << "You have entered the wrong Co-ordinates, Please Retry" << endl;

        //if user choses Move and hits a bomb, game ends
	    if (userchoice=='m' && board[X][Y] == 'b')
	    {
            cout << "Boom! Sorry, you lost this game. Better luck next time!" <<endl;
			cout << "Your total score for this game is : ";
            break;
        }
        //if user choses mark Bomb on a bomb field, set value to x
	    else if (userchoice=='b' && board[X][Y] == 'b') 
	    {
            board[X][Y] = 'x';
            guessed_mines++;
			score = score + 10;
        }         
        //possibly you may want to add a check when user marks bomb but field is empty.
	    //if field is empty and not already marked as bomb, mark as [e]mpty.
        else
	    {
            if (board[X][Y] != 'x') board[X][Y] = 'e';
			++score;
        }
    } while (guessed_mines < mines);
    
    //if the number of guessed mines is indeed the number of mines in the board, win
    if (guessed_mines == mines)
    {
        cout << "You won!" << endl;
        //you can insert a question here like "play again? next level?"
    }
}


void set_mines(char** board, int columns, int rows, int mines)
{ 
    int minecounter = 0;
	do
	{
		if (board[X][Y] !='b')
		{
			minecounter++;
		}
	} while (minecounter < mines);
}



void beginner() { //Beginner

    int rows=9, columns=9, mines=10;

	cout << "You have selected to play the Beginner level of minesweeper, Good Luck!" << endl;
	//declare and fill the board matrix, can't use regular array as we're passing
    //this to functions, don't worry about it now, just know this is an ugly quickfix.
	char** beginnerboard = new char *[rows];  
    for (int i=0;i<rows;i++)
    {
        beginnerboard[i] = new char[columns];
    }
    //assign bombs by putting the letter "b" in certain fields of our board
	
	cout<<set_mines;
    
    //play the game
    play_board(beginnerboard, rows, columns, mines);
	cout<<score<<endl;
    system ("pause");
    delete[] beginnerboard;
}

void leaguetable() //Table
{
	system("cls");
	cout << "This is the League Table" << endl;
	system ("pause");
}

void displayMenu() //Menu
{
        char choice;

        system("cls");
        cout << "Welcome to Minesweeper" << endl;
        cout << "1.Beginner" << endl;
        cout << "2.Intermediate" << endl;
        cout << "3.Expert" << endl;
        cout << "4.League Table" << endl;
        cout << "5.Exit" << endl;
        cout << "Please enter your menu choice: ";
        cin >> choice;

        switch (choice)
        { 
                case '1' : 
                    beginner(); 
                    break;
                case '2' : break;
                case '3' : break;
                case '4' : leaguetable();
                           break;
                case '5' : cout << "Thanks for playing minesweeper" << endl;
                           break;

                default :
                       cout << "Please enter a value between 1 and 5" << endl;
                       system ("pause");
                          
        }
}


int main() 
{
	srand(time(NULL));
    displayMenu();
    system("pause");
    return 0;
}
closed account (o3hC5Di1)
Hi there,

The function I sent you in PM contained a comment about what to change, i should have made that more clear, sorry.

Here's what to do:

1
2
3
4
5
6
7
8
9
10
11
12
13
void set_mines(char** board, int columns, int rows, int mines)
{ 
    int minecounter = 0;
	do
	{
		if (board[X][Y] !='b')
		{
                        //have to set the field to 'b' here :)
                        board[X][Y] = 'b';
			minecounter++;
		}
	} while (minecounter < mines);
}


Your previous code did not actually place any mines ('b'), but did increase the mine counter, so after it had generated 10 random coordinates it thought the job was done, but off course the board needs mines for the game to work.

Let me know if there are any other issues or if there's anything you don't quite understand.

All the best,
NwN
Pages: 1234