c-扫雷游戏

本文介绍了扫雷游戏规则,重点阐述如何用C语言实现简易扫雷游戏。从主函数开始设计,包括菜单、game()函数,采用多文件形式。详细说明了game()函数中初始化棋盘、打印棋盘、布置雷、排查雷等步骤的实现逻辑,最后给出完整代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

  • 扫雷游戏规则与介绍
  • 扫雷游戏逻辑设计
    • 一、菜单
    • 二、game()函数
    • 三、多文件形式 `test.c`,`game.h`,`game.c`
      • 一、game()函数
      • 二、初始化棋盘 行列为双11
      • 三、 打印棋盘 行列为双九
      • 四、布置雷 行列为双九
      • 五、 排查雷 行列为双9
  • test.c
  • game.h
  • game.c
  • 杳杳碎碎念

扫雷游戏规则与介绍

相信大家都玩过扫雷游戏,记忆犹新的有之,遗忘的亦有之。请看下图,用于帮助大家重温一下扫雷游戏。
在这里插入图片描述
在这里插入图片描述

通过看上面两个图片,我们可以得出以下结论

  1. 游戏目标:猜测随机分布的地雷位置,在游戏过程中揭开所有非地雷的方格,最终揭开所以非地雷方格即为胜利,否则游戏失败。
  2. 游戏须知:方格里要么是雷,要么是数字(即不是雷);数字表示周围格子【以选中的格子为中心,周围绕一圈】里有几个雷
    既然已经重温了扫雷游戏,接下来让我们看看如何用C语言实现一个简易版的扫雷游戏吧!!!

扫雷游戏逻辑设计

先从main()主函数开始设计

一、菜单

printf("*******************************\n");
printf("*******     1. play   *********\n");
printf("*******     0. exit  **********\n");
printf("*******************************\n");

1.针对游戏胜利或失败,玩家往往会有再玩一局的欲望,因此可以设计do while循环do while循环的结束条件需要看玩家选择1还是0
2.菜单可以放在do while循环里面,而玩家选择玩还是不玩,可以选择在do while循环里设计switch语句
三、玩家进行选择,就需要一个变量,因此需要定义变量input

	int input=0;
	do
	{
		printf("*******************************\n");
		printf("*******     1. play   *********\n");
		printf("*******     0. exit  **********\n");
		printf("*******************************\n");
		printf("请选择:>");
		scanf("%d", &input);
		//switch 根据选择的input值进行判断
		switch (input)
		{
		case 1:
			printf("请开始您期待的扫雷游戏吧\n");
			game();//扫雷游戏代码太多,因此开辟一个game()函数
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default://若是选择了其他的
			printf("选择错误,请重新选择1和0\n");
			break;
		}
	} while (input);//选择0,退出游戏,结束循环

上述代码行数太多,为了减少主函数的行数,菜单可以用函数menu()来实现

