C语言第十一课(上):编写扫雷游戏(综合练习2)

news2025/1/11 10:11:39

目录

前言:

一、文件建立:

        1.头文件game.h:

        2.函数定义文件game.c:

        3.工程测试文件test.c:

二、编写井字棋游戏: 

        1.程序整体执行思路:

        2.menu菜单:

        3.game游戏函数逻辑:

        4.game函数中各项功能的实现:

        ①.头文件game.h内容:

        ②.棋盘的初始化InitBoard:

        ③.打印棋盘DisplayBoard:

        ④.布置“地雷”SetMine:

        ⑤.统计雷get_mine_count:

        ⑥.排雷FindMine:

三、完整程序代码:

        1.gam.h:

        2.game.c:

        3.test.c:

四、总结:


前言:

        前面两篇文章中,各位小伙伴们写出了自己在C语言写学习中的第一个小游戏井字棋,阿銮在这里恭喜各位小伙伴辣!本文我们将进行更加复杂的阶段编程练习——编写扫雷游戏。

一、文件建立:

        同样的,我们在编写扫雷游戏时,也使用模块化开发的方式,将整段编程代码划分为game.h、game.c与test.c三个文件进行编写:

        1.头文件game.h:

        该文件用于包含宏与其它头文件,并存放功能实现函数的函数声明:

        2.函数定义文件game.c:

         该文件用于书写所有的程序功能实现的函数定义

        3.工程测试文件test.c:

        该文件用于书写我们的程序主体部分:

二、编写井字棋游戏: 

        1.程序整体执行思路:

        与之前写过的井字棋游戏整体执行思路相同,首先我们自定义两个函数,menu为菜单函数,负责向玩家打印游戏菜单game为游戏函数,负责实现整个游戏的逻辑实现。当程序开始编译运行后,将会按照顺序,先执行menu函数打印出游戏菜单,接着执行game函数让玩家们进行游戏:

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

//游戏逻辑:
void game()
{
	...
}

void test()
{
    menu();
    game();
    ...
}

int main()
{
	test();

	return 0;
}

        同时,与井字棋游戏相同,大多数时候玩家往往会选择多次进行游戏,所以我们通过在数函数部分结合我们在之前介绍过的的循环和分支语句很容易实现该目的:

void test()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏...\n");
			break;
		default:
			printf("输入错误,请重新选择!\n");
			break;
		}
	} while (input);
}

        这些步骤与井字棋游戏的整体运行逻完全相同,这里不再进行运行演示。

        2.menu菜单:

        与扫雷游戏相同,仅使用 printf 函数进行屏幕打印即可:

void menu()
{
	printf("******************************\n");
	printf("******************************\n");
	printf("*****  欢迎来到扫雷游戏  *****\n");
	printf("*****       1.play       *****\n");
	printf("*****       0.exit       *****\n");
	printf("******************************\n");
	printf("******************************\n");
}

        且编译运行结果与井字棋相同,不再进行过多阐述。

        3.game游戏函数逻辑:

        (注:此处我们同样只关心实现逻辑,具体功能实现后面会逐一进行研究说明)

        我们同样从扫雷游戏的游戏规则与胜利条件开始研究。扫雷游戏其本质为推理判断游戏,它有9×9标准游戏界面,在游戏开始时,所有方格中的内容将会被隐藏,玩家不清楚每个格子的内容,但在所有格子中,部分格子中被布置下了“地雷”,玩家需要在判断放个安全的情况下进行扫雷,在安全的格子上,若以该方格为中心的九宫格内有危险的“地雷”,该中心方格上将会显示中心九宫格内“地雷”的数量,玩家可以通过选择该位置来进行排雷,若判断失误,选择的位置上为“地雷”,则玩家输掉了该局游戏,直到玩家将全部的地雷全部排查出来,即可获得游戏的胜利。

        梳理了游戏的规则,我们开始尝试编写该游戏。

        在游戏函数中,首先我们进行棋盘的初始化。与井字棋游戏不同的是,在这里我们需要定义并初始化两个棋盘。需要两个棋盘原因是,我们对“安全”与“地雷”的区分将会使用到' 0 '和' 1 '两个字符,其中' 0 表示“安全”而' 1 '则表示“地雷”,如果我们仅仅只定义并初始化一个棋盘,当进行地雷判断时,我们将无法确定' 1 '是表示周围中心九宫格内地雷的数量还是表示该方格内为“地雷,造成我们对游戏情况判断的歧义”:

