[C Language] [Game] Minesweeper

Related instructions>>>

        As we all know, "Minesweeper" is an extremely classic game, I believe everyone has played it. Let's start to teach you how to implement the minesweeper game simply in C language.

        Before starting, I recommend you to read the previous blog I wrote (click here—>)  [C Language] [Game] Sanbang   wrote 8,950 words, which is super detailed, so I may not go into too much detail about some repeated details in this article, otherwise the number of words will be much larger (^ ^ cover your face.jpg).

        Before writing, let’s briefly introduce the game rules of minesweeping: the player enters the coordinates of the mines to be checked. If there are mines in the position, the game ends and the player fails the challenge ;

              I also divided this minesweeper code into three files: game.h \ game.c \ test.c, respectively writing the declaration and definition of the custom function and the implementation of the entire program. Then everyone remembers to write and test while writing, so that if there are mistakes, it will be easy to correct.

        For the convenience of the following description. We refer to the "operating surface" as the "chessboard".

Implementation ideas of minesweeping code >>>

        Let’s first analyze the components and actions of the minesweeper game: prompts for the start and end of the game, the chessboard , the computer laying mines, printing the chessboard, players clearing mines , and judging the winner.

HINTS FOR GAME START AND END >>>

        Similarly, in order to facilitate the player to exit the game and improve the game experience, a function menu needs to be written here to allow the player to choose whether to start the game .

//像这样自定义一个功能单一的函数menu只负责打印选项单
 
void menu()
{
	printf("********************\n");
	printf("****** 1.play ******\n");
	printf("****** 0.exit ******\n");
	printf("********************\n");
}

        Then write the start and end execution code of the game, first complete the largest logic, and put the implementation of the game details into the game function.

//当玩家选择1后开始游戏,进入 game函数

void test()
{
	int i = 0;
	do
	{
		menu();
		scanf("%d", &i);
		if (1 == i)
		{
			printf("开始游戏 >\n"); //好的游戏体验感,
			game();					//给个提示,不会太突然
			break;
		}
		else if(i!=0)  //这里注意美观
			printf("输入错误,请重新输入:\n");

	} while (i);
	printf("退出游戏。\n");
}


//当玩家不想玩后选择0,即结束游戏

        else if It should be judged as not equal to 0 here, because if you directly else, then if the player enters 0, an input error will occur, please re-enter: this sentence, and then it will print and exit the game. This sentence. If there is no else if to print if the judgment is not 0, then both of these two sentences will appear unsightly, as shown in the comparison diagram below.

(So ​​you have to pay attention that the do while loop is executed once)

else

 else if

 chessboard>>>

       Let's first imagine the possibility of the chessboard (emphasis) >>>

       Let's start simple, setting up a 9 x 9 board, defining rows and columns.

#define ROW 9
#define COL 9

        Then the player's operation is to input coordinates, so correspondingly we should also use a two-dimensional array. We put 0 for no mines, 1 for mines, '*' for not checked, and numeric characters to indicate (checked) the number of mines found in a circle around the location.

        Here, 0 means no thunder, and 1 means thunder. The reason for choosing to use numbers uniformly is because of convenience. Compared with using a variety of special characters, people are more accustomed to using sequential numeric characters for replacement memory. And it is convenient to calculate the information of the detected mines.

        The use of '*' to indicate that it has not been checked is to maintain the mystery of the game. The intuitive visual impact strongly implies that the situation of this location is unknown. If you use numbers, it will be confusing with Lei's information, and the rookie player may complain about being too ugly, and then quit the game directly. (Game Experience Sense of Game Experience) If a space is used, the player may think that there is no mine to be arranged, but there is no feeling of putting something in the position. And the sense of accomplishment of eliminating all the '*' on the screen is also what players need. (hey-hey)

        Then we need to pay attention here that we have to create two arrays, one for mines and one for mines, right?

        Here you may want to ask, I only use one array, and treat a special character as a mine, can't I directly put the information of the mine in other places? Then you print it for the player to see? If there is only one array, then printing will print out Lei also, but the player needs to see the entire board to select the coordinates. So we need to create a mine array only for computer operation (laying mines), hidden from players. The show array is responsible for printing out mines for the player.