void menu()
{
	printf("*******************************\n");
	printf("*******     1. play   *********\n");
	printf("*******     0. exit  **********\n");
	printf("*******************************\n");
}
int main()
{
	int input = 0do
	{	
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		//switch 根据选择的值进行判断
		switch (input)
		{
		case 1:
			printf("请开始您期待的扫雷游戏吧\n");
			game();//扫雷游戏代码太多,因此开辟一个game()函数
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择1和0\n");
			break;
		}
	} while (input);//选择0,退出游戏,结束循环

玩家选择case 1,进入game()函数。【完成扫雷游戏的整个过程】

二、game()函数

首先,布置雷(随机);其次,扫雷。

三、多文件形式 test.c,game.h,game.c

test.c------游戏的整个逻辑运行
game.h------游戏相关的函数的声明
game.c------游戏相关的函数的实现
熟悉了扫雷游戏的逻辑设计,请进入游戏的核心------game()函数的设计模块

一、game()函数

假设是99的棋盘
一、 定义字符数组 !!! 用于记录布置好的雷的信息和排查出的雷的信息,需要定义2个字符数组
假设:
雷------>‘1’
非雷------>‘0’
排查出的雷的信息------>数字(表示周围雷的个数)
没有被排查过的位置------>’
’(星号)
在这里插入图片描述
根据棋盘可知,统计周围有几个雷时,在最外层会出现越界的情况,为了防止外层越界的情况,可以在外围专门造一圈(不布置雷),构成11+11的棋盘。
在这里插入图片描述
如果[][]在方括号内直接填值,就把数组写死了,换值的话比较麻烦,为了简化代码,进行了声明,直接在game.h的头文件进行数值的改动即可。

因此在主函数中和game.h(进行相关声明)中进行如下操作:
由于主函数test.c使用了自定义头文件game.h中的ROWS,COLS,因此主函数要包含game.h的头文件#include"game.h"(包含自己的头文件,使用双引号)
在这里插入图片描述
在这里插入图片描述

二、初始化棋盘 行列为双11

1.初始化棋盘,把棋盘全部初始化为字符’0’( 数组中没有雷的时候,放字符’0’),之后再布置雷。
2.此时另外一个(盘查出的雷的信息的)数组 为了保持神秘感,应该全部初始化为字符’*’(星号✳)【没有被排查过的位置为星号】,之后再排查雷。
主函数:使用了InitBoard()函数来进行棋盘的初始化
在这里插入图片描述函数的声明:
在这里插入图片描述函数的实现:在这里插入图片描述
截止到目前为止,已经进行了棋盘的初始化,那么我们验收一下结果,看一看初始化的打印是否正确,如果这一步不对的话,那接下来的布置雷和排查雷无从谈起。

三、 打印棋盘 行列为双九

  1. 打印棋盘,此时棋盘数组仍为双11,只不过行和列的打印变为了1-9。
  2. 由于打印需要printf()函数,因此在game.c(函数实现)
    中,需要#include<stdio.h>,又因为初始化棋盘的时候,已经在game.c中添加了头文件game.h,为了简便,可以直接在头文件game.h中加#include<stdio.h>
    主函数:
    在这里插入图片描述函数的声明:
    在这里插入图片描述函数的实现
    在这里插入图片描述
    由开始执行(不调试)的运行结果可知,棋盘初始化正确。在这里插入图片描述
    问题又出现了,我们不知道具体的某个格子是第几行第几列???
    此时需要对game.cvoid DisplayBoard(char board[ROWS][COLS], int row, int col)进行以下优化。请看下列代码块。
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("-------扫雷--------\n");//为了让游戏好看一些的装饰
	//打印列号 横着的
	for (i = 0; i <= row; i++)//1改为0 为了对齐
	{
		printf("%d ", i);
	}
	printf("\n");//换行
	for (i = 1; i <= row; i++)//打印9*9,行从1开始,9结束
	{
		int j = 0;
		//打印行号 竖着的
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);//空一个
		}
		printf("\n");
	}
}

四、布置雷 行列为双九

1.我们也可以用一个函数来布置雷SetMine() 布置雷在mine数组里布置,雷只会在双九出现,所以棋盘数组的行和列的变为了1-9,而数组[][]方括号仍为双11。
2.布置雷时,如果不想地雷的次数写死,可以define定义。#define EASY_COUNT 10
3.雷在棋盘随机布置,那么该如何设计呢???
可以利用rand()函数生成伪随机数,rand()函数的返回值是按照某种算法进行运算生成的。要生成不同的随机数,得调用srand()函数,其返回值为无符号整型【srand函数不需要频繁调用,一次即可,放在mian()函数中就行。】,srand()函数是随机数生成器,需要找一个变化的值,time()函数可以返回时间戳满足变化的条件。
4.设置随机数的范围:
a-b的范围即a + rand()%100(b-a+1);
rand()的头文件:#include<stdlib.h>
srand()的头文件:#include<stdlib.h>
time()的头文件:#include<time.h>
以上头文件需要在game.h添加,因为game.c中添加了头文件game.h,为了简便,可以直接在头文件game.h中加#include<stdlib.h>,#include<time.h>。
5.注意!!!count 个雷,雷布置成功之后再减减,如果while(count–)可能会出现坐标相同的情况,造成循环次数的浪费。
主函数:
在这里插入图片描述函数的声明:
在这里插入图片描述函数的实现:
在这里插入图片描述

五、 排查雷 行列为双9

  1. 排查雷会涉及两个数组:main()show(),在main()数组里排查,排查出的雷的信息放到show()数组里
  2. 排查雷有好几次,因此采用循环的形式进行排查。那么循环的表达式是什么呢???表达式<棋盘总数-地雷数
  3. 统计周围雷的个数(count):
    需要在main()数组里进行加法计算,由于是main()是字符数组,'0’的ASCLL值是48,‘1’的ASCLL值是49。
    由’1’(字符)-‘0’(字符)=1(数字1)推理可得,周围雷的个数=周围每个格子的ASCLL值-‘0’。
    设格子坐标(x,y),那么
    count = 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’;

主函数:
在这里插入图片描述函数的声明:
在这里插入图片描述
函数的实现:

int GetMineCount(char mine[ROWS][COLS],int x, int y)
{
	//ascll值  '2'-'0'='2'
	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';//'0'=48
}
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
			{
				if (show[x][y] != '*')
				{
					printf("该坐标已经被排查过了,无需再排查\n");
				}
				else
					//统计mine数组的x,y坐标周围8个坐标有几个雷
				{
					int count = GetMineCount(mine, x, y);//去mine()数组里找
					show[x][y] = count + '0';//将数字2 转化为字符2 ASCLL值放到show数组里
					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<stdio.h>
#include"game.h"//包含自己的头文件 双引号""
void menu()
{
	printf("*******************************\n");
	printf("*******     1. play   *********\n");
	printf("*******     0. exit  **********\n");
	printf("*******************************\n");
}
//完成扫雷游戏的整个过程
void game()
{
	char mine[ROWS][COLS] = { 0 };//放   布置好的雷的信息
	char show[ROWS][COLS] = { 0 };//存放 排查出的雷的信息,用于显示

	//初始化棋盘 '0' '*'
    InitBoard(mine, ROWS, COLS,'0');//全部初始化为'0'	数组名 行 列
	InitBoard(show, ROWS, COLS,'*');//全部初始化为'*'	数组名 行 列

	//打印show棋盘  事实上不会打印,只是需要看当前代码有没有问题
	//DisplayBoard(show, ROW, COL);//打印9*9即可,不需要打印外围那一圈11*11,外围是辅助作用
	//DisplayBoard(mine, ROW, COL);//注意传的数组是11*11的,打印的行和列才是9*9


	//布置雷
	 SetMine(mine,ROW,COL);//9*9
	// DisplayBoard(mine, ROW, COL);
     DisplayBoard(show, ROW, COL);


	 //排查雷
	 FindMine(mine, show, ROW, COL);//
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));//需要stdlib.h和time的头文件
	//菜单
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		//switch 根据选择的值进行判断
		switch (input)
		{
		case 1:
			printf("请开始您期待的扫雷游戏吧\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择1和0\n");
			break;
		}
	} while (input);//选择0,退出游戏,结束循环
	return 0;
}

game.h

#pragma once
//函数的声明
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define EASY_COUNT 10
#define ROW 9//行
#define COL 9//列
//为什么要定义9???在过程中既会用9,也会用11
#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);//传11*11参数的棋盘,可以只使用9*9

//布置雷
void SetMine(char board[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"//game.c中不认识 ROWS,COLS,要包含头文件
//初始化棋盘
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)
{
	int i = 0;
	printf("-------扫雷--------\n");
	//打印列号
	for (i = 0; i <= row; i++)//1改为0
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)//打印9*9,行从1开始,9结束
	{
		int j = 0;
		//打印行号
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{//使用了printf,需要包含头文件,因为game.c中包含了game.h的头文件,因此可以将#include<stdio.h>写在game.h中
			printf("%c ", board[i][j]);//空一个,更好看出来
		}
		printf("\n");
	}
}

//布置雷(随机布置)
void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;///count 个雷
	//while (count--)//循环10次  如果有一次生成的坐标一样的话,那雷就少了【浪费】
	//{
	//	int x = rand() % row + 1;//0-8—>>1-9
	//	int y = rand() % row + 1;
	//	if (board[x][y] == '0')
	//	{
	//		board[x][y] == '1';
	//	}

	while (count)//(表达式)应该为count
	{
		//行列是随机数。
		int x = rand() % row + 1;//0-8—>>1-9
		int y = rand() % col + 1;//0-8—>>1-9
		if (board[x][y] == '0')//不是雷,才能布置雷
		{
			board[x][y] = '1';
			count--;//布置成功雷之后,再减减
		}
	}
}

int GetMineCount(char mine[ROWS][COLS],int x, int y)
{
	//ascll值  '2'-'0'='2'
	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';//'0'=48 -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//不是雷
			{
				if (show[x][y] != '*')//
				{
					printf("该坐标已经被排查过了,无需再排查\n");
				}
				else
					//统计mine数组的x,y坐标周围8个坐标有几个雷
				{
					int count = GetMineCount(mine, x, y);//去mine()数组里找
					show[x][y] = count + '0';//数字2,3 ,转化为ASCLL值【字符】
					DisplayBoard(show, ROW, COL);
					win++;
				}
				//停不下来
			}
		}
		else
		{
			printf("输入的坐标非法,请重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);

	}
}

杳杳碎碎念

这篇扫雷游戏,其实本来在前几天就该写了,可是我有点懒,想想一大串代码就想往后拖,直到现在,我才写完。怎么说呢?初次写这么多代码,写的时候,心情浮躁,总想休息,以至于从晚上开始直到现在2:33我才写完。感觉后面的学习会更难[猫猫叹息]。大家,晚安。最后,希望朋友们,可以指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值