• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
jHaskell

Simple Console based Minesweeper game

5 posts in this topic

Recently I've gotten to thinking about what it would take to implement a simple console based minesweeper game, what data structures and algorithms would be needed for a bare-minimum playable game.  No variations on game size, difficult, etc.  No scoring or timing.  No visual aids for the player beyond revealing appropriate portions of the map.

 

This weekend, I had a few spare hours, and took a crack at such an implementation.  It actually ended up being easier than I had expected (took about an hour and a half total).  Wound up being a bit over a hundred lines of code.  The sources below are actually over 200 lines, but I spent half an hour liberally commenting the code for posting.  I also set myself an additional task of utilizing the STL as much as possible in this mini-project as well, which I think went a long way in keeping the line count down.  I was not intentionally trying to keep line count down though.

 

Hope this is an appropriate place to post this.  I don't have any questions to ask, any problems I need assistance with.  Just wanted to toss this out there for others to check out, if they wish.  I'm certainly open to any comments, criticisms, or questions though.

 

So, without further ado:

#include <iostream>
#include <vector>
#include <queue>
#include <utility>
#include <random>
#include <ctime>

using namespace std;


//matrix is a 2d vector of chars, used for both our map and our mask
typedef vector<vector<char> > matrix;

//below are our minesweeper map params
const int x_dim = 10, y_dim = 10, numMines = 10;

/********************************************
Add a mine to our map
returns true if mine added
false if mine wasn't added (attempted to add a mine to a location that already had one)
also updates proximity values in the map as well
*********************************************/
bool addMine(matrix& map)
{
	//Generate a random location for this mine
	int x = rand() % x_dim, y = rand() % y_dim;

	//Add the mine, if one isn't already there
	if(map[x][y] != '*')
	{
		map[x][y] = '*';
		//walk through all neighboring locations and increment their proximity counts
		for(int dx = x-1; dx <= x+1; dx++)
			for(int dy = y-1; dy <= y+1; dy++)
				if(dx >= 0 && dx < x_dim && dy >= 0 && dy < y_dim)
					if(map[dx][dy] != '*') //Don't update proximity count for mine locations
						if(map[dx][dy] == '.')
							map[dx][dy] = '1'; //initial proximity, set to 1
						else
							map[dx][dy]++; //otherwise, increment current count (works even with chars so long as count never exceeds 9)
		return true;
	}

	//random location already contains a mine
	return false;
}


/*****************************************************************
reveals the specified location of the map/mask
expands areas that are not in proximity to a mine as well
returns true if a mine was revealed (ie game over)
******************************************************************/
bool revealLocation(matrix& map, matrix& mask, int x, int y)
{
	//reveal the location
	mask[x][y] = '.';

	//if location is a mine, game over, just return true
	if(map[x][y] == '*')
		return true;

	//If our location isn't in proximity to a mine
	//we reveal all neighboring locations
	//dots indicate no neighboring mine
	if(map[x][y] == '.')
	{
		//openLocations holds neighboring locations that also
		//are not in proximity to a mine
		queue<pair<int, int> > openLocations;
		openLocations.push(make_pair(x, y));

		//Walk through all dot locations and reveal their neighboars
		while(!openLocations.empty())
		{
			//Get the next location from our queue
			pair<int, int> next = openLocations.front();
			
			//The two for loops iterate over a 3x3 block within our map
			//surrounding the point next.  It will check the point itself
			//as well, which is redundant, but we hardly need highly
			//optimized code here
			for(int dx = next.first-1; dx <= next.first+1; dx++)
			{
				for(int dy = next.second-1; dy <= next.second+1; dy++)
				{
					//Let's make sure the current location is within the
					//bounds of our map.  If next is an edge location, then
					//we'll be iterating over some points outside the map
					//So just ignore those points
					if(dx >= 0 && dx < x_dim && dy >= 0 && dy < y_dim)
					{
						//if this neighbor is a dot location and hasn't
						//previously been revealed, add it to our list
						if(map[dx][dy] == '.' && mask[dx][dy] == '#')
							openLocations.push(make_pair(dx, dy));

						//reveal this neighboring location
						mask[dx][dy] = '.';

					}
				}
			}
			//We're done with the current location in our queue, so we can remove it
			openLocations.pop();
		}
	}

	return false;
}

/******************************************************
returns a count of the remaining masked locations
used to determine if only mines are masked (ie our win condition)
*******************************************************/
int countMask(matrix& mask)
{
	int count = 0;
	for(int x = 0; x < x_dim; x++)
		for(int y = 0; y < y_dim; y++)
			if(mask[x][y] == '#') count++;

	return count;
}