Then the show array must have a one-to-one         correspondence with the mine array to store the investigation information of the corresponding coordinates. That is, the show array has to be the same size as the mine array.

        Two chessboards are thought out, but what about the board size? Is it really the 9 x 9 you see ?

        Because mine clearance is a circle around the coordinates, how to arrange those edge coordinates? Is it the same as the intermediate coordinates to judge the whole circle directly? Then in 9 x 9, it will be judged that the edge (1,5) -> (0,5) of these edge coordinates obviously does not have the coordinate (0,5). At this time, an out-of-bounds access of the array will occur, and the compiler will directly report an error.  

        If these edge coordinates are specially judged, then there will be coordinates at the edge of the row and the edge of the column, and different special judgment situations will appear, which is obviously more troublesome.

        So a better way is to expand a circle, with one more row and one column at the top, bottom, left, and right. becomes 11 x 11 . In this way, you can use the same full-circle search function for the original (1,5) coordinates , and use the same operation to judge each element, which will be simplified, as long as you do not display these expanded ranges when printing .

        All of the above must be thought out first, otherwise there may be a lot of things that need to be changed later if you write directly.

        Start writing, start writing! I’ll say it again if it’s important: I recommend everyone to read my previous blog first (click here—>)  [C Language] [Game] 3bang   wrote 8950 words, which is super detailed, so I may not go into too much detail about some repetitive details in this article, otherwise the number of words will be much larger (^ ^ cover your face.jpg).

Think about it, start writing >>>

        First create and initialize the chessboard (two-dimensional array).

        It can be seen from the above that the maximum range of the chessboard we need to use is 11 x 11, so the array we create must be 11 x 11. When using 9 x 9 later, it is good to start accessing from subscript 1. Define the rows and columns first like this

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

         The parameter char ret needs to be passed here because we want to initialize both the mine array and the show array. The difference between the two arrays is only the initialization content, so we can use the same function for the two arrays by using the initialization content as a parameter. (The ret parameter indicates the initialization content, the mine array ret = '0'  is not buried , the show array ret = '*'  is not checked )

//初始化棋盘
void init_board(char arr[ROWS][COLS], int rows, int cols, char ret)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			arr[i][j] = ret;
		}
	}
}

        After the initialization, we usually print it out to see if it is correct. So next, write the function responsible for printing.

Print chessboard >>>

         Among the parameters of the function here, the two-dimensional array is written as 11x11 because the array we defined is 11x11, and it is impossible to pass only 9x9 out of it when passing parameters, so that the compiler does not know that what you want to pass is the middle 9x9. Passing 9 in the row and column next to it means that you will operate 9x9.

        The following ranks are all passed 9x9, and the extra circle only exists to prevent out-of-bounds access, so except for access, no redundant operations will be performed on them in the future.

//打印看看
void show_board(char arr[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 ",arr[i][j]);
		}
		printf("\n");
	}
	printf("-----------扫雷-----------\n");
}

        Similarly, the dividing lines and ranks and columns are printed here. It is important to note that the game experience is very important.

      (This printing function must be used before and after every mine clearance.) Wait, remember to see clearly which chessboard is printed when printing.

 Here is a brief look at the initialization results: (there is a final rendering below)

   

Computer buried mine>>>

        First define MINE to represent the number of mines, use random numbers (large uncertainty), so when burying, you have to judge whether the position has been buried .

//埋雷
void set_mine(char arr[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int count = MINE;
	while (count)
	{
		x = rand() % row + 1; //1~9
		y = rand() % row + 1;

		if ('0' == arr[x][y])
		{
			arr[x][y] = '1'; //'1'表示雷
			count--;
		}
	}
}

Player demining , judge win or lose>>>

        It's time for the hand skating novice players to perform again...

        Then judge the end of the game, either win or be bombed. After the end, the mine must be printed out to let the player see the position of all mines.

        It's easy to be bombed, just jump out of the loop. In fact, winning is the same, winning means avoiding mines and finishing all positions. If you haven't been bombed all the time, how can you jump out of the loop after the row is finished?

        The only thing that can be controlled here is the number of rows , because the size of the board is fixed 9x9 minus the 10 mines we buried, and the row * col - MINE (here is 71) times can be arranged at most. So set a variable win and add one for each row until the maximum number of rows is reached and the loop ends.

        emmmmm It is not necessary to use thunder as a parameter but to use ranks as parameters because the number of mines remains unchanged, while ranks with 9 and 11 will change. Using ranks as parameters intuitively tells us which ranks (that kind of chessboard) to operate.

//排查雷
void find_mine(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 - MINE)
	{
		printf("请按行列顺序输入要排查的坐标:\n");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
		{
			if ('1' == mine[x][y])
			{
				printf("是雷!被炸了!!!\n");
				show_board(mine, ROW, COL);				
				break;
			}
			else
			{
				int count = get_mine_count(mine, x, y); //函数返回值是 int 类型
				show[x][y] = count + '0';			//数组存储的是 char 类型
				show_board(show, ROW, COL);
				win++;
			}
		}
		else
			printf("坐标非法,请重新输入:");
	}

//下面因为需要打印这句话,所以得判断赢了才打印

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

}

        For the convenience of writing, it seems that the logic is clear, and the demining information obtained is written as a function, as follows.

