C language implementation of minesweeping (including recursive expansion)

Table of contents

1: The basic logic of the minesweeper game

Two: Information storage related to mine clearance

Three: The game generally realizes the logic

Four: concrete realization

(1) Initialization

(2) print

(3) Lay out mines

(4) Charley

Five: All codes

(1)game.h

(2)game.c

(3)test.c

Six: Actual Effect Demonstration


1: The basic logic of the minesweeper game

The logic of the minesweeper game is as follows:

1. When the game starts, the player will see a checkered board with a number or a mine on each grid.


2. Players need to judge which grids have mines around them by numbers. Among the eight grids around the grid where a number is located, the number of grids with mines will be displayed. For example, if the number of a grid is 2, there are two mines around it.


3. If there is a mine on a square, the player loses the game. If the player successfully marks all mines, he wins the game.


4. Players need to use their own reasoning and guessing skills to determine which squares have mines and do not need to click on it. If the player clicks on a square without mines, it will display a number or display a blank area. These The numbers and blank areas correspond exactly to the number of mines in the surrounding squares.

The logic of the entire game is to determine the position of the mines through continuous reasoning, and reveal the state of the entire board while avoiding stepping on the mines.


Two: Information storage related to mine clearance

Two pieces of information, one is Lei's information, and Lei is represented by the character '1', not by the character '0'. One is the thunder number information of the coordinates of the non-ray. We count the number of mines in the 3x3 area centered

on this point .

 

Therefore, we can design two arrays to save this information, among which the display array (show) is responsible for giving the player a reminder of the number of mines, and the mine array (mine) is responsible for storing the location information of the mines.



The character '1' is used to represent thunder because the ASCLL codes of numeric characters are continuous, and what we get by using '1'-'0' is 1. We can add up the numeric characters in the 3x3 area and subtract 7 '0's , you can get the thunder number (number character).

But if we design a 9x9 area, the 3x3 range of the border area will be out of bounds. We can design it as an 11x11 area and initialize the periphery to '0'. The periphery will not be affected during calculation. In order to be as uniform as possible, the display array is also designed as 11x11.

But if there are no mines around this position, we still need players to check the surroundings. The game efficiency is very low. We can expand around this position when there are no mines around (we will talk about it later). space.

In addition, we realize the function of marking thunder, and the corresponding display array of the marked position is changed to '!'.


Summary:

Lightning array - '0' means it's not mine, '1' means it's mine

Display array - '*' means not checked, '!' means marked but not checked, ' ' (space) means there are no mines around this position, and numeric characters represent the number of mines around this position.


Illustration:

 


Three: The game generally realizes the logic

 

 


Four: concrete realization

 

(1) Initialization

Initialize, pass the corresponding array first address and character to initialize

code:

//初始化
void InitBoard(char Board[ROWS][COLS], int row, int col, char set)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			Board[i][j] = set;
		}
	}
}

(2) print

Print, use the double cycle to print line by line, print the corresponding column number before printing the content (easy to see), and print the corresponding line number before printing a line (easy to see).

code:

//打印
void DisplayBoard(char Board[ROWS][COLS], int row, int col)
{
	int i = 0;
	//对齐就行
	printf("   ");
	for (i = 1; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		printf("%d| ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ",Board[i][j]);
		}
		printf("\n");
	}
}

(3) Lay out mines

Arrange mines, only for the inner layer 9x9 arrangement, we randomly generate coordinates, arrange for '0', and do not arrange for '1' (already arranged), and end when 10 are arranged (counter control).

code:

//布雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	//雷数
	int count = EasyCount;
	//随机坐标
	int x = 0;
	int y = 0;
	while (count)
	{
		x = rand() % row + 1;
		y = rand() % row + 1;
		//这个位置已经是雷就不修改
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			//计数器减1
			count--;
		}
	}
}

(4) Charley

Check Lei, pass the addresses of the two arrays, and enter the coordinates to see if it is mine through the mine array.


The condition for winning the game is that all non-mine locations have been checked (counter control) , and the number of non-mine locations is (number of rows in the inner layer x number of memory columns - number of mines).

There are three important points

[1] Calculate the number of mines around

Illustration:

code:

 

//计算周围雷数
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] - 7 * '0');
}

【2】Expand the location where there is no thunder around

If the number of mines around the investigation location is 0, the surrounding information will be displayed, if there are still positions with 0 mines around the location, it will continue to expand, and so on. The expanded position has also been checked, and the counter needs to be increased by 1. We can pass the address of the counter to modify it.

code:

void unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y,int* win)
{
	//合法坐标才展开
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		//计算这个位置周围雷数
		int count = GetMineCount(mine, x, y);
		//排查计数加1
		*win += 1;

		//如果这个位置周围无雷,向四周展开
		if ((count-'0') == 0)
		{
			//修改周围无雷位置为空格
			show[x][y] = ' ';
			int i = 0;

			for (i = x - 1; i <= x + 1; i++)
			{
				for (int j = y - 1; j <= y + 1; j++)
				{
					//每一个位置只能展开一次
					if (show[i][j] == '*' || show[x][y] == '!')
					{
						unfold(mine, show, row, col, i, j,win);
					}
				}
			}
		}
		//如果周围有雷,修改展示数组为雷数
		else
		{
			show[x][y] = count;
		}
	}
}

【3】Player Mark Thunder

Mark Lei, every time you finish the inspection, you will be prompted whether you need to record the location of the mine. If necessary, modify the display array to '!', but the mark is not for inspection, and this position is still legal when you check and expand later.

code:

//标记雷位置
void SignMine(char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入要标记的坐标:");
		scanf("%d %d", &x, &y);

		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] == '*')
			{
				show[x][y] = '!';
				break;
			}
			else
			{
				printf("该位置不能被标记,请重新输入:\n");
			}
		}
		else
		{
			printf("输入坐标非法,请重新输入:\n");
		}
	}
	//清空屏幕再打印
	system("cls");
	DisplayBoard(show, row, col);
}

Charley's full code:

//查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col)
{
	//保存排除了多少个不是雷的位置
	int win = 0;
	char ch = 0;
	while (win < (row*col- EasyCount))
	{
		int x = 0;
		int y = 0;
		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;
			}
			//无雷
			if(show[x][y] == '*' || show[x][y] == '!')
			{
				//展开
				unfold(mine, show, row, col, x, y,&win);
				system("cls");
				//打印展示数组
				DisplayBoard(show, ROW, COL);
				printf("需要标注地雷就输入:Y,不需要标注地雷则输入:N\n");
				//清空一下缓冲区
				while ((ch = getchar()) != '\n');
				scanf("%c", &ch);
				switch (ch)
				{
				case 'Y':
					//标记雷的位置
					SignMine(show, row, col);
					break;
				default:
					break;
				}
			}
			else
			{
				printf("你已经排查过这个位置了,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,重新输入\n");
		}
	}

	//判断是否是排雷结束跳出
	if (win == (row * col - EasyCount))
	{
		printf("恭喜你,你赢了\n");
	}
}


Five: All codes

(1)game.h


#pragma once
//可以自行修改行列数和雷的数量
//内层行数和列数
#define ROW 9
#define COL 9
//数组加上外层的实际行列
#define ROWS ROW+2
#define COLS COL+2
//雷数
#define EasyCount 10

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

//初始化
void InitBoard(char Board[ROWS][COLS], int row, int col, 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

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

//标记雷位置
void SignMine(char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入要标记的坐标:");
		scanf("%d %d", &x, &y);

		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] == '*')
			{
				show[x][y] = '!';
				break;
			}
			else
			{
				printf("该位置不能被标记,请重新输入:\n");
			}
		}
		else
		{
			printf("输入坐标非法,请重新输入:\n");
		}
	}
	//清空屏幕再打印
	system("cls");
	DisplayBoard(show, row, col);
}

//初始化
void InitBoard(char Board[ROWS][COLS], int row, int col, char set)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			Board[i][j] = set;
		}
	}
}

//打印
void DisplayBoard(char Board[ROWS][COLS], int row, int col)
{
	int i = 0;
	//对齐就行
	printf("   ");
	for (i = 1; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		printf("%d| ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ",Board[i][j]);
		}
		printf("\n");
	}
}

//布雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	//雷数
	int count = EasyCount;
	//随机坐标
	int x = 0;
	int y = 0;
	while (count)
	{
		x = rand() % row + 1;
		y = rand() % row + 1;
		//这个位置已经是雷就不修改
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			//计数器减1
			count--;
		}
	}
}

//计算周围雷数
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] - 7 * '0');
}

//展开
void unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y,int* win)
{
	//合法坐标才展开
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		//计算这个位置周围雷数
		int count = GetMineCount(mine, x, y);
		//排查计数加1
		*win += 1;
		//如果这个位置周围无雷,向四周展开
		if ((count-'0') == 0)
		{
			//修改周围无雷位置为空格
			show[x][y] = ' ';
			int i = 0;
			for (i = x - 1; i <= x + 1; i++)
			{
				for (int j = y - 1; j <= y + 1; j++)
				{
					//每一个位置只能展开一次
					if (show[i][j] == '*' || show[x][y] == '!')
					{
						unfold(mine, show, row, col, i, j,win);
					}
				}
			}
		}
		//如果周围有雷,修改展示数组为雷数
		else
		{
			show[x][y] = count;
		}
	}
}

//查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col)
{
	//保存排除了多少个不是雷的位置
	int win = 0;
	char ch = 0;
	while (win < (row*col- EasyCount))
	{
		int x = 0;
		int y = 0;
		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;
			}
			//无雷
			if(show[x][y] == '*' || show[x][y] == '!')
			{
				//展开
				unfold(mine, show, row, col, x, y,&win);
				system("cls");
				//打印展示数组
				DisplayBoard(show, ROW, COL);
				printf("需要标注地雷就输入:Y,不需要标注地雷则输入:N\n");
				//清空一下缓冲区
				while ((ch = getchar()) != '\n');
				scanf("%c", &ch);
				switch (ch)
				{
				case 'Y':
					//标记雷的位置
					SignMine(show, row, col);
					break;
				default:
					break;
				}
			}
			else
			{
				printf("你已经排查过这个位置了,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,重新输入\n");
		}
	}

	//判断是否是排雷结束跳出
	if (win == (row * col - EasyCount))
	{
		printf("恭喜你,你赢了\n");
	}
}

(3)test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//菜单
void menu()
{
	printf("****************\n");
	printf("**** 1.play ****\n");
	printf("**** 0.exit ****\n");
	printf("****************\n");
}

//游戏主逻辑
void game()
{
	//每一轮新游戏开始都清空屏幕
	system("cls");
	//雷数组
	char mine[ROWS][COLS] = { 0 };
	//展示数组
	char show[ROWS][COLS] = { 0 };

	//初始化
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');

	//开始打印
	printf("扫雷游戏--------\n");
	DisplayBoard(show, ROW, COL);

	//布雷
	SetMine(mine, ROW, COL);
	
	//查雷
	//这里打印雷数组是为了方便查看
	//DisplayBoard(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:
			break;
		default:
			printf("非法输入,重新输入\n");
			break;
		}
	} while (input);
}

Six: Actual Effect Demonstration

 

Guess you like

Origin blog.csdn.net/2301_76269963/article/details/130607218