[C Language] Detailed Explanation of the Simulation Implementation of Minesweeping

Preface
Minesweeper is a small game we played in the same year. Today we will implement this small childhood game.

insert image description here

As with the last three games, we use three files to complete this program.

1. Design ideas

1. Options

Each game has its own game selection interface

(1 / 0)  玩游戏/退出游戏    &&   输入其他数字则输入错误重新输入
void menu()
{
    
    
	printf("**************************\n");
	printf("******    1.play    ******\n");
	printf("******    0.exit    ******\n");
	printf("**************************\n");
}
#include "game.h"

int main()
{
    
    
	int input = 0;
	menu();
	do
	{
    
    
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
    
    
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误\n");
			break;
		}
		
	} while (input);
	return 0;
}
游戏模块
void game()
{
    
    
	char mine[ROWS][COLS] = {
    
     0 };
	char show[ROWS][COLS] = {
    
     0 };
	//初始化棋盘
	InitBoard(mine, ROWS, COLS , '0');
	InitBoard(show, ROWS, COLS , '*');
	//打印棋盘
	DisplayBoard(show, ROW, COL);
	//布置雷
	SetMine(mine, ROW, COL);
	DisplayBoard(mine, ROW, COL);
	//排雷
	FindMine(mine, show, ROW, COL);
}

The header file of the library function is not used in the main function #include, but written by myself game.h文件.
insert image description here

2. Minesweeper game design

We use two arrays [show array (for viewing by users) and mine array (for mine layout)]. The show array is used to display how many mines there are in the eight coordinates around the coordinate. When I learned the design of a big guy, the mine array is used to set the mines, and the characters are used '1'to set them asthunder, while the characters '0'arenon mine, of course we can also use other characters to set thunder, but when we look at the follow-up code, we can see the convenience of the boss's design.

show数组(棋盘)

insert image description here

mine数组(棋盘)

insert image description here
When we set up 9 x 9such two-dimensional array of , we will find that when the selected coordinates are not the coordinates of the boundary, we only need to count whether the surrounding eight coordinates are mines. , it is possible thatout of bounds access, and if we list these special cases separately, it is still a little difficult for us at present, so I choose to add a circle to9 x 9 the two-dimensional array of this . We don't need to worry about this problem if we add a circle under the size we need . If we need a minesweeper interface, we design an array of . As shown in the figure below, what we need is the blue part, but if we choose the boundary, the surrounding eight coordinates are all in the array, and the array will not exceed the boundary .
insert image description here
9 x 911 x 1111 x 11
insert image description here

3. Initialization of the board

We want to initialize the contents of the board. When we enter a minesweeper game, we can’t see anything at the beginning, so we initialize all the boards displayed to the user as , and *initialize all the boards where mines are placed '0', and then arrange mines.

board[i][j]When we look at the initialization code below, we don’t know what value to put here. Suppose we initialize the mine chessboard'0' , then we need to design a function to initialize the show chessboard . On the contrary, no matter what we pass in, we need to design two function, which is optimized below.

void InitBoard(char board[ROWS][COLS], int rows, int cols)
{
    
    
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
    
    
		for (j = 0; j < cols; j++)
		{
    
    
			board[i][j] = ; // 0 ? *
		}
	}
}
当我们将函数多一个参数,我们想初始化什么就将该字符传进去。
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;
		}
	}
}

4. The function of laying mines

Precautions:
(1) When arranging mines, remember to control the range of coordinates to prevent crossing the boundary.
(2) When laying out mines, it is necessary to judge whether the coordinates are the coordinates of non-mines.
(3) Remember rand()函数the necessary conditions for use.

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 (x >= 1 && x <= row && y <= col && y >= 1)
		{
    
    
			if (mine[x][y] == '0')
			{
    
    
				mine[x][y] = '1';
				count--;
			}
		}
	}
}

5. Printing of the chessboard

It may take several experiments to make a chessboard that is easy for users to see here.
Remember to record the row number and column number in each row and column, which is convenient for users to input coordinates.

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
    
    
	int i = 1;
	int j = 1;
	printf(" ***************  扫雷  ***************\n");
	for (j = 0; j <= col; j++)
	{
    
    
		printf(" %d  ", j);     //记录行号
	}
	printf("\n\n");
	for (i = 1; i <= row; i++)  
	{
    
    
		printf(" %d  ", i);    记录列号
		for (j = 1; j <= col; j++)
		{
    
    
			printf(" %c ", board[i][j]);
			if (j != col)
			{
    
    
				printf("|");
			}
		}
		printf("\n");
		if (i != row)
		{
    
    
			printf("    ");
			for (j = 1; j <= col; j++)
			{
    
    
				printf("---");
				if (j != col)
				{
    
    
					printf("|");
				}
			}
			
		}
		printf("\n");
	}
}

6. Mine removal function

6.1 Demining

