C语言扫雷,B站教学视频学习分享,附代码

8月23日晚10点

本次做的实验是一个基于C的扫雷程序,我大致分为5大模块的实现。

先上代码

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <Windows.h>
#include <Mmsystem.h>
#include <graphics.h>//图形界面库
#pragma comment(lib,"winmm.lib")

#define ROW 10//行的个数
#define COL 10//列的个数
#define NUM 10//雷的个数
//定义了一个10行10列的二位数字哦
//注意,定义的宏不能最后加;哦
#define SIZE 50

int count = 0;   //点开的个数
int map[ROW+2][COL+2];//全局变量


IMAGE img[12];		//图片数组,用来保存12张图片


//初始化函数。
void GameInit() {
	//随机数字的种子
	srand((unsigned int)time(NULL));
	//随时间的变化每次产生不同的随机数

	//让所有的二位数组的值变成0
	for (int i = 0; i < (ROW+2); i++) {
		for (int j = 0; j < (COL+2); j++) {
			map[i][j] = 0;
		}
	}
	//如何表示雷,-1表示雷,然后定义了NUM个雷

	int n = 0;
	while (n < NUM)
	{
		//随机的行和列
		//根据雷的分布,填充内部的雷的数据
		int r = rand() % ROW+1;
		int c = rand() % COL+1;//产生1-COL+1之间的随机数,
		//包含1-10的随机数
		//布下雷
		if (map[r][c] == 0) {
			map[r][c] = -1;
			n++;
		}
	}
	//计算每一个方格周围的雷数
	for (int i = 1; i <= ROW; i++) {
		for (int j = 1; j <= COL; j++) {
			if (map[i][j] != -1) {
				for (int a = i-1; a <= i+1; a++){
					for (int b = j-1; b<=j + 1;b++) {
						if (map[a][b] == -1)
						{
							map[i][j]++;
								}
							}
						}
					}
				}
			
			}

	//简单的加密,也就是为了保证一开始出来的时候是空包的图片
	for (int i = 1; i <= ROW; i++) {
		for (int j = 1; j <= COL; j++) {
			map[i][j] += 20;
		}
	}
}
//绘制函数,打印二维数组中的元素
void GameDraw() {
	//二维数组中打印
	for (int i = 1; i <= ROW; i++) {
		for (int j = 1; j <= COL; j++) {
		
			printf("%2d ", map[i][j]);
			/*
			元素         图片
			-1           img[9]
			0-8          img[0]-img[8]
			19-28        img[10]  
			30以上       img[11]
			
			*/

			if (map[i][j] == -1) {
				putimage((i - 1) * SIZE, (j - 1) * SIZE, &img[9]);//雷
			}
			else if (map[i][j] >= 0 && map[i][j] <= 8) {
				putimage((i - 1) * SIZE, (j - 1) * SIZE, &img[map[i][j]]);//数字

			}
			else if(map[i][j]>=19 && map[i][j]<=28)
			{
				putimage((i - 1) * SIZE, (j - 1) * SIZE, &img[11]);//空白图片
			}
			else if (map[i][j] > 30) {
				putimage((i - 1) * SIZE, (j - 1) * SIZE, &img[10]);//国旗图
			
			}

		}
		printf("\n");
	}
}



void OpenZero(int r, int c) {
	//先打开0
	map[r][c] -= 20;
	count++;

	for (int m = r - 1; m <= r + 1; m++) {
		for (int n = c - 1; n <= c + 1; n++) {
			if (m >= 1 && m <= ROW && n >= 1 && n <= COL) {
				if (map[m][n] >= 19 && map[m][n] <= 28) {
					if (map[m][n] != 20) {
						map[m][n] -= 20;
						count++;
					}
					else
					{
						OpenZero(m, n);
					}
				}
			}

		}


	}




}


int PlayGame() {

	//定义一个鼠标消息
	MOUSEMSG msg = { 0 };

	int r, c;

	while (1) {
		msg = GetMouseMsg();

		switch (msg.uMsg)
		{
		case WM_LBUTTONDOWN://翻开 空白图 19-28
			r = msg.x / SIZE + 1;
			c = msg.y / SIZE + 1;
			if (map[r][c] >= 19 && map[r][c] <= 28) {
				if (map[r][c] == 20) {//点到的是0
					OpenZero(r,c);
				}
				else
				{
					map[r][c] -= 20; //-1-8
					count++;
				}
			}
			return map[r][c];
			break;
		case WM_RBUTTONDOWN:
			r = msg.x / SIZE + 1;
			c = msg.y / SIZE + 1;


			if (map[r][c] >= 19 && map[r][c] <= 28) {
				map[r][c] += 50;
			}
			else if (map[r][c] >= 30)
			{
				map[r][c] -= 50;
			}
			return map[r][c];
			break;
		}

	
	}
}

