C语言(超详细讲解,帮你零基础玩转C语言) | 扫雷游戏 —— 附完整代码可以直接copy运行游玩

扫雷游戏

引言

在C语言中,有一个很经典的小游戏——扫雷,这个游戏涉及到了数组和函数两大知识点,如果小伙伴们对这两个知识点有所了解,对于这篇文章会了解的更加深刻。btw 结尾附上完整代码,可以点击目录跳转直接运行游玩。

扫雷游戏的分析与设计

在这里我会带着大家对于扫雷游戏的结构框架有一个整体的认识,帮助我们在后期的代码实现的更加效率。


1.扫雷游戏的功能说明

  • 扫雷游戏要基于控制台实现
  • 游戏可以通过菜单实现 继续游玩 还是 退出游戏
  • 扫雷的棋盘应该是9*9的格子
  • 默认的简单难度要随机布置10个雷
  • 可以排查雷
    1.如果所选位置不是雷,就显示周围有几个雷
    2.如果所选位置是雷,就被炸死游戏结束
    3.把除10个雷之外的所有非雷都找出来,就算排雷成功,游戏结束

下面是游戏的界面,以及我的酋长血统(排的第一个坐标就是雷:>)。
在这里插入图片描述


2.数据结构的分析——最重要的一个部分

要想实现扫雷游戏,我们就需要一个数据结构来存储我们的棋盘,因此我们采用一个 11x11(内置9x9的棋盘)的二维数组来存储。 如果这个地方没有雷我们就放入一个'0',如果有雷我们就要放入一个'1'在这里插入图片描述
为什么我们要采用一个 11x11(内置9x9的棋盘)的二维数组来存储呢? 因为如果是一个9x9的棋盘,我们访问边界的点位时,需要查看他周边一圈是否有雷。这个时候就有可能造成越界访问。因此我们在9x9的棋盘的外面加上一圈,并且不去布置雷,就可以做成一个11x11的棋盘,然后让玩家用到的还是9x9的棋盘就可以解决了。

接下来,我们要设置两个棋盘来运行游戏,一个棋盘(对应数组mine)我们专门用来存放布置好的雷的信息,另一个棋盘(对应另一个数组show)存放排查出雷的信息。 这样可以避免一个数组无法分辨此坐标是雷还是周边有雷的情况了。
我们只需要把雷布置到mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且打印show数组的信息给玩家。
同时为了保持神秘,show数组开始时初始化为字符'*',为了保持两个数组的类型一致,可以使用同一套函数处理,mine数组最开始也初始化为'0',布置雷的点位初始化为'1'。见下图
在这里插入图片描述
对应的数组为

char mine[11][11] = {0};//存放布置好的雷的信息
char show[11][11] = {0};//存放排查出的雷的个数信息

3.扫雷游戏的代码实现

在这里我将会手把手带你逐行分析代码如何实现扫雷游戏。请打起精神,这里将是本篇的核心。
首先我设计了3个文件用来实现,后面只会讲到game.c文件和test.c文件,game.h仅用来做各种声明。

test.c    //文件中写游戏的测试逻辑,以及各种函数的使用
game.c    //文件中写游戏中函数的实现等,以及各种函数的定义
game.h    //文件中写游戏需要的数据类型和函数声明等

game.h文件——需要大家看一下,知道每个变量和数据类型对应的是什么

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//文件中写游戏需要的数据类型和函数声明等

#define EASY_COUNT 10

#define ROW 9
#define COL 9

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

//各种函数的声明——声明是要加;

//初始化棋盘函数
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 board[ROWS][COLS],int row,int col);

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

接下来是代码实现的讲解——每一段函数所属的文件我都会在代码块的第一行给大家标注出来
我们首先需要一个菜单来供大家选择

//test.c文件
void menu()
{
	printf("**************************\n");
	printf("********  1.扫雷  ********\n");
	printf("********  0.exit  ********\n");	//用0是因为也可以将选择的0作为假条件跳出循环
	printf("**************************\n");
}

如果玩家选择1,则进入游戏,如果玩家选择0,则退出游戏。


然后使用do while循环和switch语句——做到菜单可以多次供你选择。

//test.c文件
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;
}
srand((unsigned int)time(NULL));  //初始化随机数

