【C语言】扫雷游戏(自动递归排查周围8格)

发布于:2023-01-15 ⋅ 阅读:(399) ⋅ 点赞:(0)

目录

1.基本思路

2.难点

        2.1数组的设置

        2.2 使用随机数设置雷

        2.3统计雷的个数

        2.4递归自动扩展无雷格子周围格子

        2.5判断游戏是否结束

3.源代码

        3.1头文件 game.h

        3.2源文件 扫雷.c

        3.3源文件 子函数.c

4.遗留问题

        4.1 没有实现标记功能

        4.2没有实现用鼠标操作


1.基本思路

        扫雷本质上是两个二维数组,一个二维数组是表(是我们进行扫雷操作的界面)(命名为Scan[][]),另一个二维数组是里(存放着本局随机生成的雷的位置信息)(明明为Bomb[][])。

        扫雷程序需要实现的功能包括:

        1.初始化两个数组。

        2.随机布置本局中雷的位置 。

        3.对输入坐标进行判断。 3.1.该位置是雷。

                                                3.2该位置不是雷。3.2.1.显示该位置及周围8格雷的数量。

        4.判断游戏是否结束。

2.难点

        2.1数组的设置

        当玩家输入一个坐标时,需要遍历该坐标周围的8个并计算所有雷的数量,将其显示在改坐标上。

        当玩家输入的坐标位于边角时,坐标周围不足8格,这就会导致数组的溢出。因此我们设置的数组行(列)数=想要设置的棋盘行(列)数+2。

             因此,定义常量如下:

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define BOOM 10

        2.2 使用随机数设置雷

        使用rand()函数设置随机数,需要先使用srand()函数设置随机数种子。可以用时间作为随机数种子。

        使用rand()、srand()函数时,需要引用的头文件为#include<stdlib.h>

        使用time()函数时,需要应用的头文件为#include<time.h>

        因此,随机数的生成代码如下:

#include<stdlib.h>
#include<time.h>
srand((unsigned int)time(NULL));
int ran = rand();

        随机生成雷的行数应该在1~ROW(行数)之间,列数应该在1~COL(列数)之间。使用%x取余数时,随机数的范围为0~x-1。所以行数和列数生成的代码如下:

i = rand() % row + 1;
j = rand() % col + 1;

        2.3统计雷的个数

        当玩家输入一个坐标时,需要判断该位置是不是雷。如果这个位置不是雷,需要遍历并统计该位置周围8格中雷的数量。

        坐标及周围8格的坐标如下:

         所以使用循环[ x - 1 + i ][ y - 1 + j ],就可以遍历这9个格子。

        因为数组的中的雷(1)和空格子(0)是用char类型存储的,但统计的雷的个数应该是int类型的,所以我们该如何将char类型和int类型联系起来呢?

        答案是ASCII码,'0'的ASCII码为48,'1'的ASCII码为49,差值正好为1,这样就可以代替雷的个数了。

        统计函数代码如下:

int CountBomb(char bomb[ROWS][COLS], int x, int y)
{
	int i, j;
	int result = 0;
	for (i = 0; i <= 2; i++)
	{
		for (j = 0; j <= 2; j++)
		{
			result = result + bomb[x - 1 + i][y - 1 + j] - '0';
		}
	}
	return result;
}

        2.4递归自动扩展无雷格子周围格子

        在这个游戏中,当一个位置及周围8个格子没有雷时,该位置显示的数字为0。但这个信息对于排查周围雷的个数并没有任何帮助。因此我们希望实现,当一个位置及周围都没有雷时,排查的范围就自动以该坐标周围的8个格子为起点,向外遍历,直到某一位置的显示的数字不再为0。

        递归扩展无雷格子周围格子函数代码如下:

//2.4.2扩展无雷格子周围格子函数
void ExpandBoard(char bomb[ROWS][COLS], char scan[ROWS][COLS], int x, int y)
{
	//统计周围8格雷的个数
	int count = CountBomb(bomb, x, y);
	//周围8格有雷
	if (count != 0 && scan[x][y] == '*')
	{
		scan[x][y] = count + '0';
	}
	//周围8格无雷,递归,自动向外扩展排查
	if (count == 0 && scan[x][y] == '*')
	{
		scan[x][y] = ' ';
		if (scan[x - 1][y - 1] == '*' && x - 1 > 0 && x - 1 <= ROW && y - 1 > 0 && y - 1 <= COL)
			ExpandBoard(bomb, scan, x - 1, y - 1);
		if (scan[x - 1][y] == '*' && x - 1 > 0 && x - 1 <= ROW && y > 0 && y <= COL)
			ExpandBoard(bomb, scan, x - 1, y);
		if (scan[x - 1][y + 1] == '*' && x - 1 > 0 && x - 1 <= ROW && y + 1 > 0 && y + 1 <= COL)
			ExpandBoard(bomb, scan, x - 1, y + 1);
		if (scan[x][y - 1] == '*' && x > 0 && x <= ROW && y - 1 > 0 && y - 1 <= COL)
			ExpandBoard(bomb, scan, x, y - 1);
		if (scan[x][y] == '*' && x > 0 && x <= ROW && y > 0 && y <= COL)
			ExpandBoard(bomb, scan, x, y);
		if (scan[x][y + 1] == '*' && x > 0 && x <= ROW && y + 1 > 0 && y + 1 <= COL)
			ExpandBoard(bomb, scan, x - 1, y + 1);
		if (scan[x + 1][y - 1] == '*' && x + 1 > 0 && x + 1 <= ROW && y - 1 > 0 && y - 1 <= COL)
			ExpandBoard(bomb, scan, x + 1, y - 1);
		if (scan[x + 1][y] == '*' && x + 1 > 0 && x + 1 <= ROW && y > 0 && y <= COL)
			ExpandBoard(bomb, scan, x + 1, y);
		if (scan[x + 1][y + 1] == '*' && x + 1 > 0 && x + 1 <= ROW && y + 1 > 0 && y + 1 <= COL)
			ExpandBoard(bomb, scan, x + 1, y + 1);
	}
}

        2.5判断游戏是否结束

        每次玩家输入坐标结束后,对scan[][]数组进行遍历,当'*'的数量等于设置的雷的数量时,扫雷成功。

        注:(不会存在*下面包括雷和空格的情况,因为雷和*的数量是对应的,如果存在雷和空格,说明必有一颗雷被踩了,这样数才是对的。但是踩到雷就会立即结束,不会进行到这一步判断了。)

//遍历,Scan[][]==*的数量
for (i = 1; i <= ROW; i++)
{
	for (j = 1; j <= COL; j++)
    {
	    if (scan[i][j] == '*')
		win++;
	}
}

3.源代码

        3.1头文件 game.h

#pragma once

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define BOOM 10

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

void menu();
void game();
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
void DisplayBoard(char board[ROWS][COLS], int row, int col);
void FindBomb(char bomb[ROWS][COLS], char scan[ROWS][COLS], int row, int col);
int CountBomb(char bomb[ROWS][COLS], int x, int y);
void ContinueMenu();
void ExpandBoard(char bomb[ROWS][COLS], char scan[ROWS][COLS], int x, int y);

        3.2源文件 扫雷.c

#define _CRT_SECURE_NO_WARNINGS

//扫雷游戏
#include"game.h"

int main()
{
	int input;
	srand((unsigned int)time(NULL));
	menu();
again:
	printf("请选择:");
	scanf("%d",&input);
	switch (input)
	{
	case 1:
		game();
		ContinueMenu();
		goto again;
		break;
	case 2:
		printf("******退 出 游 戏******\n");
		break;
	default:
		printf("**输入无效,请重新输入**\n");
		printf("\n");
		goto again;
	}
	return 0;
}

        3.3源文件 子函数.c

#define _CRT_SECURE_NO_WARNINGS
 
#include"game.h"

//1.菜单函数
void menu()
{
	printf("*********扫 雷*********\n");
	printf("****1.Play   2.Exit****\n");
	printf("***********************\n");
}


//2.游戏函数
void game()
{
	printf("\n");
	printf("******开 始 游 戏******\n");
	//存放随机生成的雷的数组
	char Bomb[ROWS][COLS] = {0};
	//存放排查出的雷的数组
	char Scan[ROWS][COLS] = { 0 };
	//2.1初始化
	InitBoard(Bomb, ROWS, COLS, '0');
	InitBoard(Scan, ROWS, COLS, '*');
	//2.2展示棋盘
	DisplayBoard(Scan, ROW, COL);
	//2.3布置雷
	SetBomb(Bomb, ROW, COL);
	//展示雷的位置
	//DisplayBoard(Bomb, ROW, COL);
	//2.4查找雷
	FindBomb(Bomb, Scan, ROW, COL);
}

//2.1初始化函数
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i, j;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

//2.2棋盘展示函数
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i, j;
	//打印行号
	for (i = 0; i <= col; i++)
	{
		printf("%2d", i);
	}
	printf("\n");
	//打印分割线
	//打印列号&棋盘
	for (i = 1; i <= row; i++)
	{
		printf("%2d",i);
		for (j = 1; j <= col; j++)
		{
			printf(" %c", board[i][j]);
		}
		printf("\n");
	}
}

//2.3布置雷函数
void SetBomb(char board[ROWS][COLS],int row, int col)
{
	int i, j, k;
	for (k = 0; k < BOOM;)
	{
		i = rand() % row + 1;
		j = rand() % col + 1;
		if (board[i][j] == '0')
		{
			board[i][j] = '1';
			k++;
		}
		else
		{
			;
		}
	}
}