void game()
{
	char mine[ROWS][COLS] = { 0 };
	//存放布置的雷的信息
	char show[ROWS][COLS] = { 0 };
	//存放查出的雷的信息
}

        接着我们对这两个棋盘进行初始化。mine数组中用于存放布置的地雷的信息,故首先将其全部初始化为“安全”,而show数组用于打印向玩家反馈当前扫雷情况,故刚开局时全部位置均未未知,初始化为象征性占位符' * ',并在棋盘初始化完成后将反馈棋盘show进行打印

        (mine数组中存放实际布雷信息,游戏实际为操作mine数组,show数组仅供反馈排雷情况使用)

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

        在棋盘定义并初始化完成后,我们需要在我们定义的全“安全”棋盘中,放入一定数量随机位置的“地雷”

        (注:此处初始化与定义的棋盘,与进行实际操作的行与列不同,后文实现过程将会进行说明)

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

        在放置好“地雷”后,就可以让玩家开始进行“扫雷”了:

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.game函数中各项功能的实现:

        ①.头文件game.h内容:

        在我们的头文件中,存放着宏定义、引用头文件与函数的声明

        首先我们来看宏定义

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

        可以看到,在这个游戏中,我们一共定义了五个宏常量。其中行ROW和列COL定义为9,而行ROWS和列COLS定义为11。那么同样是棋盘的行和列,为什么要定义两组不同的行和列呢?这样定义的原因是为了避免出现越界,同时便于进行操作

        我们向玩家展示的棋盘是9×9的棋盘,但是当我们后面需要统计中心九宫格中地雷的数量时,倘若在操作最上或最下行(第一、九行)与最左或最右(第一、九列)时,若只定义9×9的棋盘,在对一、九行、列方格周围进行“地雷”的计数时,将会造成数组越界的错误,所以我们在定义时应当在第一行之上、第九行之下、第一列之左和第九列之右各添加一行/列,总计定义11×11的棋盘数组。(即实际定义11×11数组,展示中间的9×9数组)

        我们还定义了宏常量EASY_COUNT,用于表示在我们的一局游戏中添加的“地雷”的数量

        接着是头文件的引用

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

        整个游戏中多次使用了输入scanf 与输出printf 函数,需引用头文件stdio.h;在进行布雷时,我们需要生成随机数作为“地雷”的下标,这时需要使用srand函数来确定随机数的起点,需引用头文件stdlib.h;在确定随机数起点时,我们采用根据时间戳来确定的方式,需引用头文件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 SetMine(char mine[ROWS][ROWS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
int get_mine_count(char mine[ROWS][COLS], int x, int y);

        ②.棋盘的初始化InitBoard:

        我们前面已经定义好了两个棋盘,这里的初始化也很简单,我们采用循环遍历的方式,将每一个位置上的数据都初始化为相应符号。对于mine数组来说,该数组用于存放地雷信息,故初始化为全' 0 ';而对于show数组来说,该数组用于展示,需要隐藏方格中的信息,故全部初始化为' * '

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

        ③.打印棋盘DisplayBoard:

        我们在将棋盘初始化完成以及每一次排雷,乃至最终反馈游戏结果时,都需要将我们当前的排雷情况进行打印,反馈给玩家。同时,为了界面更加的美观,我们可以在打印棋盘前先打印一行列号,并在每一行打印前先打印一个行号。棋盘界面的打印我们同样通过遍历思想来实现:

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	printf("  --------扫雷--------\n");
	int i = 0;
	printf("| \\ 1 2 3 4 5 6 7 8 9 |\n");
    //第一列为列号,在列号最上方打印"\"时,一定注意代码应写为\\,否则\将被视为转义字符而不生效
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		printf("| %d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("|\n");
	}
	printf("  --------扫雷--------\n");
}

        我们将其编译运行起来,看看棋盘的打印效果:

        可以看到棋盘的打印已经按照我们规定的格式完成了打印操作。

        ④.布置“地雷”SetMine:

        布置地雷也很好理解,在中心的9×9棋盘中,通过使用时间戳来确定随机数的起点取随机数作为雷的下标,并放置在mine数组中

void SetMine(char mine[ROWS][ROWS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		//生成随机下标:
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		//布置雷:
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

        由于我们需要生成不止一个“地雷”,故需要进行循环,循环的判断条件我们就设置为初值等于宏常量地雷数的自定义变量count,并在每次布雷后执行减一操作,当其不为零时便判断为真不断执行循环布雷,直至最后一次布雷后变为零,判断为假循环停止

        同时,细心的小伙伴们可能会有疑问了,我们定义的棋盘为11×11,那我们怎样去确定“地雷”都被布置在中间的9×9的部分呢?首先我们看到,在我们取随机下标时,是对传入的行数和列数求余后加一求余后保证下标从零开始,再加一即保证了从和数组的第一列(0+1)开始算起,同时在传参时我们传入的是展示的行与列数:

//布置雷
SetMine(mine, ROW, COL);

        传入参数的值为9,则随机下标的上限即被限制为为9(0+9)。这样就实现了从第一行到第九行,从第一列到第九列,即中心9×9棋盘的范围限制。

        ⑤.统计雷get_mine_count:

        我们还需要一个函数来统计我们排雷位置周围的地雷数量:

//统计雷:
int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x - 1][y] + mine[x + 1][y] + mine[x - 1][y + 1] + mine[x][y + 1] + mine[x + 1][y + 1] - 8 * '0'); 
}

        其原理很好理解,将该坐标周围八个位置上的数据加在一起即可。但由于我们定义的是字符型数组,其中存放的是字符' 0 '和字符' 1 '!而不是整形1和0!在打印时若直接进行相加计算,则不会得到我们想要的结果!所以我们在计算时,应当根据其ASCII码值进行计算,将周围八个位置上的字符进行相加后,减去8个字符' 0 ',才是我们想要的得到结果,并将结果返回

        ⑥.排雷FindMine:

        在排雷时,我们同样是根据坐标进行排雷,而在输入坐标时,我们需要对坐标的合理性进行判断。我们首先要循环对行坐标进行判断,当输入坐标在中心9×9棋盘中时,我们进行下一步,否则提示玩家“您输入的行号有误,请重新输入”:

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	printf("请输入您想要排查的坐标:\n");
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("您想排查哪一行:");
		scanf("%d", &x);
		if (x >= 1 && x <= row)
		{
			;
		}
		else
		{
			printf("您输入的行号有误,请重新输入!\n");
		}
	}
}

        接着我们用同样的方式对列坐标进行判断


