C language basics - minesweeping (including recursion)

Table of contents

Preface: The complete code will be given at the end of the article, and the previous code is for reference only.

1. Minesweeper game rules

2. Analyze code layout

3. Code display and explanation

1. Game menu

2. Display the chessboard

 3. Bray

4. Check mine

5. Game Win or Lose

4. Total code display

1.game.h

2.game.c

3.test.c

5. Summary


Preface: The complete code will be given at the end of the article, and the previous code is for reference only.

1. Minesweeper game rules

 

       My friends must have played the minesweeper game. The picture above is the web version of minesweeper. We click on a grid at random, and then a large blank area and numbers will pop up. If there are numbers, it means that there are eight grids for a week of this number . How many mines are there in total . All we have to do is to find all the grids that are not Ray . Today we will explain the following simple 9*9 grid minesweeper . A link to the minesweeper web version is attached below, and friends who want to play can experience it immediately! http://www.minesweeper.cn/

2. Analyze code layout

We adhere to the principle of clean and easy-to-read code , and divide the code into three parts, as shown in the following figure:

The "game.h" header file is used to define the required constants, declare the header files we need to use, and declare the functions to be created.

The "game.c" source file is used to define and write the functions we are going to create.

The "test.c" source file is the body of the function we want to write.

3. Code display and explanation

1. Game menu

Any game needs a menu to control the game process, so the following code must be familiar.

​​#include"game.h"
void menu()
{
	printf("**************************************\n");
	printf("************    1.play    ************\n");
	printf("************    0.exit    ************\n");
	printf("**************************************\n");
}
void game()
{
	char arr1[ROWS][COLS];
	char arr2[ROWS][COLS];
	SetBoard(arr1, ROWS, COLS, '0');//创建棋盘
	SetBoard(arr2, ROWS, COLS, '*');//创建棋盘
	SetMine(arr1, ROW, COL);//布雷
	ShowBoard(arr2, ROW, COL);//展示棋盘
	FindMine(arr1, arr2, ROW, COL);//排查雷
}
int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请选择->:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input == 1);
	return 0;
}

We implement our calls to all other functions by creating a game function .

       Here is a little trick , we know that the judgment condition of while is 0 is false , and non-zero is true , so we use case 0 as the exit game, and it can also terminate the loop , and input 1 or other numbers , it will continue to loop, restart choose.

2. Display the chessboard

 The ideal chessboard is a two-dimensional array of 9*9, but in fact we need to create two arrays of 11*11 , why create two arrays? Because this game is a guessing game , as a player, we can only see its appearance, but we don't know how it works, and as a programmer, we still need to realize its operation. As for why to create an array of 11*11 , let’s buy a pass here first, and we will explain it when we talk about it below.

	char arr1[ROWS][COLS];
	char arr2[ROWS][COLS];
	SetBoard(arr1, ROWS, COLS, '0');//创建棋盘
	SetBoard(arr2, ROWS, COLS, '*');//创建棋盘
void SetBoard(char arr1[ROWS][COLS], int rows, int cols, char set)//创建棋盘
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			arr1[i][j] = set;
		}
	}
}

In the above code, our array arr1 is used for various initialization operations on the board , while the array arr2 is used for displaying the players .

void ShowBoard(char arr1[ROWS][COLS], int row, int col)//展示棋盘
{
	for (int i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	printf("___________________\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d|", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", arr1[i][j]);
		}
		printf("\n");
	}
}

In the above code, we realize the display of the chessboard, but we hope that the player can select the position coordinates to be judged more accurately , so we also print the number of rows and columns , and the results are as follows:

 

 3. Bray

After creating the board, we need to call the rand() function to implement random mine , the code is as follows:

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

The result is as follows:

 

4. Check mine

After setting up the thunder, we will start the game immediately, the code is as follows:

void FindMine(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int row, int col)//排查雷
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (1)
	{
		printf("请输入要排查的坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (arr1[x][y] == '1')
			{
				printf("很遗憾,踩雷了,游戏结束。\n");
				ShowBoard(arr1, ROW, COL);//展示棋盘	
				break;
			}
			else
			{
				system("cls");
				RecUnfold(arr1, arr2, x, y, &win);
				ShowBoard(arr2, ROW, COL);//展示棋盘
			}
		}
		else
		{
			printf("输入错误,请重新输入:");
		}
	}
}

       When checking for mines, we want to click on a coordinate. If it is not a mine, it will display how many mines are in the 8 grids around it. This is achieved by the following code:

int  MineNum(char arr1[ROWS][COLS], int x, int y)//统计周围雷数
{
	int count = 0;
	for (int i = x - 1; i <= x + 1; i++)
	{
		for (int j = y - 1; j <= y + 1; j++)
		{
			if (arr1[i][j] == '1')
				count++;
		}
	}
	return count;
}

Define the count variable to count the number of mines. Here we use the above code to explain why we want to create an array of 11*11 instead of an array of 9*9.

 

       If we want to count the number of mines around a grid, if it is only a 9*9 array, when we judge the most boundary grid , some of its adjacent grids will not be able to be judged , which is prone to problems , so we create an array of 11*11, but only initialize the middle 9*9 , so as to avoid code errors.

       If we just judge one by one coordinates like that, it is a bit too cumbersome. In order to realize the effect of clicking a grid and exploding a circle as shown in the figure below, we create the RecUnfold function to achieve it recursively .