//2.4查找雷函数
void FindBomb(char bomb[ROWS][COLS], char scan[ROWS][COLS], int row, int col)
{
	int i, j;
	int x, y;
	int win = 0;
	while (win != BOOM)
	{
		win = 0;
		//1.输入坐标
		printf("请输入要排查的坐标:");
		scanf("%d%d", &x, &y);
		//1.1坐标有效
		if (x > 0 && x <= row && y > 0 && y <= col)
		{
			//1.1.1踩雷
			if (bomb[x][y] == '1')
			{
				printf("*****踩雷,游戏结束*****\n");
				DisplayBoard(bomb, ROW, COL);
				break;
			}
			//1.1.2不是雷
			else
			{
				ExpandBoard(bomb, scan, x, y);
			}
			DisplayBoard(scan, ROW, COL);
			//DisplayBoard(bomb, ROW, COL);
			//遍历,Scan[][]==*的数量
			for (i = 1; i <= ROW; i++)
			{
				for (j = 1; j <= COL; j++)
				{
					if (scan[i][j] == '*')
						win++;
				}
			}
		}
		//1.2坐标无效
		else
			printf("坐标无效,");
	}
	if (win == BOOM)
	{
		printf("***恭喜你,排雷成功!***\n");
		DisplayBoard(bomb, ROW, COL);
	}
}

//2.4.1统计周围8格雷的个数函数
int CountBomb(char bomb[ROWS][COLS], int x, int y)
{
	int i, j;
	int result = 0;
	for (i = 0; i <= 2; i++)
	{
		for (j = 0; j <= 2; j++)
		{
			result = result + bomb[x - 1 + i][y - 1 + j] - '0';
		}
	}
	return result;
}

//2.4.2扩展无雷周围格子函数
void ExpandBoard(char bomb[ROWS][COLS], char scan[ROWS][COLS], int x, int y)
{
	//统计周围8格雷的个数
	int count = CountBomb(bomb, x, y);
	//周围8格有雷
	if (count != 0 && scan[x][y] == '*')
	{
		scan[x][y] = count + '0';
	}
	//周围8格无雷,递归,自动向外扩展排查
	if (count == 0 && scan[x][y] == '*')
	{
		scan[x][y] = ' ';
		if (scan[x - 1][y - 1] == '*' && x - 1 > 0 && x - 1 <= ROW && y - 1 > 0 && y - 1 <= COL)
			ExpandBoard(bomb, scan, x - 1, y - 1);
		if (scan[x - 1][y] == '*' && x - 1 > 0 && x - 1 <= ROW && y > 0 && y <= COL)
			ExpandBoard(bomb, scan, x - 1, y);
		if (scan[x - 1][y + 1] == '*' && x - 1 > 0 && x - 1 <= ROW && y + 1 > 0 && y + 1 <= COL)
			ExpandBoard(bomb, scan, x - 1, y + 1);
		if (scan[x][y - 1] == '*' && x > 0 && x <= ROW && y - 1 > 0 && y - 1 <= COL)
			ExpandBoard(bomb, scan, x, y - 1);
		if (scan[x][y] == '*' && x > 0 && x <= ROW && y > 0 && y <= COL)
			ExpandBoard(bomb, scan, x, y);
		if (scan[x][y + 1] == '*' && x > 0 && x <= ROW && y + 1 > 0 && y + 1 <= COL)
			ExpandBoard(bomb, scan, x - 1, y + 1);
		if (scan[x + 1][y - 1] == '*' && x + 1 > 0 && x + 1 <= ROW && y - 1 > 0 && y - 1 <= COL)
			ExpandBoard(bomb, scan, x + 1, y - 1);
		if (scan[x + 1][y] == '*' && x + 1 > 0 && x + 1 <= ROW && y > 0 && y <= COL)
			ExpandBoard(bomb, scan, x + 1, y);
		if (scan[x + 1][y + 1] == '*' && x + 1 > 0 && x + 1 <= ROW && y + 1 > 0 && y + 1 <= COL)
			ExpandBoard(bomb, scan, x + 1, y + 1);
	}
}



//3.继续菜单
void ContinueMenu()
{
	printf("\n");
	printf("*****是否继续游戏?*****\n");
	printf("***1.Continue 2.Exit***\n");
	printf("***********************\n");
}

4.遗留问题

        4.1 没有实现标记功能

        4.2没有实现用鼠标操作

       码力不够,没能实现用鼠标进行操作。

        也是因为这个原因,我觉得每次输入坐标之前都要选择这次是要“标记”还是“排查”,这样未免泰国繁琐了,所以决定放弃“标记”功能。


网站公告

今日签到

点亮在社区的每一天
去签到