这一段代码是用来创建随机数的,现在可以先不用管,后面随机点位布置雷的时候会讲到。
这段代码的意思是:首先让玩家输入一个值,并把这个值给到dowhile循环和switch分支的条件,他们接收到1和0,都会有对应的措施。


这个时候我们的初始框架就有了。接下来我们要实现game()函数。
game()
首先第一步初始化棋盘

//game.c文件中定义
void initBoard(char board[ROWS][COLS],int rows,int cols ,char set)  
{//参数需要传一个数组,以及这个数组的长度——数组的长度不要用宏,还有要放置的元素。
	//使用for循环的嵌套来实现初始化
	int i = 0;
	for (i=0;i<rows;i++)
	{
		int j = 0;
		for (j = 0;j<cols;j++)
		{
			board[i][j] = set;
		}
	}
}

简单的for循环嵌套,在棋盘中放置'0' '*'

//test.c文件中使用
initBoard(mine,ROWS,COLS,'0');
initBoard(show,ROWS,COLS,'*');

第二步,打印棋盘

//game.c文件中定义
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	//依旧是使用两个for循环来打印数组
	int i = 0;
	//打印棋盘框架

	printf("----------扫雷游戏----------\n");
	for (i=0;i<=col;i++)
	{
		printf("%d ",i);		//打印棋盘的横坐标
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ",i);   //打印棋盘的纵坐标
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ",board[i][j]);
		}
		printf("\n");
	}
}

通过for循环打印棋盘的横坐标,然后用for循环的嵌套,打印棋盘的纵坐标以及棋盘上的信息。

//test.c文件中使用
DisplayBoard(show, ROW, COL);//这里向玩家展示是9x9的棋盘,所以用ROW和COL

接下来的操作是在逻辑数组(mine)上完成的
第三步,布置雷

//game.c文件中定义
void SetMine(char board[ROWS][COLS], int row, int col)
{
	//布置10个雷
	//生成随机的坐标,布置雷
	int count = EASY_COUNT;    //可以在这里根据难度的不同来设置雷的数量
	while (count)
	{
		//制定x y的随机值
		int x = rand() % row + 1;
		int y = rand() % col + 1;

		//如果这个地方是'0',就放设置一个雷,并用count计数
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}

}

通过while循环和rand()函数来生成随机的坐标放置雷,并用count来计数。

//test.c文件中使用
SetMine(mine, ROW, COL);

最后一步,排查雷——也是扫雷游戏的核心逻辑

//game.c文件中定义
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	//坐标为x , y 形式
	
	int win = 0;
	while (win < row * col - EASY_COUNT)
	{
		int x = 0;
		int y = 0;
		printf("请输入要排查的坐标:");
		scanf("%d %d", &x, &y);

		int c;										  // 防止非法格式输入坐标
		while ((c = getchar()) != '\n' && c != EOF);  // 因为在缓冲区中,非法格式的坐标不能被读取出来,数据就一直在缓冲区中。
													  // 每一次循环的scanf,都会直接去缓冲区拿数据,每次都是非法字符,就会造成死循环。

		if (x >= 1 && x <= row && y >= 1 && y <= col)	//因为我们的棋盘实际上是11*11的,但是玩家所看到的9*9的,所以我们需要让坐标在合法范围内
		{
			//开始判定是否是雷
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				//该位置不是雷,就统计这个坐标周围有几个雷
				int count = GetMineCount(mine, x, y);   //统计周围有几个雷
				show[x][y] = count + '0';	            //将信息同步到玩家的棋盘上
				DisplayBoard(show, ROW, COL);
				win++;                                  //统计扫了几个点位了 
			}

		}
		else
		{
			printf("坐标非法,重新输入\n");
		}

	}
	if (win == row * col -EASY_COUNT)//row*col为棋盘中所有的点位,EASY_COUNT为棋盘中所有的炸弹数,当win==两位相减就证明已经排除了所有的雷
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);   //打印棋盘
	}

}

首先函数参数传入两个数组用来判定和展示,然后设置一个变量win来记录查找的点位数量,如果点位数量等于了全部点位减去全部雷位(也就是所有安全点位),则证明挑战成功。其次通过第一个外循环来接收玩家选择的点位以及通过if语句来判断是否通关。在外循环中内置一个if - else结构,来判断玩家输入的是否是正确的合法坐标。嵌套的if - else结构来判定是否有雷,以及不是雷的话,统计坐标周围有几个雷。并将棋盘信息展示给玩家。