void RecUnfold(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int x, int y, int* win)//递归炸开
{
	if (x >= 1 && x <= ROWS && y >= 1 && y <= COLS)
	{
		int count = MineNum(arr1, x, y);//获取雷数
		if (count == 0)//四周没有雷时向四周展开
		{
			arr2[x][y] = ' ';//四周都没雷时改为空格
			for (int i = x - 1; i <= x + 1; i++)
			{
				for (int j = y - 1; j <= y + 1; j++)
				{
					if (arr2[i][j] == '*')//只对'*'展开,防止死递归
					{
						RecUnfold(arr1, arr2, i, j, win);
					}
				}
			}
		}
		//四周有雷时显示雷数
		else
		{
			arr2[x][y] = count + '0';
		}
		(*win)++;
	}
}

If you click on a grid, if it is not thunder, then we will replace it with " " , and recursively judge other grids around it, the effect is as follows:

 

5. Game Win or Lose

       We already know that if we click on the mine, the game will be lost, but if we click on all the grids that are not mine , then the game will win. We implement the following code:

	int win = 0;
	if (win == row * col - EASY_COUNT)
		{
			printf("恭喜你,游戏胜利!\n");
			ShowBoard(arr1, ROW, COL);
			break;
		}

       We define a win constant , initialize it to 0 , and use it as an actual parameter for the RecUnfold function call , so that as long as a grid is opened, win will be ++ until win == 71 , that is, the total number of grids - the number of thunder , the game That's it. One thing to note here is that we call the RecUnfold function to change the value of win , so when calling, we need to pass in the address and use the pointer to receive it .

4. Total code display

1.game.h

#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 9//初始化部分
#define COL 9//初始化部分
#define ROWS ROW+2//数组整体
#define COLS COL+2//数组整体
#define EASY_COUNT 10//雷数
void SetBoard(char arr1[ROWS][COLS], int rows, int cols,char set);//创建棋盘
void ShowBoard(char arr1[ROWS][COLS], int row, int col);//展示棋盘
void SetMine(char arr1[ROWS][COLS], int row, int col);//布雷
void FindMine(char arr1[ROWS][COLS],char arr2[ROWS][COLS], int row, int col);//布雷

2.game.c

#include"game.h"
void SetBoard(char arr1[ROWS][COLS], int rows, int cols, char set)//创建棋盘
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			arr1[i][j] = set;
		}
	}
}
void ShowBoard(char arr1[ROWS][COLS], int row, int col)//展示棋盘
{
	for (int i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	printf("___________________\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d|", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", arr1[i][j]);
		}
		printf("\n");
	}
}
void SetMine(char arr1[ROWS][COLS], int row, int col)//布雷
{
	int count = EASY_COUNT;
	while(count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (arr1[x][y] == '0')
		{
			arr1[x][y] = '1';
			count--;
		}
	}
}
int  MineNum(char arr1[ROWS][COLS], int x, int y)//统计周围雷数
{
	int count = 0;
	for (int i = x - 1; i <= x + 1; i++)
	{
		for (int j = y - 1; j <= y + 1; j++)
		{
			if (arr1[i][j] == '1')
				count++;
		}
	}
	return count;
}
void RecUnfold(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int x, int y, int* win)//递归炸开
{
	if (x >= 1 && x <= ROWS && y >= 1 && y <= COLS)
	{
		int count = MineNum(arr1, x, y);//获取雷数
		if (count == 0)//四周没有雷时向四周展开
		{
			arr2[x][y] = ' ';//四周都没雷时改为空格
			for (int i = x - 1; i <= x + 1; i++)
			{
				for (int j = y - 1; j <= y + 1; j++)
				{
					if (arr2[i][j] == '*')//只对'*'展开,防止死递归
					{
						RecUnfold(arr1, arr2, i, j, win);
					}
				}
			}
		}
		//四周有雷时显示雷数
		else
		{
			arr2[x][y] = count + '0';
		}
		(*win)++;
	}
}
void FindMine(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int row, int col)//排查雷
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (1)
	{
		printf("请输入要排查的坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (arr1[x][y] == '1')
			{
				printf("很遗憾,踩雷了,游戏结束。\n");
				ShowBoard(arr1, ROW, COL);//展示棋盘	
				break;
			}
			else
			{
				system("cls");
				RecUnfold(arr1, arr2, x, y, &win);
				ShowBoard(arr2, ROW, COL);//展示棋盘
			}
		}
		else
		{
			printf("输入错误,请重新输入:");
		}
		if (win == row * col - EASY_COUNT)
		{
			printf("恭喜你,游戏胜利!\n");
			ShowBoard(arr1, ROW, COL);
			break;
		}

	}
}

3.test.c

#include"game.h"
void menu()
{
	printf("**************************************\n");
	printf("************    1.play    ************\n");
	printf("************    0.exit    ************\n");
	printf("**************************************\n");
}
void game()
{
	char arr1[ROWS][COLS];//定义初始化数组
	char arr2[ROWS][COLS];//定义展示数组
	SetBoard(arr1, ROWS, COLS, '0');//创建棋盘
	SetBoard(arr2, ROWS, COLS, '*');//创建棋盘
	SetMine(arr1, ROW, COL);//布雷
	ShowBoard(arr2, ROW, COL);//展示棋盘
	FindMine(arr1, arr2, ROW, COL);//排查雷
}
int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请选择->:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input == 1);
	return 0;
}

5. Summary

       For me, a fledgling little blogger, I was devastated writing this game, but fortunately, I worked hard to persevere in the end. There is no shortcut to success, let alone smooth sailing. Only by constantly overcoming difficulties can we continue to break through ourselves and go further and further on the road of programming.

       Well, the content of this issue is here. Friends are welcome to comment on the private message. If you like the blogger’s article, remember to click three times. See you in the next issue!

Guess you like

Origin blog.csdn.net/2303_78442132/article/details/132008352