C language implementation of mine sweeping (including expansion, with source code)

I believe that everyone who is doing it has played minesweeper before, so I won't go into details about the gameplay.


Go straight to the topic: think first, then code! !

First, I divided minesweeper into two boards, one for mines and the other for players to guess.

This is what some students asked, setting up a chessboard is not over, so it will make it complicated?

To briefly answer the student's question:

Because my thinking is like this, I use '1' to represent mines, and '0' to represent no mines. If there are multiple 1's in a disk, it is impossible to determine whether it is a mine or one of the surrounding 8 grids. There is a thunder.

Further explanation

  Image reference:                      

When the mine is 1, and the record number is also 1 , the following yellow mark position is an example:         

                                         

At this point, click on the yellow position, then the number it displays is 2 instead of 1, and we will find that the thunder'' has become more''.

But another classmate wants to ask, why do you have to use 1 to represent thunder and 0 to represent no thunder? I use ¥ to represent thunder, and @ to represent non-lei, so this situation will not happen.

In fact, there is no problem with this arrangement. I also encourage everyone to try it, but the benefits of splitting the chessboard will be reflected in the code analysis later. (A chessboard version can be made later)

It should be noted that we use '1' and '0' , that is, character 1 and character 0 represent mines and no mines. Why is it arranged this way? This will help us to design arrays and functions in the future. Now we will explain this temporarily, and we will give you a clearer and more systematic understanding later.

This is the end of the preliminary design ideas! The train starts now, please close the doors and windows and fasten your seat belts! !


First, we design the game in separate files, a test.c to manage the execution process of the game, a game.c to implement the custom functions required by the game, a game.h to encapsulate the function declaration, the definition of constants, the inclusion of header files, etc. .

For the preliminary game execution process design of test.c, refer to the following code:

void menu()
{
    printf("******************\n");
	printf("***   1.play   ***\n");
	printf("***   0.exit   ***\n");
	printf("******************\n");
}

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("已退出\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

Because the game may need to be played many times, in order not to restart the game, the form of loop is used here, and because the game needs to be executed at least once, the do while loop is used here, and it is not a big problem to use the while loop. Well, the do while loop first executes the print menu, and then we choose the branch to be executed next according to the menu. When we choose 1, it means we want to play the game next, and when we choose 0, we want to exit the game, Here we choose to use the integer variable input to accept our choice and switch to execute our choice. At the same time, we will find that there is an advantage to arranging the exit number to be 0. When the input is 0, the loop can be exited directly, and the loop can continue to be continued instead of 0, which is perfectly in line with our choice design! ! !


Next is the implementation of the function game().

We just discussed that we need two chessboards, a mine board and a guess board for the player.

So we create two 2D arrays to represent the two disks.

That is (thunder disk) char mine [ ] [ ] = { 0 }; (player interface disk) char show [ ] [ ] = { 0 }; But what are the sizes of the two disks? This is what we need to consider Question, according to the difficulty of primary minesweeping, we temporarily arrange a chessboard of 9X9. For a longer-term consideration, it is impossible for us to directly write char mine [ 9 ] [ 9 ] = { 0 }; char show [ 9 ] [ 9 ] = { 0 }; For future maintenance and control difficulty at a small cost, We use #define to define ROW as 9 and COL as 9. But are we really comprehensive enough? How to count 8 mines around the border position? This will undoubtedly result in an array out of bounds. So we need to expand the chessboard into a 11X11 chessboard. So we create two checkerboards like this char mine[ROWS][COLS] = { 0 }; char mine[ ROWS ][ COLS ] = { 0 };

Here we need to briefly say:

#define ROWS ROW+2

#define COLS COL+2.

Well, we finally have objects we can manipulate, now we need to initialize them.

Let's take a function name according to the tradition, initboard(), that's it. Code on! ! !

void initboard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

Here, we initialize the mine disk with 0, which means that there are no mines at all locations, and use * to keep the mystery of the location for players to guess.

即         initboard(mine, ROWS, COLS, '0');
            initboard(show, ROWS, COLS, '*');

Although the printing function has not been completed for a while, it does not prevent us from admiring the initialization results (refer to the figure below)

    

In fact, this shows why the characters '1' and '0' are used to represent mines and no mines, instead of numbers 1 and 0, because we only need one function to initialize both disks. And if one board is an array of integers and the other is an array of characters, you'll need two initialization functions.


 After initializing the chessboard, we need to complete the rest of the printing function, because we want to check whether our initialization function implements the initialization of the chessboard as we want. code show as below:

void displayboard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i <= col; i++)
	{
		if (i == 0)
		{
			printf("  %d     ", i);
		}
		else
		{
			printf("%d     ", i);
		}
	}
	printf("\n------------------------------------------------------------\n");
	for (i = 1; i <= row; i++)
	{
		printf("  %d  |", i);
		for (j = 1; j <= col; j++)
		{
			printf("  %c  |", board[i][j]);
		}
		printf("\n------------------------------------------------------------\n");
	}
}

