[C Language] Minesweeper Game (Nanny Tutorial)

Table of contents

1. Introduction to Minesweeper Game

2. Code packaging

3. Code implementation steps

1. Create menu function and game running logic flow

2. Array chessboard analysis

3. Create a checkerboard array

 4. Initialize the chessboard InitBoard function

5. Display the chessboard DisplayBoard function

6. Arrange the SetMine function

 7. GetMineCount function to count the number of mines

 8. Troubleshoot the FindMine function

4. The complete code of Minesweeper


1. Introduction to Minesweeper Game

"Minesweeper" is a popular puzzle game released in 1992. The goal of the game is to find all the non-ray grids in the shortest time according to the numbers that appear on the clicked grids, while avoiding stepping on mines. If you step on one mine, you will lose everything.

2. Code packaging

Source File:

test.c: Tests related to the entire game

game.c: implements game-related functions

head File:

game.h: declaration of related functions, header files and macro definitions to be referenced by the entire code

3. Code implementation steps

1. Create menu function and game running logic flow

  • First, define a menu function in test.c to print the menu, prompting player 1 to start the game and player 0 to exit the game.
  • scanf receives the player's input and uses switch to judge. 1 means starting the game, 0 means exiting the game, and if it is not 1 or 0, it will prompt that the selection is wrong and select again. In order to allow the user to reselect and start another game after ending a game, they need to be included using a do while statement.
void menu()
{
	printf("*********************\n");
	printf("*****  1. game  *****\n");
	printf("*****  0. exit  *****\n");
	printf("*********************\n");
}

void game()
{
	printf("扫雷\n");
}

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

2. Array chessboard analysis

Assume that the required chessboard size is 9*9. If it is not a thunder, it is represented by 0, and if it is a thunder, it is represented by 1.

For the convenience of operation, two chessboard arrays are defined here, mine and show. The mine chessboard is used to store mine information and is initialized to 0, while the show chessboard is used to print to players and is initialized to *. At the same time, the types stored in the two chessboards are uniformly set to the char type, which is a character array, for convenient operation.

  • Assume that mines have been placed on the mine board, and then assume that when checking the area at coordinates (7,6), if it is not a mine, the system needs to return the number of mines in the 8 areas around coordinates (7,6) (that is, the 8 yellow areas ), at this time 1 needs to be returned at coordinates (7,6), and if the number of returned mines 1 is placed in (7,6) of the mine board, it will easily be ambiguous with the 1 representing mines , so the approach here is to put The number of thunders, 1, is stored in the show board.
  • There is another problem here. If you check the area at coordinates (4,9), for example, part of the area exceeds the array (that is, the red area). In order to prevent the array from going out of bounds , each surrounding area needs to be judged whether it exceeds the range of the array during each inspection. This operation is cumbersome and inefficient.

What I do here is to expand both chessboards by one circle (that is, the blue area). At this time, the array size is 11*11 . The advantage of this is that there is no need to worry about the array going out of bounds, and the position of the coordinate system can be standardized. , the coordinates can be completely matched, making code writing more convenient.

3. Create a checkerboard array

In order to facilitate modification, define ROW=9, COL=9, ROWS=ROW+2, COLS=COL+2 in game.h;

Here ROW and COL are set to 9 and ROWS and COLS are set to 11 because, although the actual usage range is 11*11, the mine area and the area shown to the player are still 9*9, so two symbols are needed to represent 11 respectively. *11, 9*9.

#define ROW 9
#define COL 9

#define ROWS ROW+2 //11
#define COLS COL+2 //11
void game()
{
	char mine[ROWS][COLS] = { 0 };//放置雷的数组
	char show[ROWS][COLS] = { 0 };//排查雷信息的数组
}

 4. Initialize the chessboard InitBoard function

Because the initialization content is variable, one is 0 and the other is *, so one more parameter needs to be added to represent the initialization content.

game.h 

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

 game.c

//初始化棋盘
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;
		}
	}
}

 test.c

void game()
{
	char mine[ROWS][COLS] = { 0 };//放置雷的数组
	char show[ROWS][COLS] = { 0 };//排查雷信息的数组
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	
}

 Next we make a display chessboard function to observe whether the initialization is successful.

5. Display the chessboard DisplayBoard function

game.h 

//显示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);

 game.c

//显示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("--------扫雷--------\n");
	for ( i = 0; i <= col; i++) //打印列号 注意这里的i是从0开始
	{
		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");
	}
	printf("--------扫雷--------\n");

}