int main() {
	//打开一个图形窗口
	HWND hwnd = initgraph(ROW * SIZE, COL * SIZE);

	mciSendString(L"open music.mp3 alias music", 0, 0, 0);
	mciSendString(L"play music", 0, 0, 0);


	loadimage(&img[0], L"0.jpg", 50, 50);
	loadimage(&img[1], L"1.jpg", 50, 50);
	loadimage(&img[2], L"2.jpg", 50, 50);
	loadimage(&img[3], L"3.jpg", 50, 50);
	loadimage(&img[4], L"4.jpg", 50, 50);
	loadimage(&img[5], L"5.jpg", 50, 50);
	loadimage(&img[6], L"6.jpg", 50, 50);
	loadimage(&img[7], L"7.jpg", 50, 50);
	loadimage(&img[8], L"8.jpg", 50, 50);
	loadimage(&img[9], L"9.jpg", 50, 50);
	loadimage(&img[10], L"10.jpg", 50, 50);
	loadimage(&img[11], L"11.jpg", 50, 50);




	//把0.jpgd的图片以50*50的大小放到img这个数组中

	GameInit();
	
	while (1)
	{
		GameDraw();
		if (PlayGame() == -1) {
			GameDraw();
			MessageBox(hwnd,L"Game Over",L"",MB_OK);
			break;
		}

		if (ROW*COL - NUM == count) {
			MessageBox(hwnd, L"You Win", L"", MB_OK);
			break;
		}
	}
	return 0;
}
/*
	根据雷的分布,填充其他不为雷的数据
	1.遍历九宫格、
	2.边上的数据遍历的时候 会越界
	3.***辅助区 游戏区,这两个区其实就是之前的ROW+2和COL+2

*/

具体的实验步骤是:

1.

首先,先定义一个二位数组,10行,10列,其中ROW和COL都放在宏中。以便后期的更改需要,最主要的还是,因为本次的实验相对比较简单,不需要引用外部份文件,只有一个Main,所以不用担心会有变量重复的问题,避免出错。然后把雷的值设置成-1,基于要实现这种需求,本次学习中,新学习到了<time.h>库和两条语句:

srand((unsigned int)time(NULL)); //根据系统的时间产生随机数的种子,比较常用

int r = rand() % ROW+1;//产生[1-row+1)内的数字,注意是包含1,但是不包含row+1的

写入雷的时候也得判定是否有重复输入的情况,加一条if就轻松解决啦。

2.既然基本的雷区已经完成了,那接下就是扫雷中比较经典的,点开后的小数字的实现,

以上的表格里面,首先,中间的坐标(i,j)得是雷哦。那也就是通过for循环来,遍历雷周围的数字来增加1,然后最重要的,也就是边界问题,比方说(0,0),(10,10)的坐标周围并不是8个格子,所以为了解决这一个问题,就需要把原来的表格每行每列往外扩充2格。这样就相当于设置了一个游戏区和一个空闲区。

 

这样就可以让原来边界的(0,0)----(ROW-1,COL-1)变成(1,1)----(ROW,COL),相当于最外面一圈是全0,这样就可以不考虑坐标越界的问题了。那这样的话也就算出没个方格周围的雷数了。

3.

由于后期没个方格都得是图片,所以需要对鼠标操作,以及相应值得变化得定义。我先声明一下,

0-8的数字刚刚号就是 0-8,雷是-1,而初始化的图片就是19-28,之所以是这个范围是为了后面点击操作后-20然后变成[-1,8]显示出来。旗子的值是>30表示,也就是空包格子+30,必会成>30的数。

结合函数       WM_LBOTTONDOWN和WM_RBOTTONDOWN,进行每一次点击的判定,然后转数字,配合后期的图片加载需要。

4图形化

以上已经对数据有了初步的定义和处理,接下里就是很重要的环节,图形化的处理,这里就得应用一个新的库了<graphics.h>,(这个库需要网上下载哦)

HWND hwnd = initgraph(ROW * SIZE, COL * SIZE);//打开一个图形窗口

loadimage(&img[n], L"n.jpg", 50, 50);//加载图片,其中的n就是图片的编号,接下来就是图片的分配,一开始就对图片背后的值进行了预处理,那只需要对值进行图片的定义就行。这里用的是while(true)就是一直判定。

5.

玩过扫雷的都知道,在点开一个0后会有很多的空白显示出来,那这实际上就是一种递归的思想,定义了一个 OpenZero(int r, int c)

 基于这样的一个递归的思想,也就是判定一开始点开的是雷还是数字还是0,三种情况

如果是0那就进行递归处理,接着判断0周围的8个也就是判断(i-1,j-1)-(i+1,j+1)范围内的每一个再次递归。

最后我还加了一个音乐,用到以下的库,和在项目属性中添加winmm.lib操作

#include <Windows.h>
#include <Mmsystem.h>

遇到的问题:

1.一开始添加了休闲区后需要对下标进行修改,当时没改,所以出来的就是一排0或者1列0.

2.播放的时候那几个库有点玄学,一开始怎么弄都不行,后面把#include <Windows.h>放到
#include <Mmsystem.h>上面就好了。够玄学的,holy shit.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值