//得出排查后雷的信息
int get_mine_count(char arr[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = -1; i <= 1; i++)
	{
		for (j = -1; j <= 1; j++)
		{			
			if ('1' == arr[row + i][col + j])
				count++;
		}
	}

	return count;
}

        Note that the return value of this function is int type, and the array is of char type, so the int type information must be converted into char type before it can be stored in the char array. 

        According to the ASCII code table below, it can be seen that the ASCII code values ​​of the numeric characters 0~9 increase sequentially. So we can write show[x][y] = count + '0'; to convert int information into corresponding numeric characters.

Total code >>>

//game.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#define ROW 9
#define COL 9

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

#define MINE 10

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//初始化棋盘
void init_board(char arr[ROWS][COLS], int row, int col, char ret);

//打印看看
void show_board(char arr[ROWS][COLS], int row, int col);

//电脑埋雷
void set_mine(char arr[ROWS][COLS], int row, int col);


//玩家排雷    //判断游戏结束
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col);

It is more intuitive to put the menu function and game function in test.c.

//test.c


#include"game.h"

void menu()
{
	printf("***********************\n");
	printf("*******  1.play  ******\n");
	printf("*******  0.exit  ******\n");
	printf("***********************\n");
}
void game()
{
	srand((unsigned int)time(NULL));
	//初始化二维数组
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	//使用的是11*11,调用的是其中的9*9
	
	//初始化棋盘
	init_board(mine, ROWS, COLS, '0');
	init_board(show, ROWS, COLS, '*');

	//打印看看

	//show_board(mine, ROW, COL);
	//注意雷是不打印给玩家看的,
	//但我们写的时候可以打印出来看看是否写对了。

	show_board(show, ROW, COL);

	//埋雷
	set_mine(mine, ROW, COL);
	//show_board(mine, ROW, COL);

	//排查雷
	find_mine(mine, show, ROW, COL);

}
void test()
{
	int i = 0;
	do
	{
		menu();
		scanf("%d", &i);
		if (1 == i)
		{
			printf("开始游戏 >\n"); //好的游戏体验感,
			game();					//给个提示,不会太突然
			break;
		}
		else if(i!=0)  //这里注意美观
			printf("输入错误,请重新输入:\n");

	} while (i);
	printf("退出游戏。\n");
}

int main()
{

	test();

	return 0;
}
//game.c

#include "game.h"

//初始化棋盘
void init_board(char arr[ROWS][COLS], int rows, int cols, char ret)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			arr[i][j] = ret;
		}
	}
}

//打印看看
void show_board(char arr[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 ",arr[i][j]);
		}
		printf("\n");
	}
	printf("-----------扫雷-----------\n");
}

//埋雷
void set_mine(char arr[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int count = MINE;
	while (count)
	{
		x = rand() % row + 1; //1~9
		y = rand() % row + 1;

		if ('0' == arr[x][y])
		{
			arr[x][y] = '1'; //'1'表示雷
			count--;
		}
	}
}

//得出排查后雷的信息
int get_mine_count(char arr[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = -1; i <= 1; i++)
	{
		for (j = -1; j <= 1; j++)
		{			
			if ('1' == arr[row + i][col + j])
				count++;
		}
	}

	return count;
}

//排查雷
void find_mine(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 - MINE)
	{
		printf("请按行列顺序输入要排查的坐标:\n");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
		{
			if ('1' == mine[x][y])
			{
				printf("是雷!被炸了!!!\n");
				show_board(mine, ROW, COL);				
				break;
			}
			else
			{
				int count = get_mine_count(mine, x, y); //函数返回值是 int 类型
				show[x][y] = count + '0';			//数组存储的是 char 类型
				show_board(show, ROW, COL);
				win++;
			}
		}
		else
			printf("坐标非法,请重新输入:");
	}
	if (win == row * col - MINE)
	{
		printf("恭喜你,排雷成功\n");
		show_board(mine, ROW, COL);
	}

}

The final rendering >>>

 ......

 In this way, everyone can write and share with friends, maybe they will try the small game you wrote by yourself! ^ ^

Thoughts after writing >>>

        Know enough about what you're going to write about, about its basic components and operations. Think about this, and think about the little accidents that might happen. Just like this minesweeper, if the defined two-dimensional array is 9x9, there are too many problems, and you have to think of a small way to add a circle to it without printing it.

        Also, I think the most important thing about a game is the sense of experience, so it should be more beautiful when writing.

The most important words >>>

           If you don’t understand, remember to private message me ^ ^.

        (You can also leave a message directly in the comment area^ ^)

Guess you like

Origin blog.csdn.net/zoe23333/article/details/123768545