while (1)
{
    printf("您想排查哪一列:");
	scanf("%d", &y);
	if (y >= 1 && y <= col)
	{
	    //执行排查
		break;
	}
	else
	{
		printf("您输入的列数有误,请重新输入!\n");
	}
}

        当行与列均判断合理后进行后续操做,操作完成后使用一个break语句跳出当前坐标的操作,并进行下一次判断与操作。

        当确定坐标在范围内后,我们还需要进行一次判断,内容是要去判断此时我们要进行操作的坐标是否在之前已经被操作过,如果操作过则重新选择坐标,没有操作过才进行接下来的排雷操作:

if (show[x][y] != '*')
{
	printf("该坐标已被排查过!\n");
	DisplayBoard(show, ROW, COL);
	break;
}

        在确定了坐标合理且没有进行过操作后,我们就可以对该坐标进行排雷了:

if (mine[x][y] == '1')
{
	DisplayBoard(mine, ROW, COL);
	printf("很遗憾,您死了!\n");
	reak;
}
else
{
    int n = get_mine_count(mine, x, y);
	show[x][y] = n + '0';
	DisplayBoard(show, ROW, COL);
	break;
}

        我们进行判断,若该坐标内存放的是' 1 ',则说明该位置放置的为“地雷”,根据游戏规则,此时玩家就输掉了这场游戏,应提示玩家“很遗憾,您死了”并退出游戏;若为' 0 ',则说明此处没有“地雷”,玩家在该位置上的排雷成功

        接下来,应当在本次成功排雷的坐标上显示出以该坐标为中心的九宫格内的“地雷”数量,我们定义一个变量n来接收计数函数返回的“地雷”数量统计结果。这里同样需要注意,我们的n接收到的是经过处理的ASCII码值,我们在进行使用时,应当对该值进行逆处理,即将该结果加上字符' 0 ',才是我们期望中要打印在该坐标处的内容。

        将它们组合在一起就能实现目的了吗?我们来看:

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	printf("请输入您想要排查的坐标:\n");
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("您想排查哪一行:");
		scanf("%d", &x);
		if (x >= 1 && x <= row)
		{
			while (1)
			{
				printf("您想排查哪一列:");
				scanf("%d", &y);
				if (y >= 1 && y <= col)
				{
					if (show[x][y] != '*')
					{
						printf("该坐标已被排查过!\n");
						DisplayBoard(show, ROW, COL);
						break;
					}
					if (mine[x][y] == '1')
					{
						DisplayBoard(mine, ROW, COL);
						printf("很遗憾,您死了!\n");
						break;
					}
					else
					{
						int n = get_mine_count(mine, x, y);
						show[x][y] = n + '0';
						DisplayBoard(show, ROW, COL);
						break;
					}
				}
				else
				{
					printf("您输入的列数有误,请重新输入!\n");
				}
			}
		}
		else
		{
			printf("您输入的行数有误,请重新输入!\n");
		}
	}
}

        我们运行后发现,程序出现了问题,即使在我们“踩到地雷”后,程序仍没有结束,仍在提示我们输入下一个坐标,这是为什么呢?我们研究我们的代码后发现,我们最内层的break在执行时并没有结束最外层的循环,导致程序无休止的执行下去而不会终结。那我们该如何处理呢?

        我们的思路是,既然它现在影响不到最外层循环,那我们就想办法让它去影响最外层的循环。我们的解决方案是,在最外层循环的控制表达式上下功夫:

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	printf("请输入您想要排查的坐标:\n");
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < (row * col - EASY_COUNT))
	{
		printf("您想排查哪一行:");
		scanf("%d", &x);
		if (x >= 1 && x <= row)
		{
			while (1)
			{
				printf("您想排查哪一列:");
				scanf("%d", &y);
				if (y >= 1 && y <= col)
				{
					if (show[x][y] != '*')
					{
						printf("该坐标已被排查过!\n");
						DisplayBoard(show, ROW, COL);
						break;
					}
					if (mine[x][y] == '1')
					{
						DisplayBoard(mine, ROW, COL);
						printf("很遗憾,您死了!\n");
						win = 0;
						break;
					}
					else
					{
						int n = get_mine_count(mine, x, y);
						show[x][y] = n + '0';
						DisplayBoard(show, ROW, COL);
						win++;
						break;
					}
				}
				else
				{
					printf("您输入的列数有误,请重新输入!\n");
				}
			}
		}
		else
		{
			printf("您输入的行数有误,请重新输入!\n");
		}
	}
}

        我们定义一个变量win,当win小于空着的格子,即排雷没有全部排完时,就不断进行循环排雷,若期间“踩到地雷”,便修改win = row * col - EASY_COUNT + 1,即破坏循环条件使判断判定为假而跳出循环。若没有“踩到地雷”则将执行替换的同时将变量win加一,直至全部排完游戏胜利退出循环。

        那么为什么我们在“踩到地雷”时不让win = row * col - EASY_COUNT呢?这样不也可以破坏循环条件没?原因是,在所有的安全坐标都排完以后,此时的变量win是等于row * col -  EASY_COUNT的,我们需要根据这个条件来进行胜利条件判断:

if (win == (row * col - EASY_COUNT))
{
	printf("恭喜您排雷成功,获得胜利!\n");
	DisplayBoard(mine, ROW, COL);
}

        若“踩到地雷”时也执行这样的修改,则会造成程序语句歧义而产生错误的执行结果

        则总结排雷函数FindMine代码应当为:

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	printf("请输入您想要排查的坐标:\n");
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < (row * col - EASY_COUNT))
	{
		printf("您想排查哪一行:");
		scanf("%d", &x);
		if (x >= 1 && x <= row)
		{
			while (1)
			{
				printf("您想排查哪一列:");
				scanf("%d", &y);
				if (y >= 1 && y <= col)
				{
					if (show[x][y] != '*')
					{
						printf("该坐标已被排查过!\n");
						DisplayBoard(show, ROW, COL);
						break;
					}
					if (mine[x][y] == '1')
					{
						DisplayBoard(mine, ROW, COL);
						printf("很遗憾,您死了!\n");
						win = row * col - EASY_COUNT + 1;
						break;
					}
					else
					{
						int n = get_mine_count(mine, x, y);
						show[x][y] = n + '0';
						DisplayBoard(show, ROW, COL);
						win++;
						break;
					}
				}
				else
				{
					printf("您输入的列数有误,请重新输入!\n");
				}
			}
		}
		else
		{
			printf("您输入的行数有误,请重新输入!\n");
		}
	}
	if (win == (row * col - EASY_COUNT))
	{
		printf("恭喜您排雷成功,获得胜利!\n");
		DisplayBoard(mine, ROW, COL);
	}
}

        至此,扫雷游戏的最基础游戏功能就得到了实现。