void FindMine(char mine[ROWS][COLS], 
	char show[ROWS][COLS], int row, int col)
{
    
    
	int x = 0;
	int y = 0;
	int win = 0;
	char ch = 0;
	while (win < row * col - EASY_COUNT)   
	{
    
    
		printf("请输入需要排除的坐标:>");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y <= col && y >= 1)
		{
    
    
			if (show[x][y] == '*')          //判断该坐标是否已经被排除
			{
    
    
				if (mine[x][y] == '1')      //判断是否为雷
				{
    
    
					printf("你输了,游戏结束\n");
					DisplayBoard(mine, ROW, COL);
					return;
				}
				else
				{
    
    
					//记录排除坐标的个数
					win += Explode_spread(show , mine , row , col , x ,y);
					DisplayBoard(show, ROW, COL);
				}
			}
			else
			{
    
    
				printf("该坐标已被占用\n");
			}
		}
		else
		{
    
    
			printf("输入的坐标非法\n");
		}

		printf("是否需要标记地址,若需要则输入'Y',否则则输入’N'\n");
		while ((ch = getchar()) != '\n'); //清理缓存区
		scanf("%c", &ch);
		switch (ch)
		{
    
    
		case 'Y':
			SignMine(show, row, col);
			break;
		case 'N':
			break;
		}
	}

	if (win == row * col - EASY_COUNT)
	{
    
    
		printf("恭喜你,游戏胜利\n");
	}
}

6.2 Record the number of mines around

The method of counting the number of mines here is very clever.
If you use other definition mines, it will be a bit troublesome to judge all the eight surrounding coordinates, but here you '1'only need to add the values ​​​​of the eight coordinates and 8 * '0'subtract them to get the number of mines directly.

int MineCount(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';
}

6.3 Continuous expansion of Feilei

insert image description here
insert image description here

In the minesweeper game, when there is no mine in the eight coordinates around the checked coordinates , the coordinates will be displayed as blanks on the show board , and then the scope of the checked will be expanded, and the eight surrounding coordinates will be judged as before until they meet Stop when there are mines around, and record the number of mines around on the show board .

Note:
(1) The function we designed is a recursive function, which must be restricted by conditions, otherwise it will cause dead recursion. Here we see two overlapping positions. If no restrictions are imposed, these coordinates will be judged outward from each other, resulting in an endless loop.
(2) Pay attention to the legality of the control coordinates, and the array cannot be accessed out of bounds .
insert image description here

int Explode_spread(char show[ROWS][COLS],
	char mine[ROWS][COLS],int row,int col,int x,int y)
{
    
    
	int i = 0, j = 0;
	int win = 0;
	if (x >= 1 && x <= row && y <= col && y >= 1)
	{
    
    
		//防止数组越界
		if ((MineCount(mine, x, y) + '0') == '0')
		{
    
    
			//若该坐标周围八个坐标都没有雷,向外扩散
			show[x][y] =' ';
			for (i = x - 1; i <= x + 1; i++)
			{
    
    
				for (j = y - 1; j <= y + 1; j++)
				{
    
    
					if (show[i][j] == '*')
					{
    
    
						//判断是否该坐标已经被判断过
						Explode_spread(show, mine, row, col, i, j);
						win++;
					}

				}
			}
		}
		else
		{
    
    
			show[x][y] = MineCount(mine, x, y) + '0';
			win++;
		}
	}
	return win;
}

6.4 Mark mine

void SignMine(char show[ROWS][COLS],int row, int col)
{
    
    
	static sign = EASY_COUNT;  //标记的次数
	char option = 'Y';
	int x = 0;
	int y = 0;
	char ch = 0;
	if (sign)    //现在标记次数
	{
    
    
		while (option == 'Y')
		{
    
    
			printf("还能标记%d个坐标\n", sign);
			printf("请输入需要标记的坐标:>");
			scanf("%d%d", &x, &y);
			if (x >= 1 && x <= row && y <= col && y >= 1)
			{
    
    
				if (show[x][y] == '*')
				{
    
    
					show[x][y] = '?';
					sign--;
					DisplayBoard(show, ROW, COL);
				}
				else
				{
    
    
					printf("该坐标已被占用\n");
				}
			}
			else
			{
    
    
				printf("输入的坐标非法\n");
			}
			printf("是否需要继续标记地址,若需要则输入'Y',否则则输入’N'\n");
			while ((ch = getchar()) != '\n'); //清理缓存区
			scanf("%c", &option);
		}
	}
	else
	{
    
    
		printf("标记次数过多\n");
		return;
	}
}

2. Completion of the overall program

1. game.h file:statement

#pragma once

#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 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 FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

2. game.c file: Implementation module of Minesweeper

#include "game.h"
//初始化棋盘
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 = 1;
	int j = 1;
	printf(" ***************  扫雷  ***************\n");
	for (j = 0; j <= col; j++)
	{
    
    
		printf(" %d  ", j);    //记录行号
	}
	printf("\n\n");
	for (i = 1; i <= row; i++)
	{
    
    
		printf(" %d  ", i);    //记录列号
		for (j = 1; j <= col; j++)
		{
    
    
			printf(" %c ", board[i][j]);
			if (j != col)
			{
    
    
				printf("|");
			}
		}
		printf("\n");
		if (i != row)
		{
    
    
			printf("    ");
			for (j = 1; j <= col; j++)
			{
    
    
				printf("---");
				if (j != col)
				{
    
    
					printf("|");
				}
			}
			
		}
		printf("\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 (x >= 1 && x <= row && y <= col && y >= 1)
		{
    
    
			if (mine[x][y] == '0')
			{
    
    
				mine[x][y] = '1';
				count--;
			}
		}
	}
}