test.c 

The area that really needs to be printed is the 9*9 area in the middle, so the parameters passed by the DisplayBoard function are ROW and COL. 

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);
	DisplayBoard(mine, ROW, COL);
}

At this time, call the display chessboard function to see whether the initialization is completed. 

It can be found that the initialization is completed, one is all * and the other is all 0.

6. Arrange the SetMine function

    Arrange the mines into the mine array on the chessboard .

    The strategy for arranging mines here is to arrange them randomly, which requires the use of random numbers. The acquisition of random numbers involves the srand function, rand function and time function. Regarding the differences between the srand function, rand function and time function, I have explained the knowledge points in previous blogs. If you are interested, you can go to: [C Language] Number guessing game - in-depth analysis of the rand function to generate random numbers_Hacynn's blog-CSDN blog icon-default.png?t=N7T8https://blog .csdn.net/zzzzzhxxx/article/details/132635456?spm=1001.2014.3001.5502 to view, I won’t go into details here.

    To use srand and rand, you need to include  the stdlib.h  header file, and to use time, you need to include  the time.h  header file. And call srand in the main function to generate random seeds, so that the rand function can generate random numbers.

 game.h

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

#define EASY_COUNT 10

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

 game.c

Use ESAY_COUNT to represent the number of mines, use a while loop to traverse the layout, and count decreases by 1 every time a mine is placed, until it exits the loop when it reaches 0.

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

test.c 

The area where mines really need to be laid is the 9*9 area in the middle, so the SetMine function parameters are still ROW and COL. 

After arranging the mines, use the DisplayBoard function to print the mine board, and the mine arranging part is completed.

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

 7. GetMineCount function to count the number of mines

Suppose you check the (x, y) coordinates. If it is not a mine, you need to count the number of mines in the 8 areas around (x, y). Next I will use a diagram to explain how to return.

The x and y in the above picture are the coordinates of the investigation, then the (1,1) area is (x-1, y-1), the (1,2) area is (x-1, y), and the others are the same . So you only need to add the values ​​​​of the 8 areas to get the number of mines.

game.c 

//统计x,y坐标周围有几个雷
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';
}

However , it should be noted that because the mine array stores character types , the returned value cannot be used directly. One character '0' needs to be subtracted to equal the corresponding number (such as the picture below) , so a total of 8 must be subtracted. '0', thereby converting from char type to int type .

 8. Troubleshoot the FindMine function

Mine troubleshooting involves two arrays. Check the number of mines in the chessboard mine array and display the number in the chessboard show array . Therefore, the FindMine function needs to pass in two arrays when passing parameters.

 game.h

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

 game.c

First, use a while loop to nest the troubleshooting operation part. When the mine is checked, it will prompt that it was blown up and print the chessboard mine.

When it is found that it is not a mine, you need to return the number of mines in the surrounding 8 areas. At this time, you need to call the GetMineCount function, and then print the chessboard show to display the number of mines. The number at this time is of int type. You need to pass it in to the show array. It is added to '0' to get the corresponding char type.

//排查雷
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");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int c = GetMineCount(mine, x, y);
				show[x][y] = c + '0'; //int转char
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,重新输入!\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功!\n");
		DisplayBoard(mine, ROW, COL);
	}
}

test.c

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

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

4. The complete code of Minesweeper

 game.h

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

#define ROW 9
#define COL 9

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

#define EASY_COUNT 10

//初始化棋盘
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);

game.c

#define _CRT_SECURE_NO_WARNINGS 1

#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 = 0;
	int j = 0;
	printf("--------扫雷--------\n");
	for ( i = 0; i <= col; i++) //打印列号 注意这里的i是从0开始
	{
		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");
	}
	printf("--------扫雷--------\n");

}

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

//统计x,y坐标周围有几个雷
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';
}

//排查雷
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");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int c = GetMineCount(mine, x, y);
				show[x][y] = c + '0'; //int转char
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,重新输入!\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功!\n");
		DisplayBoard(mine, ROW, COL);
	}
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

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

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

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

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

That’s it for this minesweeper mini-game. I believe you can also make your own three-piece chess mini-game. The most important thing about this game is code thinking rather than the code itself. Understanding code thinking can be greatly improved. .

If you think the author's writing is good, please give the author a big thumbs up to support me. Your support is my biggest motivation for updating!

Guess you like

Origin blog.csdn.net/zzzzzhxxx/article/details/132747003