三、完整程序代码:

        1.gam.h:

#pragma once

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS ROW+2
#define EASY_COUNT 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 SetMine(char mine[ROWS][ROWS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
int get_mine_count(char mine[ROWS][COLS], int x, int y);

        2.game.c:

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

//打印菜单:
void menu()
{
	printf("******************************\n");
	printf("******************************\n");
	printf("*****  欢迎来到扫雷游戏  *****\n");
	printf("*****       1.play       *****\n");
	printf("*****       0.exit       *****\n");
	printf("******************************\n");
	printf("******************************\n");
}

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

//打印棋盘:
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	printf("  --------扫雷--------\n");
	int i = 0;
	printf("| \\ 1 2 3 4 5 6 7 8 9 |\n");
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		printf("| %d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("|\n");
	}
	printf("  --------扫雷--------\n");
}

//布置雷:
void SetMine(char mine[ROWS][ROWS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		//生成随机下标:
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		//布置雷:
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

//统计雷:
int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x - 1][y] + mine[x + 1][y] + mine[x - 1][y + 1] + mine[x][y + 1] + mine[x + 1][y + 1] - 8 * '0'); 
}

//排雷:
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	printf("请输入您想要排查的坐标:\n");
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < (row * col - EASY_COUNT))
	{
		printf("您想排查哪一行:");
		scanf("%d", &x);
		if (x >= 1 && x <= row)
		{
			while (1)
			{
				printf("您想排查哪一列:");
				scanf("%d", &y);
				if (y >= 1 && y <= col)
				{
					if (show[x][y] != '*')
					{
						printf("该坐标已被排查过!\n");
						DisplayBoard(show, ROW, COL);
						break;
					}
					if (mine[x][y] == '1')
					{
						DisplayBoard(mine, ROW, COL);
						printf("很遗憾,您死了!\n");
						win = row * col - EASY_COUNT + 1;
						break;
					}
					else
					{
						int n = get_mine_count(mine, x, y);
						show[x][y] = n + '0';
						DisplayBoard(show, ROW, COL);
						win++;
						break;
					}
				}
				else
				{
					printf("您输入的列数有误,请重新输入!\n");
				}
			}
		}
		else
		{
			printf("您输入的行数有误,请重新输入!\n");
		}
	}
	if (win == (row * col - EASY_COUNT))
	{
		printf("恭喜您排雷成功,获得胜利!\n");
		DisplayBoard(mine, ROW, COL);
	}
}

        3.test.c:

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

//游戏逻辑:
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);
}

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

int main()
{
	test();

	return 0;
}

四、总结:

        以上就是基础版扫雷游戏的各项功能实现了,但是它也和之前我们写的井字棋游戏一样,存在着很多的缺陷,例如它并不能像我们电脑里的扫雷游戏一样,选择一个坐标会打开一片,还有同样存在着界面不够美观的问题。而诸如此类这些问题我们在下文中将会一一进行处理。

        以上就是今天我为大家介绍的基础版扫雷的知识啦!既然我已经踏上这条道路,那么,任何东西都不应妨碍我沿着这条路走下去!

        新人初来乍到,辛苦各位小伙伴们动动小手,三连走一走 ~ ~ ~  最后,本文仍有许多不足之处,欢迎各位看官老爷随时私信批评指正!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/21465.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Detectron2】代码库学习-5.标注格式- 矩形框, 旋转框,关键点, mask, 实例标注,IOU计算, 旋转框IOU计算,