Due to space limitations, the printing function is not described here, and the appearance of the chessboard is determined by personal preference.


Chong Chong Chong, the next step is to set the thunder disk, and put the code! ! !

void setmine(char mine[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

Here EASY_COUNT is a constant function defined by #define EASY_COUNT 9, which means a simple number of mines. Here, the rand() library function is used to design random numbers for the coordinates X and Y respectively. %row+1 and %col+1 ensure that X and Y are within the range of 1~row. It should be noted that mine[ x ] [ y ] When assigning value, make sure that mine[ x ] [ y ] is not buried in mines. If there is no if condition in the above picture, the mine may be less, because repeated assignments will overwrite the last value.


Hahaha, the exciting demining session is finally here, there should be applause here! ! ! Look at the picture first

First of all, we need to input the coordinates of the inspection mine, so we create two integer variables X and Y. When we input the values ​​of X and Y, the input X and Y may not meet the requirements. At this time, the if statement appears. When the value we input meets the requirements, we first need to judge whether mine [ X ] [ Y ] == '1', if the condition is true, we can declare the game over, if mine [ X ] [ Y ] == ' 1', if it does not hold, then we need to assign a value to the coordinate (X, Y) position, and count how many mines there are in 8 positions around it. We use int get_mine_count() to count the number of mines in 8 positions around the coordinate (X, Y) position; the code is as follows:

void foundmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int iswin = 0;
	while (iswin < row * col - EASY_COUNT)
	{
		printf("请选择要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				displayboard(mine, row, col);
				break;
			}
			else
			{
				iswin++;
				expand(mine, show, row, col, x, y);
				displayboard(show, row, col);
			}
		}
		else
		{
			printf("输入坐标不合法,请重新输入\n");
		}
	}
	if (iswin == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
	}
}

The demining process is the core of the entire game. Players need to find out the positions of all the mines or step on them before the game ends. Therefore, the demining function must be a cyclic process, so that the effect of multiple demining can be obtained. How to judge victory? We adopt such a design idea: check out all non-mine locations, and other locations are naturally mines, so as to achieve a mine-sweeping effect. Here I use the integer variable iswin as the counter. When inputting a non-mine coordinate position, iswin+=1. When iswin is equal to ROW*COL-EASY_COUNT, the game can be judged to win.


The point is here, a detailed analysis of the expand() function

The function of the following get_mine_count() function is reiterated here: Count how many mines there are in 8 positions around the input coordinate (X,Y) position.

expand() is an expansion function, the function is:

When checking the coordinate position get_mine_count()! = 0, change the value of this position to the return value of get_mine_count().

When there are 0 mines around the coordinate position to be checked, set the position to empty, and check whether the surrounding 8 positions are also surrounded by 0 mines. If the surrounding coordinate position satisfies the condition get_mine_count == 0, this will be The location is also set to null, if the surrounding surrounding location also satisfies the condition get_mine_count == 0, this also sets the location to 0, if the surrounding surrounding location also satisfies the condition et_mine_count == 0......... .

Speaking in human words is: take the position you entered as the starting point, as long as the position get_mine_count == 0, empty it, and at the same time empty the surrounding if get_mine_count == 0, and also regard the surrounding position as the starting point. Obviously satisfying the idea of ​​recursion, it can be solved comfortably with recursion.

Code on! ! !

void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	if (get_mine_count(mine, x, y)==0)
	{
		show[x][y] = ' ';
		int i = 0;
		int j = 0;
		for (i = x - 1; i <= x + 1; i++)
		{
			for (j = y - 1; j <= y + 1; j++)
			{
				if (show[i][j] == '*' && i > 0 && i <= row && j > 0 && j <= col)
				{
					expand(mine, show, row, col , i, j);
				}
			}
		}
	}
	else
	{
		show[x][y] = get_mine_count(mine, x, y) + '0';
	}
}