//记录一个坐标周围有多少个地雷
int MineCount(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';
}

//若一个坐标周围八个坐标都没有雷,连片展开
int Explode_spread(char show[ROWS][COLS],
	char mine[ROWS][COLS],int row,int col,int x,int y)
{
    
    
	int i = 0, j = 0;
	int win = 0;
	if (x >= 1 && x <= row && y <= col && y >= 1)
	{
    
    
		//防止数组越界
		if ((MineCount(mine, x, y) + '0') == '0')
		{
    
    
			//若该坐标周围八个坐标都没有雷,向外扩散
			show[x][y] =' ';
			for (i = x - 1; i <= x + 1; i++)
			{
    
    
				for (j = y - 1; j <= y + 1; j++)
				{
    
    
					if (show[i][j] == '*')
					{
    
    
						//判断是否该坐标已经被判断过
						Explode_spread(show, mine, row, col, i, j);
						win++;
					}

				}
			}
		}
		else
		{
    
    
			show[x][y] = MineCount(mine, x, y) + '0';
			win++;
		}
	}
	return win;
}

//标记地雷
void SignMine(char show[ROWS][COLS],int row, int col)
{
    
    
	static sign = EASY_COUNT;
	char option = 'Y';
	int x = 0;
	int y = 0;
	char ch = 0;
	if (sign)
	{
    
    
		while (option == 'Y')
		{
    
    
			printf("还能标记%d个坐标\n", sign);
			printf("请输入需要标记的坐标:>");
			scanf("%d%d", &x, &y);
			if (x >= 1 && x <= row && y <= col && y >= 1)
			{
    
    
				if (show[x][y] == '*')
				{
    
    
					show[x][y] = '?';
					sign--;
					DisplayBoard(show, ROW, COL);
				}
				else
				{
    
    
					printf("该坐标已被占用\n");
				}
			}
			else
			{
    
    
				printf("输入的坐标非法\n");
			}
			printf("是否需要继续标记地址,若需要则输入'Y',否则则输入’N'\n");
			while ((ch = getchar()) != '\n'); //清理缓存区
			scanf("%c", &option);
		}
	}
	else
	{
    
    
		printf("标记次数过多\n");
		return;
	}
}

//排雷
void FindMine(char mine[ROWS][COLS], 
	char show[ROWS][COLS], int row, int col)
{
    
    
	int x = 0;
	int y = 0;
	int win = 0;
	char ch = 0;
	while (win < row * col - EASY_COUNT)   
	{
    
    
		printf("请输入需要排除的坐标:>");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y <= col && y >= 1)
		{
    
    
			if (show[x][y] == '*')          //判断该坐标是否已经被排除
			{
    
    
				if (mine[x][y] == '1')      //判断是否为雷
				{
    
    
					printf("你输了,游戏结束\n");
					DisplayBoard(mine, ROW, COL);
					return;
				}
				else
				{
    
    
					//记录排除坐标的个数
					win += Explode_spread(show , mine , row , col , x ,y);
					DisplayBoard(show, ROW, COL);
				}
			}
			else
			{
    
    
				printf("该坐标已被占用\n");
			}
		}
		else
		{
    
    
			printf("输入的坐标非法\n");
		}

		printf("是否需要标记地址,若需要则输入'Y',否则则输入’N'\n");
		while ((ch = getchar()) != '\n'); //清理缓存区
		scanf("%c", &ch);
		switch (ch)
		{
    
    
		case 'Y':
			SignMine(show, row, col);
			break;
		case 'N':
			break;
		}
	}

	if (win == row * col - EASY_COUNT)
	{
    
    
		printf("恭喜你,游戏胜利\n");
	}
}

3. test.c file: Test the basic functions of Minesweeper

#include "game.h"

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

void game()
{
    
    
	//定义两个11 x 11 的数组(棋盘)
	char mine[ROWS][COLS] = {
    
     0 };
	char show[ROWS][COLS] = {
    
     0 };
	//初始化棋盘
	InitBoard(mine, ROWS, COLS , '0');
	InitBoard(show, ROWS, COLS , '*');
	//打印棋盘
	DisplayBoard(show, ROW, COL);
	//布置雷
	SetMine(mine, ROW, COL);
	DisplayBoard(mine, ROW, COL);
	//排雷
	FindMine(mine, show, ROW, COL);
}


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

end

If you have any suggestions and questions, or if there are any mistakes, I hope everyone can mention them.
I hope everyone can make progress with me in the future! !
If this article is useful to you, I hope you can give me a little like!

Guess you like

Origin blog.csdn.net/qq_55401402/article/details/129604967