//game.c文件中定义以及使用
//计算周围有几颗雷
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	//计算所选坐标的周围8个坐标有几个 '1'——雷
	return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] + mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0');
}

其中涉及到的统计炸弹函数GetMineCount(mine, x, y)在这里,此函数把所选点位旁边的8个点位相加,如果都不是雷则相加值减去8个'0',就得0。相反同理。

//test.c文件中使用

结尾——完整代码

game.c文件

#define _CRT_SECURE_NO_WARNINGS 1 
#include"game.h"

//文件中写游戏中函数的实现等
//各种函数的定义

//初始化棋盘函数
void initBoard(char board[ROWS][COLS],int rows,int cols ,char set)  //参数需要传一个数组,以及这个数组的长度——数组的长度不要用宏,还有要放置的元素。
{
	//使用for循环的嵌套来实现初始化
	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)
{
	//依旧是使用两个for循环来打印数组
	int i = 0;
	//打印棋盘框架

	printf("----------扫雷游戏----------\n");
	for (i=0;i<=col;i++)
	{
		printf("%d ",i);		//打印棋盘的横坐标
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ",i);   //打印棋盘的纵坐标
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ",board[i][j]);
		}
		printf("\n");
	}
}

//接下来的操作是在逻辑数组上完成的
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
	//布置10个雷
	//生成随机的坐标,布置雷
	int count = EASY_COUNT;
	while (count)
	{
		//制定x y的随机值
		int x = rand() % row + 1;
		int y = rand() % col + 1;

		//如果这个地方是'0',就放设置一个雷,并用count计数
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}

}

//计算周围有几颗雷
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	//计算所选坐标的周围8个坐标有几个 '1'——雷
	return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + 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)
{
	//坐标为x , y 形式
	
	int win = 0;
	while (win < row * col - EASY_COUNT)
	{
		int x = 0;
		int y = 0;
		printf("请输入要排查的坐标:");
		scanf("%d %d", &x, &y);

		int c;										  // 防止非法格式输入坐标
		while ((c = getchar()) != '\n' && c != EOF);  // 因为在缓冲区中,非法格式的坐标不能被读取出来,数据就一直在缓冲区中。
													  // 每一次循环的scanf,都会直接去缓冲区拿数据,每次都是非法字符,就会造成死循环。

		if (x >= 1 && x <= row && y >= 1 && y <= col)	//因为我们的棋盘实际上是11*11的,但是玩家所看到的9*9的,所以我们需要让坐标在合法范围内
		{
			//开始判定是否是雷
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				//该位置不是雷,就统计这个坐标周围有几个雷
				int count = GetMineCount(mine, x, y);   //统计周围有几个雷
				show[x][y] = count + '0';	            //将信息同步到玩家的棋盘上
				DisplayBoard(show, ROW, COL);
				win++;                                  //统计扫了几个点位了 
			}

		}
		else
		{
			printf("坐标非法,重新输入\n");
		}

	}
	if (win == row * col -EASY_COUNT)//row*col为棋盘中所有的点位,EASY_COUNT为棋盘中所有的炸弹数,当win==两位相减就证明已经排除了所有的雷
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);   //打印棋盘
	}

}

game.h文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//文件中写游戏需要的数据类型和函数声明等

#define EASY_COUNT 10

#define ROW 9
#define COL 9

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

//各种函数的声明——声明是要加;

//初始化棋盘函数
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 board[ROWS][COLS],int row,int col);

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

test.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

//文件中写游戏的测试逻辑
//各种函数的使用

//先写一个菜单的函数
void menu()
{
	printf("**************************\n");
	printf("********  1.扫雷  ********\n");
	printf("********  0.exit  ********\n");			//用0是因为也可以将选择的0作为假条件跳出循环
	printf("**************************\n");
}


