【C语言】扫雷游戏的实现(模块化编程)——内附源码

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

前言

随着开学季的到来,c语言作为大学工科学习中不可或缺的一部分,今天笔者就为大家带来C语言中函数与数组的运用实例—扫雷游戏的教学;
本程序对于初学者有些许难度,多加练习理解透彻后方可掌握;
在这里插入图片描述

一、关于扫雷

扫雷是一款年代久远的小游戏,相信熟悉电脑的小伙伴们在初高中的计算机课上都略有接触。让我们先来回顾一下原版的扫雷游戏吧~
1、游戏规则;
1、扫雷游戏是一种单人的数字游戏,在一个包含有隐藏雷的方格板上进行。
2、每个方格都可能包含一颗雷,也可能不包含任何内容。如果玩家点击了一个带有雷的方格,就会失去游戏。
3、当用户将非雷方块全部找出时,就可以赢得这场比赛
扫雷初版图片在这里插入图片描述
2、扫雷程序的功能模板
1.菜单//menu
2.初始化
3.游戏界面的打印
4.布置雷
5.排查雷
在这里插入图片描述

二、扫雷功能的具体分析

主函数需要有2个数组:一个用于存储雷等用作内核;一个用于每次排查完显示雷的个数用于显示。

1.菜单

菜单功能的实现 便于游戏界面的引入【创建变量1/0用于游戏的开始/退出

void main()
{
	printf("**************************");
	printf("**********1.play**********");//用于游戏的开始 变量1
	printf("**********0.exit**********");//用于游戏的退出 变量0
	printf("**************************");
}

2.初始化

定下雷的个数以及棋盘大小

对棋盘的行列进行宏定义 #define
新手棋盘为9x9,12个格子的定义便于后文扫除雷 的程序操作
宏定义的好处不会把程序的变量定死,便于后续改变,换句话说就是 想要多大棋盘与多少颗雷我们可以随时更改.

//对棋盘的行列进行宏定义
#define row 9//游戏内棋盘
#define col 9
#define rows row+2//扫雷外棋盘 便于程序的融洽
#define cols col+2

#define art 10//十颗雷 艺术就是---boom
棋盘初始化

棋盘初始化就是 将我们想放的东西(set)放到棋盘上 利用函数模块化表示

void Init(char board[ROWS][COLS],int rows,int cols,char set)//set用于设计要布置什么
{
	int i = 0;
		for (i = 0; i < rows; i++)
		{
			int j = 0;
				for ( j = 0; j < cols; j++)
				{
					board[i][j] = set;
				}
		}
}

3.游戏界面打印

玩游戏的时,需要看到游戏界面才能进行各种操作,所以这里要设置一个函数来打印扫雷的棋盘(数组的内容)
1.打印行列来让玩家能够确认坐标; 2.打印键盘的9x9格数构成游戏内容

void Display(char board[ROWS][COLS], int row, int col)
{
	printf("=============扫雷游戏==============\n");
  int i = 0;
	for (i = 0; i < row; i++)
	{
		printf("%d", i);//打印行(无j 全都处于第一行)
	}
	printf("\n");
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			if ( j == 0)//j=0时 都处于第一格
				printf("%d", i);//打印列
			else
			{
				printf("%c", board[i][j]);//打印棋盘
			}
			printf("\n");
		}
	}
	printf("=============扫雷游戏==============\n");
}

4.布置雷

来到了关键且复杂的一步;
布置雷首先需要知道雷的数量,我们则用宏定义便于后续修改雷的数量
#define art 10

  • 关于雷分布在数组中的位置
    需要俩个变量x与y来实现定位(扫雷中的雷是随机分布的,所以需要随机数 rand)
    创建随机数放于x与y中,作为行号和列号

布置雷需要放在数组中的位置上,因此需要我们设置雷的数字为’1’
注意 这里不是数字1是字符’1’
设置一个循环来不断放雷 根据while函数()为0时停止 来设置一个count表示剩余的雷

每次布置雷时需检查是否已经布置过雷,若已布置需重新生成随机数重新布置

void getart(char board[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int count = ART;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand()%col + 1;
		if ('1'!= board[x][y] )//检查是否布置过雷
		{
			board[x][y] = '1';
			count--;
		}
	}
}

5.排查雷

布置好雷后,游戏进入主要部分,排查雷;
我们需要设计程序达成一下目的:

  1. 输入坐标值来排查该位置有无雷
  2. 若有雷 显示被炸死 游戏结束并跳回菜单界面;
  3. 若没有雷 计算周围一圈8个格的雷的数目并显示在该坐标上
  4. ;每次排查完都要进行一次排查完的展示(打印)
  5. 一直循环 直到被炸死 或者雷被排查干净->显示胜利
