Minesweeper, a classic game in C language (super detailed explanation + source code)

"Young spirit is the precipitation of thousands of sails, and it is also the open-mindedness of life with an optimistic and indifferent smile!" Today we will learn how to use the C language to realize the minesweeper game!

1. Game introduction

A minesweeping board is composed of many cells, in which a certain number of mines are randomly distributed, and there is at most 1 mine in a cell. The victory condition is to open all safe cells (non-regs, safe cells), and the failure condition is to open a cell (stepping on mines). The picture below is an example of 9*9:
insert image description here

2. Game preparation

Same as the previous three chess, here, we also need three source files to jointly realize this program.
insert image description here

(1) Header files game.h, which are used to store function declarations, #define constant definitions, and library function references.
(2) Source file test.c, which contains the test logic of the game.
(3) Source file game.c, which contains the implementation logic (function implementation) of the game.

3. Game implementation

3.1 Generate menu

Here, we are the same as backgammon, we still give the user a choice through the switch statement. When the user enters different numbers, our program will give different functions.

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
#include <stdio.h>
void menu()
{
    
    
	printf("******************************\n");
	printf("*********   1.play   *********\n");
	printf("*********   0.exit   *********\n");
	printf("******************************\n");
}
void game()
{
    
    

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

The general framework is out, now we need to look at the specific implementation of the minesweeper game.
insert image description here

3.2 The specific implementation of the game

Here we simplify the game, set up a 9*9chessboard, and place it 10个雷.
The troubleshooting process is as follows:
1. If the position is mine, then the game is over.
2. If all the positions that are not mines are found, the game is over.
3. If this position is not a mine, calculate how many mines there are in the 8 grids around this position, and display the number of mines.

3.2.1 Initialize the chessboard

The chessboard here is 9*9yes, it can be the same as the three chess, first initialize each board 0, as shown in the figure below, and then fill in the place with mines 1, as shown in the figure below:
insert image description here
However, we know from the rules of the game, When we click on a grid that is not thunder, we need to return the number of mines in the eight grids around it. As shown in the figure below, if we click on 绿色1the grid, then the position will return 1this value. At this time, the value here will be confused 1with the one representing thunder . At the same time, when we click on a grid on four sides, there will also be a problem of crossing the border: at this time, we can consider expanding the chessboard into a chessboard when we check for mines .1
insert image description here

insert image description here
11*11
insert image description here

9*9At the same time, we can initialize all the grids for troubleshooting to *, to avoid the confusion of two 1s with different meanings. In this way, we get two 11*11chessboards.
insert image description here

//game.h
#pragma once
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//初始化棋盘函数的声明
void Init_Board(char board[ROWS][COLS], int rows, int cols, char set);

//game.h*
//初始化棋盘的定义
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void Init_Board(char board[ROWS][COLS], int rows, int cols, char set)
{
    
    
	int i = 0;
	for (i = 0; i < rows; i++)
	{
    
    
		int j = 0;
		for (j = 0; j < cols; j++)
		{
    
    
			board[i][j] = set;
		}
	}
}

//test.c
void game()
{
    
    
	char assign[ROWS][COLS] = {
    
     0 };
	char show[ROWS][COLS] = {
    
     0 };
	Init_Board(assign,ROWS,COLS,'0');
	Init_Board(show, ROWS, COLS, '*');
}

After the initialization is complete, we want to verify it, right? At this time, we need to print out the chessboard.

3.2 Print the chessboard

Although we initialize 11*11the chessboard here, the user needs the middle area 9*9, so we only need to print the middle area 9*9.

//game.h
//打印棋盘函数的声明
void Display_Board(char board[ROWS][COLS], int row, int col);

//game.c
//打印棋盘函数的定义
//我们只需要打印出中间的9*9的格子
void Display_Board(char board[ROWS][COLS], int row, int col)
{
    
    
	int i = 0;
	for (i = 1; i <= row; i++)
	{
    
    
		int j = 0;
		for (j = 1; j <= col; j++)
		{
    
    
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

//test.c
void game()
{
    
    
	Display_Board(assign, ROW, COL);
}

insert image description here
Here we need to print out the line number and column number, so that it can be seen more clearly. Just modify the code like this!
insert image description here
Optimize it again, as shown in the figure below:
insert image description here

3.3 Lay out mines

What we want is to randomly generate 10 mines in this 9*9 chessboard. Here we think of a function that can generate random numbers. To use these two functions, we need to rand()和srand()add header files #include <time.h>和#include <stdlib.h>. At the same time, we need to add in the main function statement srand((unsigned int)time(NULL));to generate a random number seed.

//game.h
#define Easy_Thunder 10
#include <stdlib.h>
#include <time.h>
//布置雷函数的声明
void Set_thunder(char board[ROWS][COLS], int row, int col);

//game.c
//布置雷函数的定义
void Set_thunder(char board[ROWS][COLS], int row, int col)
{
    
    
	int count = Easy_Thunder;
	while (count)
	{
    
    
		int x = rand() % row + 1;//生成横坐标
		int y = rand() % col + 1;//生成纵坐标
		if (board[x][y] == '0')//防止在同一个地方重复布雷
		{
    
    
			board[x][y] = '1';
			count--;
		}
	}
}

//test.c
void game()
{
    
    
	char assign[ROWS][COLS] = {
    
     0 };
	char show[ROWS][COLS] = {
    
     0 };
	Init_Board(assign,ROWS,COLS,'0');
	Init_Board(show, ROWS, COLS, '*');
	Display_Board(assign, ROW, COL);
	Set_thunder(assign, ROW, COL);
}
int main()
{
    
    
	srand((unsigned int)time(NULL));
	return 0;
}

So, the code is written, let's verify it, is it possible to arrange ten mines in this way?
insert image description here

3.4 Troubleshooting

We start to randomly click a grid. When this grid is not a mine, we can calculate the ASCII code value of the eight grid characters around it and ASCII码值subtract 8个'0'it to know how many mines are around this grid. Then we '0'的ASCII码值add The corresponding character can be returned on the grid here. When we find out all the grids that are not mines, we will prompt the user that the mine clearance is successful. When we step on thunder, we also give corresponding prompts to the user, and the game is over. Here is the same as backgammon, we can 添加清屏的操作system("cls");make our game panel not so lengthy.

insert image description here

//game.h
//排查雷函数的声明
void Find_thunder(char assign[ROWS][COLS], char show[ROWS][COLS], int row, int col);

//game.c
int Getassign(char assign[ROWS][COLS], int row, int col)//查找不是雷的格子周围有几个雷
{
    
    
	return (assign[row - 1][col - 1]//字符'0'的ASCII值为48,字符'1'的ASCII值为49
		+ assign[row - 1][col]
		+ assign[row - 1][col + 1]
		+ assign[row][col - 1]
		+ assign[row][col + 1]
		+ assign[row + 1][col - 1]
		+ assign[row + 1][col]
		+ assign[row + 1][col + 1] - 8 * '0');
}
//排查雷函数的定义
void Find_thunder(char assign[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    
    
	int x = 0;
	int y = 0;
	int win = 0;//计算没有踩到雷的次数
	while (win<col*row-Easy_Thunder)
	{
    
    
		printf("请输入坐标:>");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
    
    
			if (assign[x][y] == '1')
			{
    
    
				printf("很遗憾,你被炸死了!\n");
				Display_Board(assign, ROW, COL);
				break;
			}
			else
			{
    
    
				//如果这个位置不是雷就统计周围八个格子雷的个数
				int c = Getassign(assign, x, y);
				show[x][y] = c + '0';
				Display_Board(show, ROW, COL);
				system("cls");
				Display_Board(show, ROW, COL);
				win++;
			}
		}
		else
		{
    
    
			printf("坐标非法,请重新输入!\n");
		}
	}
	if (win == row * col - Easy_Thunder)
	{
    
    
		printf("恭喜你排雷成功!\n");
		Display_Board(assign, ROW, COL);
	}	
}

//test.c
void game()
{
    
    
	Find_thunder(assign, show, ROW, COL);//排雷函数的引用
}

Here, we input randomly, without thinking, and try the result of stepping on mines:
insert image description here
Then, if we think carefully, see if we can succeed when we find out all the grids that are not mines:
insert image description here

4. Detailed code of minesweeper game

//game.h
#pragma once
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define Easy_Thunder 10
#include <stdlib.h>
#include <time.h>
//初始化棋盘函数的声明
void Init_Board(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘函数的声明
void Display_Board(char board[ROWS][COLS], int row, int col);
//布置雷函数的声明
void Set_thunder(char board[ROWS][COLS], int row, int col);
//排查雷函数的声明
void Find_thunder(char assign[ROWS][COLS], char show[ROWS][COLS], int row, int col);

//game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化棋盘的定义
void Init_Board(char board[ROWS][COLS], int rows, int cols, char set)
{
    
    
	int i = 0;
	for (i = 0; i < rows; i++)
	{
    
    
		int j = 0;
		for (j = 0; j < cols; j++)
		{
    
    
			board[i][j] = set;
		}
	}
}
//打印棋盘函数的定义
//我们只需要打印出中间的9*9的格子
void Display_Board(char board[ROWS][COLS], int row, int col)
{
    
    
	int i = 0;
	int j = 0;
	printf("--------扫雷--------\n");
	for (i = 0; i <= col; i++)
	{
    
    
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
    
    
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
    
    
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}
//布置雷函数的定义
void Set_thunder(char board[ROWS][COLS], int row, int col)
{
    
    
	int count = Easy_Thunder;//布置10个雷
	while (count)
	{
    
    
		int x = rand() % row + 1;//生成横坐标
		int y = rand() % col + 1;//生成纵坐标
		if (board[x][y] == '0')//防止在同一个地方重复布雷
		{
    
    
			board[x][y] = '1';
			count--;
		}
	}
}
int Getassign(char assign[ROWS][COLS], int row, int col)//查找不是雷的格子周围有几个雷
{
    
    
	return (assign[row - 1][col - 1]//字符'0'的ASCII值为48,字符'1'的ASCII值为49
		+ assign[row - 1][col]
		+ assign[row - 1][col + 1]
		+ assign[row][col - 1]
		+ assign[row][col + 1]
		+ assign[row + 1][col - 1]
		+ assign[row + 1][col]
		+ assign[row + 1][col + 1] - 8 * '0');
}
//排查雷函数的定义
void Find_thunder(char assign[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    
    
	int x = 0;
	int y = 0;
	int win = 0;//计算没有踩到雷的次数
	while (win<col*row-Easy_Thunder)
	{
    
    
		printf("请输入坐标:>");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
    
    
			if (assign[x][y] == '1')
			{
    
    
				printf("很遗憾,你被炸死了!\n");
				Display_Board(assign, ROW, COL);
				break;
			}
			else
			{
    
    
				//如果这个位置不是雷就统计周围八个格子雷的个数
				int c = Getassign(assign, x, y);
				show[x][y] = c + '0';
				Display_Board(show, ROW, COL);
				system("cls");//清屏操作
				Display_Board(show, ROW, COL);
				win++;
			}
		}
		else
		{
    
    
			printf("坐标非法,请重新输入!\n");
		}
	}
	if (win == row * col - Easy_Thunder)
	{
    
    
		printf("恭喜你排雷成功!\n");
		Display_Board(assign, ROW, COL);
	}	
}

//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
#include <stdio.h>
void menu()
{
    
    
	printf("******************************\n");
	printf("*********   1.play   *********\n");
	printf("*********   0.exit   *********\n");
	printf("******************************\n");
}
void game()
{
    
    
	char assign[ROWS][COLS] = {
    
     0 };
	char show[ROWS][COLS] = {
    
     0 };
	Init_Board(assign,ROWS,COLS,'0');
	Init_Board(show, ROWS, COLS, '*');
	Display_Board(assign, ROW, COL);
	Set_thunder(assign, ROW, COL);
	Find_thunder(assign, show, ROW, COL);
}
int main()
{
    
    
	srand((unsigned int)time(NULL));
	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;
}

The above is all the code about the minesweeper game! Of course, there is still room for optimization in this program (for example, our game can only display one point at a time, but it cannot display a piece like the web version, which needs to be considered), and everyone is welcome to communicate and optimize the code in the comment area.

Guess you like

Origin blog.csdn.net/qq_73121173/article/details/132120555