//再写一个扫雷的函数——使用扫雷要用到的函数
void game()
{
	//首先要创造两个数组,一个是逻辑数组——用来存放雷,和判定周围一圈有几个雷.一个是展示数组——用来向玩家展示扫雷棋盘的情况

	//首先第一步初始化棋盘
	//1.mine的数组最开始是全 '0 '
	//2.show的数组最开始全是 '* '
	char mine[ROWS][COLS];//存放布置好的雷
	char show[ROWS][COLS];//存放排查出的雷的信息
	//写一个函数来初始化棋盘
	initBoard(mine,ROWS,COLS,'0');
	initBoard(show,ROWS,COLS,'*');
	
	//其次打印棋盘
	DisplayBoard(show, ROW, COL);//这里向玩家展示是9x9的棋盘,所以用ROW和COL

	//接下来的操作是在逻辑数组上完成的
	//然后是布置雷
	SetMine(mine, ROW, COL);

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


int main()
{
	//做一个选择菜单界面——使用do while循环和switch语句——做到菜单可以多次供你选择。
	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;
}

到这里扫雷游戏的讲解就全部结束了,祝大家玩得愉快~

  • 25
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 好的,那我们来一步一步地学习如何用 C 语言编写一个游戏。 首先,让我们了解一下如何在 C 语言中进行绘图。你可以使用一些图形库,比如 SDL(Simple DirectMedia Layer)或者 OpenGL,来实现图形的绘制和渲染。这些库通常都有较为丰富的文档和教程,可以助你快速入门。 其次,你需要了解一些基本的算法和数据结构,比如排序、搜索、链表、栈和队列等。这些算法和数据结构可以助你解决游戏中的各种问题,比如角色移动、碰撞检测、场景管理等。 在你掌握了这些基础知识之后,你就可以开始设计并编写你的游戏了。你需要先定义游戏的整体架构,包括游戏循环、游戏对象、场景和交互等。然后你可以根据自己的想法开始实现游戏的各个部分,比如角色的移动、射击、动画效果等。 最后,你还需要考虑一些细节问题,比如游戏的音效和音乐、存档和恢复等。这些细节问题可能会让你的 ### 回答2: 当然可以教您使用C语言制作一个简单的游戏。首先,您需要了解C语言的基础知识,包括变量、循环、条件语句等。 假设我们要制作一个猜数字的游戏游戏规则是用户需要猜测一个范围内的随机数字,直到猜对为止。以下是一个简单的代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <time.h> int main() { int randomNumber, guess, attempts = 0; // 生成随机数 srand(time(NULL)); randomNumber = rand() % 100 + 1; // 生成1-100之间的随机数 printf("欢迎来到猜数字游戏!\n"); do { printf("请输入你猜测的数字:"); scanf("%d", &guess); attempts++; if (guess > randomNumber) { printf("太大了,请再尝试!\n"); } else if (guess < randomNumber) { printf("太小了,请再尝试!\n"); } else { printf("恭喜你猜对了!你一共使用了%d次尝试。\n", attempts); } } while (guess != randomNumber); return 0; } ``` 以上代码使用rand()函数生成一个1-100之间的随机数,用户需要通过输入猜测的数字来猜对随机数。程序会根据用户的猜测给予相应的提示,直到猜对为止才会结束游戏。在游戏结束时,程序会显示用户一共使用了多少次猜测。 通过以上示例,您可以了解到C语言中的变量、运算符、输入输出函数等的基本用法,以此为基础,您可以尝试制作更多更复杂的游戏。希望这可以助到您,祝您玩得开心! ### 回答3: 当然可以!以下是用C语言编写一个简单的猜数游戏的示例: ```c #include <stdio.h> #include <stdlib.h> #include <time.h> int main() { int number, guess, attempts = 0; // 使用当前时间作为随机数的种子 srand(time(0)); // 生成一个0到100之间的随机数 number = rand() % 101; printf("猜数游戏开始!\n"); do { printf("请输入一个0到100之间的数:"); scanf("%d", &guess); attempts++; if (guess > number) { printf("猜大了!\n"); } else if (guess < number) { printf("猜小了!\n"); } else { printf("恭喜你,猜对了!你用了%d次尝试。\n", attempts); } } while (guess != number); return 0; } ``` 这个游戏会随机生成一个0到100之间的数,然后让你通过输入来猜这个数。根据你的猜测,程序会告诉你是猜大了还是猜小了,直到你猜对为止。并且会显示你猜的次数。希望对你有所助!如果有任何问题,请随时提问。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值