void outart(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
	{
		int x = 0;
		int y = 0;
		int count = row * col - ART;
		while (count)
		{
			printf("请输入坐标x y\n");
			scanf("%d %d", &x, &y);
			if (x > 1 && x < row && y>1 && y < col)
			{
				if('*'!=show[x][y])
				{
					printf("该位置已经被检查过了!请重新输入\n");
				}
				if ('1'==mine[x][y])
				{
					printf("很遗憾,你被炸死了\n");
					break;
				}
				else
				{
					char num = getnum(mine, x, y);//引入一个新的函数来计算数量
					show[x][y] = num;
					Display(show, ROW, COL);
				}
			}
		}
	}
数雷的个数 //getnum

因为我们的俩个数组mine 和show 都是字符类型数组 所以我们的返回雷的个数也需要是字符,将返回类型设置为char类型。

char getnum(char board[ROW][COL], int x, int y)
{
	return(board[x - 1][y - 1] +
		board[x - 1][y] +
		board[x - 1][y + 1] +
		board[x][y - 1] +
		board[x][y + 1] +
		board[x + 1][y] +
		board[x + 1][y - 1]+board[x + 1][y + 1] - 8 * 48 + 48);//48为一个字符'0',一个字符数字减去字符'0'会等于数字本身
}

接下来,请特邀嘉宾GGBOND 对这一大串加减运算进行解释~
在这里插入图片描述

  • x-1、x+1、x、y、y-1、y+1分别代表什么
    首先由上可知 x,y皆为坐标的定位,因此我们有:

在这里插入图片描述
为坐标示意图 中间方块为x,y 为我们所需要求坐标
在这里插入图片描述

  • 8*48代表什么
    根据ASCII表在这里插入图片描述不难发现:各个字符数字减去字符’0’ (ascii码值48) 就等于数字本身*e.g. ‘1’-48=1,‘1’-‘0’=1(数学运算便于理解不代表最终函数表示)
  • 为什么加上48
    若不加上48,则运算结果是从ASCII值为0的地方开始,而从上表可发现 ascii值=0时,对应的输出结果是不显示字符;但是字符0对应的编码是48,所以需要加上48后,才能从字符0开始输出。
    在这里插入图片描述

三、效果演示

在这里插入图片描述
在这里插入图片描述
现有的功能大概就是这些了,接下来将代码优化后让ggbond放上源码
在这里插入图片描述

四、源码

1、头文件

game .h

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>//随机数头函数
#include <time.h>//随机数时间随机
//对棋盘的行列进行宏定义

//游戏内棋盘
#define ROW 9
#define COL 9
//程序用棋盘 便于计数时不超界
#define ROWS ROW + 2
#define COLS COL + 2

#define ART 10//十颗雷 艺术就是---boom

void menu();
void Init(char board[ROWS][COLS], int rows, int cols, char set);
void Display(char board[ROWS][COLS], int row, int col);
void getart(char board[ROWS][COLS], int row, int col);
void outart(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
int getnum(char board[ROWS][COLS], int x, int y);

2 函数实现部分

game.c

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

void Init(char board[ROWS][COLS],int rows,int cols,char set)//set用于设计要布置什么
	{
		int i = 0;
			for (i = 0; i < rows; i++)
			{
				int j = 0;
					for ( j = 0; j < cols; j++)
					{
						board[i][j] = set;
					}
			}
	}


void Display(char board[ROWS][COLS], int row, int col)
	{
		printf("=============扫雷游戏==============\n");
	  int i = 0;
		for (i = 0; i <= col; i++)
		{
			printf("%d ", i);//打印行(无j 全都处于第一行)
		}
		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");
		}
		printf("=============扫雷游戏==============\n");
	}


void getart(char board[ROWS][COLS], int row, int col)
	{
		int count = ART;
		while (count)
		{
			int x = rand()%row + 1;
			int y = rand()%col + 1;
			if ('0' == board[x][y] )
			{
				board[x][y] = '1';
				count--;
			}

		}
	}

int getnum(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] +
		mine[x + 1][y - 1]+mine[x+1][y+1] - 8 * 48);//48为一个字符'0',一个字符数字减去字符'0'会等于数字本身
}
void outart(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
	{
		int x = 0;
		int y = 0;
		int count = row * col - ART;
		while (count)
		{
			printf("请输入坐标x y\n");
			scanf("%d %d", &x, &y);
			if (x >=1 && x <= row && y>=1 && y <= col)
			{
				if(show[x][y]!='*')
				{
					printf("该位置已经被检查过了!请重新输入\n");
				}
				if (mine[x][y]=='1')
				{
					printf("很遗憾,你被炸死了\n");
					break;
				}
				else
				{
					int num = getnum(mine, x, y);//引入一个新的函数来计算数量
					show[x][y] = num+'0';
					Display(show, ROW, COL);
					count--;
				}
			}
			else
			{
				printf("坐标非法,请重新输入\n");
				break;
			}
		}
		if (0 == count)
			printf("Win!!!!");
	}

3、主函数部分

test.c

void game()
{
	char mine[ROWS][COLS] ;//内核数组放雷
	char show[ROWS][COLS] ;//展示数组打印雷数
	//初始化
	Init(mine, ROWS, COLS, '0');
	Init(show, ROWS, COLS, '*');
	//打印
	Display(show, ROW, COL);
	//布置雷
	getart(mine, ROW, COL);
	//排查雷
	outart(mine, show, ROW, COL);
}

int main()
{
	srand((unsigned)time(NULL));//设置随机数
	int input = 0;
	do
	{
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("=======扫雷=======\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输出错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

到这里,本期的分享就结束啦,欢迎各位小伙伴留言,博主看到就会回复的!


求关注求点赞一键三连~~~~
别忘记点赞!!!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值