int main()
{
	srand((unsigned int)time(NULL));

	//map contains our mines and proximity values
	//Only unmasked locations of map will be visible though
	//as tracked by our following mask matrix
	matrix map;

	//mask tracks revealed locations
	//# char indicates unrevealed locations
	//. char indicated revealed locations
	matrix mask;


	//Generate our map and mask
	for(int i = 0; i < x_dim; i++)
	{
		map.push_back(vector<char>(y_dim, '.'));
		mask.push_back(vector<char>(y_dim, '#'));
	}
	int mineCount = 0;


	//loop to add mines until we have a full minefield
	do
	{
		if(addMine(map))
			mineCount++;
	}while(mineCount != numMines);


	int x_in, y_in;
	do
	{
		//Display the masked minefield
		//output column indices first
		cout << "  0123456789" << endl;
		cout << "  ----------" << endl;

		//loop through our rows
		for(int x = 0; x < x_dim; x++)
		{
			//output each row index before the row itself
			cout << x << "|";

			//loop through all the columns for this row
			for(int y = 0; y < y_dim; y++)
			{
				//if location is still masked, display mask char
				//otherwise display the underlying map value
				if(mask[x][y] == '#')
					cout << '#';
				else
					cout << map[x][y];
			}
			cout << endl;
		}

		//Wait for user input to reveal a location
		cin >> x_in >> y_in;

		//pass the specified location to our revealLocation function
		//revealLocation returns true if a mine was unmasked
		if(revealLocation(map, mask, x_in, y_in))
		{
			cout << "You set off a mine.  Game over!" << endl;
			break;
		}

		//Count our masked location, if == numMines, then only mines are left
		//and player has won the game
		if(countMask(mask) == numMines)
		{
			cout << "You have found all the mines.  Congratulations!" << endl;
			break;
		}

	}while(1);

	map.clear();
	mask.clear();

	cout << endl;

	system("pause");
	return 0;
}


1

Share this post


Link to post
Share on other sites

I wrote a unix based minesweeper when I was learning C.

 

I see you are calculating the score of neighbouring mines every time you add one. Don't do that, just do it when a tile without a mine is revealed by the player. Another reason to do it that way is that the game cheats if you pick a mine on the first turn and moves it somewhere else ;)

 

I used recursion rather than a queue for opening multiple zero squares. Your way is fine though.

 

EDIT: Everyone on my course moaned at me until I put in a "mark square" ability as well, I used *a4 syntax for that. It was popular in the class everyone used to play it ;) I had variable size and number of bombs too, either as arguments to main (so you could do minesweeper 10 10 20 for a 10 x 10 board with 20 mines), or asked you for the values if you didn't use the command line arguments. If I ran out of letters I went aa, bb, cc, dd, etc. EDIT2: I see you use numbers for both rows and columns though. I allowed a4 or 4a to be valid input, the letters were the rows and the numerals specified columns.

 

I notice you don't do any error checking on the bounds of the input either. You'll want to do that!

Edited by Paradigm Shifter
0

Share this post


Link to post
Share on other sites

I was more interested in just getting the core gameplay implemented as a short exercise.  If I progress with it any further at all, it will be a shift to a full graphical UI.  I've never really been a fan of cin based input error checking.  I find it rather cumbersome, but then I guess that can probably be said about many/most forms of error checking.

 

EDIT:  Now that I think about it more though, I like your idea of using an alphanumeric combination as opposed to a double numeric for rows columns, and I'm thinking I'll get input in a string and play around with regex's for parsing. Regex's may be a bit overkill, but I don't get a lot of real opportunities to use them, and I'm not all that proficient with them as a result.  Plus they're certainly more interesting than checking the state of cin, clearing, and ignoring.

Edited by jHaskell
0

Share this post


Link to post
Share on other sites

Yeah, I would have gone on and done a GUI version if we weren't using a command line based unix system. It's pretty easy to do if you use fixed size button controls for the cells.

0

Share this post


Link to post
Share on other sites

I just copy and pasted your code into MSVC++ to check it out and noticed that you don't clear the screen before updating the screen, an easy way to do this on windows is to call system("cls"); this provides a much more user friendly experience biggrin.png

 

This is where I added it in to your code:

...
//Wait for user input to reveal a location
cin >> x_in >> y_in;
system("cls"); 
...

Cool little project though!

Edited by xDarkShadowKnightx
0

Share this post


Link to post
Share on other sites

EDIT:  Now that I think about it more though, I like your idea of using an alphanumeric combination as opposed to a double numeric for rows columns, and I'm thinking I'll get input in a string and play around with regex's for parsing. Regex's may be a bit overkill, but I don't get a lot of real opportunities to use them, and I'm not all that proficient with them as a result.  Plus they're certainly more interesting than checking the state of cin, clearing, and ignoring.

 

I used the power of sscanf I think ;) It was C rather than C++ though.

0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0