[C Language] -- Minesweeper (code + detailed explanation)

Table of contents

Foreword:

1. Game rules

2. Create files

2.1 Header files

2.2 define the number of rows, columns, and thunder defined

2.3 Function declaration

3. Game test part

3.1 Main function

Analysis of the content of the main function:

3.1.1 menu() function (menu function)

3.1.2 switch statement

3.1.3 do while statement

3.1.4 game() function

4. Game function implementation part

Game implementation logic

4.1 Creation of the chessboard

4.2 Initialize chessboard function

4.3 Print chessboard function

4.4 Place mines

4.5 Mine detection


Foreword:

This article uses C language to implement mine clearance. It is a flexible application of previous knowledge to consolidate, and how to write an engineering code in reasonable chunks. If anyone reads this article and finds something wrong, Please be sure to point it out.

1. Game rules

We click on a grid at random on the chessboard. If this grid is not a mine, we will exclude this location. After elimination, this grid will show how many mines there are in the grids surrounding it. If the clicked grid has mines buried in it, The game fails.

2. Create files

We need to divide it into blocks to write files for different purposes. To implement Sanbangqi, we need to divide it into three files, which are:

We need to divide into modules to write files for different purposes. When implementing minesweeper, we need to divide into three files, which are:

game.h //Header file (contains all required header files, define constants, function declarations)

Note: We write all 2.1, 2.2, and 2.3 in this file. The purpose is that when calling these contents in game.c and test.c, we only need to declare them once to the game.h we wrote ourselves, and then we can directly use the contents inside. .

game.c //Game file (write the functions to be used respectively)

test.c //Test file (code integration)

2.1 Header files

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

2.2 define the number of rows, columns, and thunder defined

Advantages: If we need to change the size of the chessboard and the number of mines later, we only need to change it here. Once and for all, there is no need to modify it in the entire program, which improves efficiency.

#define ROW 9
#define COL 9

#define ROWS ROW + 2
#define COLS COL + 2

#define EASY_COUNT 10

2.3 Function declaration

//初始化棋盘
void InitBorad(char board[ROWS][COLS], int rows, int cols, char set);

//打印棋盘
void DisplayBorad(char board[ROWS][COLS], int row, int col);

//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col);

//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

3. Game test part

3.1 Main function

In this part, first write down the logic of playing the game.

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;
}

Analysis of the content of the main function:

3.1.1 menu() function (menu function)

void menu()
{
	printf("*******************\n");
	printf("\t1.play\n");
	printf("\t0.exit\n");
	printf("*******************\n");
}

Show results:

3.1.2 switch statement

The switch statement enables users to select different menus.

Show results:

3.1.3 do while语句

The dowhile statement used here is to run the main function first and then make a selection. The main function will implement different functions according to the user's selection.

3.1.4 game() function

When the player selects the number 1, the game() function will be called to implement the following functions.

void game()
{
	//创建数组
	//创建雷的数组(mine)	显示数组(show)		两个数组的类型,大小相同
	char mine[ROWS][COLS];//存放布置的雷的信息
	char show[ROWS][COLS];//存放排查出雷的信息

	//初始化棋盘
	InitBorad(mine, ROWS, COLS, '0');//将布置雷的数组初始化为 '0'
	InitBorad(show, ROWS, COLS, '*');//将排查雷的数组初始化为 '*'

	//打印棋盘
	DisplayBorad(show, ROW, COL);
	//布置雷

	SetMine(mine, ROW, COL);
	//DisplayBorad(mine, ROW, COL);
	
	//排查雷
	FindMine(mine, show, ROW, COL);
}

The game() function contains the array for laying mines and the array for display (these two arrays are the same size and should be written separately to avoid confusion), as well as initializing the chessboard function, displaying the chessboard function, arranging the mine function, and troubleshooting the mines. Function calls, these functions will be expanded and analyzed below.

The logical sequence of the game() function is: Create chessboard ——> Initialize chessboard ——> Print chessboard ——> Lay out mines ——> Check mines

4. Game function implementation part

Game implementation logic

4.1 Creation of the chessboard

//创建数组
	//创建雷的数组(mine)	显示数组(show)		两个数组的类型,大小相同
	char mine[ROWS][COLS];//存放布置的雷的信息
	char show[ROWS][COLS];//存放排查出雷的信息

Here we need to create two arrays of the same type and size.

Q: Why do we need to create two identical arrays?

A:Because when the user plays the game, he sees the covered chessboard and cannot see where the mines are buried. This is to display the chessboard. We also need a chessboard with mines planted, so that the players can pass or fail to pass the level while playing the game, and the points where the mines are planted can be presented to the players.