Alas, I really can't put it down, so I can only trouble the spectators to swipe and watch.

In order to represent the 8 positions around the input position, we use the i,j variables to represent the for loop, that is, the following 8 positions

        mine[x - 1][y - 1]        mine[x - 1][ y ]        mine[x - 1][y + 1]
        mine[ x ][y - 1]                                          mine[x][y + 1]
        mine[x + 1][y - 1]       mine[x + 1][ y ]        mine[x + 1][y + 1]

It should be noted that when the boundary get_mine_count == 0, the surrounding position cannot be empty, because if the surrounding position is empty, there will be problems in calculating get_mine_count. For details, see the implementation principle of get_mine_count. Therefore, the above recursive implementation requires a constraint to solve this problem, see the above code for details.

The following supplements the implementation principle of get_mine_count:

static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1]
		+ mine[x][y - 1] + mine[x][y + 1]
		+ mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * 48;
}

Reiterate the function of the get_mine_count() function: calculate how many mines there are in 8 positions around the input coordinate (X,Y) position.

Because there are mines, I set them with the character '1', so add up the values ​​of these 8 positions to know the number of mines. But it doesn't happen that this is not the number 1, but the character '1', because its ASCII value is 49, so it actually represents the number 49, but that doesn't prevent us from adding, because the character '1'== The character '0'+1, so we can add them and subtract the character '0', because the character '0' actually represents the number 48. When printed as an integer, it prints the character '0', which displays 0, the same as the number 0. When it prints as an integer, it prints 48.), which also reflects the use of the characters '1' and The character '0' represents the benefits with and without mines.

share the entire source code

test.c:

#include "game.h"
void game()
{
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	initboard(mine, ROWS, COLS, '0');
	initboard(show, ROWS, COLS, '*');
	setmine(mine, ROW, COL);
	displayboard(show, ROW, COL);
	displayboard(mine, ROW, COL);
	foundmine(mine, show, ROW, COL);
}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("已退出\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

game.c:

#include "game.h"
void menu()
{
	printf("******************\n");
	printf("***   1.play   ***\n");
	printf("***   0.exit   ***\n");
	printf("******************\n");
}
void initboard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

void displayboard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i <= col; i++)
	{
		if (i == 0)
		{
			printf("  %d     ", i);
		}
		else
		{
			printf("%d     ", i);
		}
	}
	printf("\n------------------------------------------------------------\n");
	for (i = 1; i <= row; i++)
	{
		printf("  %d  |", i);
		for (j = 1; j <= col; j++)
		{
			printf("  %c  |", board[i][j]);
		}
		printf("\n------------------------------------------------------------\n");
	}
}

void setmine(char mine[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1]
		+ mine[x][y - 1] + mine[x][y + 1]
		+ mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * 48;
}

void foundmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int iswin = 0;
	while (iswin < row * col - EASY_COUNT)
	{
		printf("请选择要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				displayboard(mine, row, col);
				break;
			}
			else
			{
				iswin++;
				expand(mine, show, row, col, x, y);
				displayboard(show, row, col);
			}
		}
		else
		{
			printf("输入坐标不合法,请重新输入\n");
		}
	}
	if (iswin == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
	}
}

void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	if (get_mine_count(mine, x, y)==0)
	{
		show[x][y] = ' ';
		int i = 0;
		int j = 0;
		for (i = x - 1; i <= x + 1; i++)
		{
			for (j = y - 1; j <= y + 1; j++)
			{
				if (show[i][j] == '*' && i > 0 && i <= row && j > 0 && j <= col)
				{
					expand(mine, show, row, col , i, j);
				}
			}
		}
	}
	else
	{
		show[x][y] = get_mine_count(mine, x, y) + '0';
	}
}

game.h:

#define _CRT_SECURE_NO_WARNINGS 1
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void menu();
void initboard(char board[ROWS][COLS], int rows, int cols, char set);
void displayboard(char board[ROWS][COLS], int row, int col);
void setmine(char mine[ROWS][COLS], int row, int col);
void foundmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y);


Well, the above is a simple implementation of mine sweeping. Don't you like it when you see it, haha. There will be more series in the future, such as the analysis and application of all keywords in C language, or more game implementations. This is my first blog in the true sense of the word, and I hope you all take care of it.

If there are any mistakes in the above, I would like to ask you to give me your advice, and I will be grateful.

Hope to make progress together with you all! God rewards hard work! ! Life is worth it, and the future is promising! ! !

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324281918&siteId=291194637