文章目录Detectron2 内置的标注格式BoxMode 表示方式实用APIRotatedBoxesInstances 实例标注KeypointsMasks结语Detectron2 内置的标注格式 BoxesRotatedBoxesBitMasksPolygonMasksROIMasksKeypointsInstancesImageList BoxMode 表示方式 XYXY_ABSXYWH_ABSXYXY_REL # 相对模…

Windows安装mysql并且配置odbc

文章目录 mysql下载ODBC驱动下载安装mysql使用测试安装ODBC驱动添加ODBC数据源配置完成了用户不能远程访问的问题mysql下载 https://dev.mysql.com/downloads/installer/ ODBC驱动下载 https://dev.mysql.com/downloads/connector/odbc/ 安装mysql 点击mysql安装包,选择…

【25-业务开发-基础业务-品牌管理-图片管理-图片上传方式的三种实现方式-第三方公共服务模块集成到项目中-服务端生成签名实战】

一.知识回顾 【0.三高商城系统的专题专栏都帮你整理好了&#xff0c;请点击这里&#xff01;】 【1-系统架构演进过程】 【2-微服务系统架构需求】 【3-高性能、高并发、高可用的三高商城系统项目介绍】 【4-Linux云服务器上安装Docker】 【5-Docker安装部署MySQL和Redis服务】…

【第三部分 | 移动端开发】1:移动端基础概要

目录 | 概述 | 手机端调试 | 视口 ViewPort 三种视口 meta标签 设置视口 代码适配PE端的要点 | 二倍图 物理像素和物理像素比 利用二倍图解决图片在PE端默认放大失真 背景缩放 background-size | 移动端的开发选择 | 移动端的相关开发注意点 | 概述 | 手机端调试 打…

【操作系统习题】假定某多道程序设计系统供用户使用的主存空间为100 KB ,磁带机2台,打印机1台

4&#xff0e;假定某多道程序设计系统供用户使用的主存空间为100 KB &#xff0c;磁带机2台&#xff0c;打印机1台。采用可变分区方式管理主存&#xff0c;采用静态分配方式分配磁带机和打印机&#xff0c;忽略用户作业I/O时间。现有如下作业序列&#xff0c;见表2-8。 采用先来…

Linux磁盘分区中物理卷(PV)、卷组(VG)、逻辑卷(LV)创建和(LVM)管理

文章目录一 基础定义二 创建逻辑卷2-1 准备物理设备2-2 创建物理卷2-3 创建卷组2-4 创建逻辑卷2-5 创建文件系统并挂载文件三 扩展卷组和缩减卷组3-1 准备物理设备3-2 创建物理卷3-3 扩展卷组3-4 查看卷组的详细信息以验证3-5 缩减卷组四 扩展逻辑卷4-1 检查卷组是否有可用的空…

Python实现全自动输入文本

文章目录1. 效果图2. 示例代码3. 代码解释1. 效果图 该Python脚本可以实现自动用Notepad打开文本文件&#xff0c;然后自动输入文本&#xff0c;最后保存并关闭文件&#xff0c;从而实现全面自动化处理文本。 2. 示例代码 Python脚本源码如下&#xff0c;主要使用了win32gui、…

Modern Radar for Automotive Applications(用于汽车应用的现代雷达)

目录 1 引言 2 汽车雷达系统的工作原理 2.1 基本雷达功能 2.2 汽车雷达架构 2.2.1 发射机 2.2.2 接收机 2.2.3 天线和天线阵 2.3 信号模型 2.3.1 振幅模型 2.3.2 噪声模型 2.4 雷达波形和信号处理 2.4.1 距离处理 2.4.2 多普勒处理 2.4.3 FMCW汽车雷达应用的典型波形参数…

[Unity好插件之PlayMaker]PlayMaker如何扩展额外创建更多的脚本

学习目标&#xff1a; 如果你正在学习使用PlayMaker的话&#xff0c;那么本篇文章将非常的适用。关于如何连线则是你自己的想法。本篇侧重于扩展适用更多的PlayMaker行为Action&#xff0c;那么什么是PlayMaker行为Action呢&#xff1f; 就是这个列表。当我们要给PlayMaker行为…