What we display on the screen is a 9*9 array, but when we created it, it was an 11*11 array, and we were going to bury 10 mines.

Q: Why is an 11*11 array created instead of a 9*9 array?

A: In mine sweeping, if a number is displayed on a location we are checking, it means that there are mines of this number in the 8 grids around this location, then we When arranging the positions on the four sides, there are not enough 8 grids, but when our function is implemented, we will still look for these 8 grids, which will cause an out-of-bounds access problem in the array (we will draw a picture to explain this problem ), but we now create an 11*11 array, and initialize the array to '0'. When displaying the chessboard, we print a 9*9 array, so that the array will not go out of bounds. Access problem, the game can still run normally.

As shown in the figure:If we are using an 11*11 array, the printed array is a 9*9 array (red area), and the function function will surround the grid with 8 If the number of mines found in each grid is exceeded, there will be no cross-border problem.

4.2 Initialize chessboard function

Define the InitBorad() function and implement the code:

void InitBorad(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;
		}
	}
}

First, we initialize the array of mines to character 0, (non-mines are represented by characters 0, mines are represented by characters 1), and all arrays displaying the chessboard are covered with * to cover the arrays of mines, < /span>InitBorad() function is called twice in the game() function. So the

4.3 Print chessboard function

Define the DisplayBorad() function and implement the code:

void DisplayBorad(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("########扫雷#######\n");
	for (j = 0; j <= col; j++)
	{
		printf("%d ", j);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

Show results:

Mine chessboard (mine)

Show the board (show)

We can see that it is the same as what we originally conceived, we just need to show the show board to the user.

4.4 Place mines

Define the SetMine() function and code implementation:

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--;
		}
	}
}

Code analysis:

Q: How can we randomly arrange thunder on a 9*9 chessboard?

A: We use the rand function to generate random numbers using timestamps, and then modulo the rows and columns (9) of the generated random numbers and add one, so that we can put 10 random numbers into the 9*9 chessboard in the middle. thunder.

int x = rand() % row + 1;//随机数取模于row得到0~8,+1处理后得到的是1~9
int y = rand() % col + 1;//随机数取模于col得到0~8,+1处理后得到的是1~9

We set count mines, subtract one when one is buried, and judge whether this position is the character '0', if so, change the character '0' to the character ' ;1', if it is not the character '0', then a legal random position will be generated, so that it can be legally placed on the 9*9 chessboard, so that there will be no repeated mines. When using the while loop, until the count is reduced to 0, the while is no longer in the loop, and the 10 mines have just been buried.

4.5 Mine detection

Define the FindMine() function and implement the code:

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win<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");
				DisplayBorad(mine, ROW, COL);
				break;
			}
			else
			{
				//此坐标不是雷,就要统计此坐标附近雷的个数
				int count = GetMineCount(mine, x, y);
				show[x][y] = count + '0';
				DisplayBorad(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("排查的坐标非法,请重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功!\n");
		DisplayBorad(mine, ROW, COL);
	}

}

The GetMineCount() function for calculating nearby mines is added, and the code implementation is:

int GetMineCount(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 * '0');
}

We analyze these two pieces of code:

First, we judge whether the coordinate position input by the player is legal. The coordinate must be between 1~row/col (including 1 and row/col). If it is not in this range, the code will use the first else statement to remind the player to "check The coordinates are illegal, please re-enter".

If it is legal, we will go to the first if statement. After entering, we will judge the position to see if the position is the character '1'. If it is the character '1', we have stepped on the thunder. , the player will be reminded of the words "Unfortunately, you were killed" and the mine distribution map will be presented to the player.

If it is the character '0', use the else statement to calculate and mark the number of mines in the 8 grids around the position. At this time, we call < a i=1>GetMineCount() function to calculate, let’s take a look at the content:

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 * '0');

We can see that this is the sum of the characters of the 8 coordinates around the coordinate. We know that the ASCII code values ​​corresponding to the character '1' and the character '0' are connected. Therefore, we add up the surrounding 8 characters and subtract 8 characters '0', so that we can get the number of characters '1', which is the number of thunders.

Show results:

In order to see it quickly, we also display part of Lei's array so that we can see it intuitively.

mine chessboard 

show board:

If we make no mistakes at all, until the entire mine is discharged, the following code will be executed:

if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功!\n");
		DisplayBorad(mine, ROW, COL);
	}

After successful demining, we will print the mine layout diagram again. For the convenience of display, we arranged 80 mines for testing.

Show results:

At this point, the two mini-games are all finished.


The complete code is in the code repository, the entrance is here:>

C language jy: The primary stage of C language - Gitee.com

Guess you like

Origin blog.csdn.net/Ljy_cx_21_4_3/article/details/129066489