CSS的元素显示模式和CSS的背景

&#x1f353;个人主页&#xff1a;bit.. &#x1f352;系列专栏&#xff1a;Linux(Ubuntu)入门必看 C语言刷题 数据结构与算法 HTML和CSS3 目录 一.CSS的元素显示模式 1.1什么是元素的显示模式 1.2块元素 1.3行内元素 1.4 行内块元素 1.5元素显示模式总结 1.6…

JavaEE——HttpServletRequest

HttpServletRequest 核心方法 方法功能String getProtocol()返回请求协议的名称和版本。String getMethod()返回请求的 HTTP 方法的名称String getRequestURI()从协议名称直到 HTTP 请求的第一行的查询字符串中&#xff0c;返回该请求的 URL 的一部分。String getContextPath…

DolphinScheduler3.1简介、部署、功能介绍以及架构设计

DolphinScheduler3.1简介、部署、功能介绍以及架构设计1.DolphinScheduler简介 1-1.关于DolphinScheduler Apache DolphinScheduler 是一个分布式易扩展的可视化DAG工作流任务调度开源系统。适用于企业级场景&#xff0c;提供了一个可视化操作任务、工作流和全生命周期数据处…

Day08--自定义组件的数据监听器案例

1.案例效果&#xff1a; 我的操作&#xff1a; 1》新建一个组件test4 2》在app.json里面将test4设置为全局组件 3》在home.wxml里面是用这个test4组件。 4》在test4.js中编写代码&#xff1a;【需要的配置项都弄一下呗。】 *********************************************…

anaconda3报错Can‘t find libdevice directory解决方案

anaconda3报错Cant find libdevice directory解决方案1. 问题描述2. 解决方案3. 原理分析4. 其他解决方案1. 问题描述 使用anaconda3运行tensorflow进行单机多GPU运算时报错&#xff1a; error: Cant find libdevice directory ${CUDA_DIR}/nvvm/libdevice较的全报错如下&…

【Wayland】QtWayland框架分析

QtWayland框架分析 QtWayland概念介绍 QtWayland是Qt官方基于Wayland开发的一款Toolbox&#xff0c;根据其官网介绍 The QtWayland module consists of two parts: Wayland platform plugin: Enables Qt applications to be run as Wayland clients. QtWaylandCompositor API…

地理空间技术改变世界的未来

摘要: 地理空间技术是一项重大的科学发现&#xff0c;它将人类的可能性推向了一个全新的水平。那么什么是地理空间技术呢&#xff1f;事实上&#xff0c;它与普通的空间数据不同&#xff0c;地理空间技术的创新使我们能够确定物体或人在地球上的确切位置。人们将地理空间技术应…

网络安全系列-四十: suricata 的运行模型Running mode讲解

1. 什么是Running mode 1.1. 基本概念 Sruciata由线程、线程模块、队列组成。 数据包在线程间传递通过队列实现,线程由多个线程模块组成,每个线程模块实现一种功能一个数据包可以由多个线程处理,数据包将通过队列传递到下一个线程。包每次由一个线程处理,但是引擎可以同时…

docker (网卡设置、namespace、网络互通)

1 查看网卡信息 查看网卡的三种命令 ip a lo&#xff1a; 表示本地网络 127.0.0.1eth0&#xff1a; 连接网卡的网络docker0&#xff1a; docker的网卡 ip link show ls /sys/class/net 以文件的形式查看 2 网卡的操作 网卡中增加IP ip addr add 192.168.100.120/24 dev et…

springboot+jsp大学图书借阅管理系统idea maven

本论文是以构建图书借阅为目标&#xff0c;使用 jsp制作&#xff0c;由前台用户借阅图书、后台管理员添加图书两大部分组成。着重论述了系统设计分析&#xff0c;系统的实现&#xff08;用户注册模块&#xff0c;用户登录&#xff0c;用户浏览图书模块&#xff0c;图书借阅模块…

代码复杂度分析

1.复杂度分析原则 1.1 最大循环原则 只看高阶部分 public class SumNum {public static void main(String[] args) {System.out.println(sum2(4));}/*** 1-n 的累加* param n* return*/public static int sum1(int n){int sum 0;// 执行1次for (int i0;i<n;i){//这是三个…