C语言实现扫雷(详解版)

目录

引言

1 游戏规则

2 代码实现

2.1 基础功能分析

2.2 功能实现

2.3 运行效果

3 附源码


引言

扫雷的灵感来源于上世纪60年代的计算机编程游戏,其现代版本由微软的Robert Donner和Curt Johnson在1989年左右开发。从Windows 3.1开始,它和“纸牌”、“空当接龙”一起成为Windows系统的内置游戏,目的是为了让用户练习使用鼠标(尤其是左右键点击和双击),这极大地推广了它的普及。对于许多80后、90后来说,扫雷是在学校、办公室电脑上“摸鱼”的经典回忆,是一款真正的“时代记忆”游戏。至今仍有庞大的玩家社群和世界级的竞速比赛,顶尖玩家完成高级棋盘的时间通常在50秒以内,世界纪录更是达到了惊人的30秒级别。

1 游戏规则

一个棋盘上,有若干个雷,当我们点击非雷区时,会展开一片没有雷的区域,这片区域的边界会显示其周围8格雷的数量,我们的目标就是找到所有的雷或者展开所有无雷的区域。

2 代码实现

2.1 基础功能分析

(1)自定义棋盘大小。

(2)自定义雷的数量。

(3)程序能够自动初始化棋盘,并随机往棋盘中安排雷。

(4)玩家输入坐标显示周围8格雷的数量,踩到雷则游戏结束。

2.2 功能实现

通过分析可以看到我们的代码要实现不少功能,所以我们考虑将每一个功能模块分配给一个个函数来实现,并且为了逻辑清晰,我们可以将一些宏定义和函数在头文件game.h里进行申明,在game.c文件进行函数实现,test.c文件进行主程序实现。如下图:

那么理论可行,我们正式开始实践。

(1)自定义棋盘大小。

扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需要⼀定的数据结构来存储这些信息。 假设我们需要在9*9的棋盘上布置雷的信息和排查雷,我们⾸先想到的就是创建⼀个9*9的数组来存放信息。

所以我们可以在game.h文件中将棋盘的长宽定义为常量,后续数组的创建我们都用定义的字母代替,这样我们更改棋盘大小就只用改头文件的数字就行了。

#define ROW 9//设置盘面长度
#define COL 9//设置盘面宽度

(2)自定义雷的数量。

同理,在game.h中将雷的数量定义为常量

#define minecount 10//设置雷数

(3)程序能够自动初始化棋盘,并随机往棋盘中安排雷。

在这个数组中,如果这个位置布置雷,我们就存放1,没有布置雷就存放0。

另外,假设我们排查(2,5)这个坐标时,我们访问周围的⼀圈8个⻩⾊位置,统计周围雷的个数是1 假设我们排查(8,6)这个坐标时,我们访问周围的⼀圈8个⻩⾊位置,统计周围雷的个数时,最下⾯的三 个坐标就会越界,为了防⽌越界,我们在设计的时候,给数组扩⼤⼀圈,雷还是布置在中间的9*9的坐 标上,周围⼀圈不去布置雷就⾏,这样就解决了越界的问题。所以我们将存放数据的数组创建成11*11 是⽐较合适。

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

这时候我们还要思考一个问题,程序排布雷的棋盘和展现给我们玩家的棋盘应该是分开的。我们玩家可见的棋盘一开始可以全部用*填充,表示未知,当我们选择坐标时,再显示坐标周围雷的数量。

这样我们就需要初始化两个棋盘。先在game.h中申明函数

void creatboard(char arr[ROWS][COLS], int rows, int cols, char m);

在game.c中实现函数

void creatboard(char arr[ROWS][COLS], int row, int col,char m) {
	for (int i = 0; i < ROWS; i++) {
		for (int j = 0; j < COLS; j++) {
			arr[i][j] = m;
		}
	}
}

我们把初始化棋盘函数放在game()函数中调用。game.h中申明。

void game();

game.c中定义

	void game() 
	{
		char show[ROWS][COLS];
		char mine[ROWS][COLS];
		creatboard(mine, ROWS, COLS, '0');//放置布置雷的信息,先全部初始化为0
		creatboard(show, ROWS, COLS, '*');//显示排查雷的信息,初始化为‘*’
		



	}

初始化完成后,我们还需要定义一个函数来布置雷,为了实现随机性,我们要引用<stdlib.h>库,里面提供的rand()函数可以生成随机数。

void setmine(char arr[ROWS][COLS], int row, int col);
void setmine(char arr[ROWS][COLS], int row, int col) {
	srand((unsigned int)time(NULL));//rand()实际上是伪随机,需要设置初始种子,
//我们将当前时间戳返回当作种子
	int count = minecount;
	while (count) {
		int x = rand() % 9 + 1;//随机数生成范围限定在1到9
		int y = rand() % 9 + 1;
		if (arr[x][y] != '1') {
			arr[x][y] = '1';
			count--;
		}
	}
}

布置完雷后还需要一个函数将排雷信息输出给玩家。

void displayboard(char arr[ROWS][COLS],int row ,int col);
void displayboard(char arr[ROWS][COLS], int row, int col) {
	printf("----------------分界线-----------------\n");
	for (int i = 0; i <= col; i++) printf("%d ", i);
	printf("\n");
	for (int i = 1; i <= row; i++) {
		printf("%d ", i );
		for (int j = 1; j <= col; j++) {
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
}

这些都定义完后,game()函数就变成了这样。

	void game() 
	{
		char show[ROWS][COLS];
		char mine[ROWS][COLS];
		creatboard(mine, ROWS, COLS, '0');
		creatboard(show, ROWS, COLS, '*');
		displayboard(show, ROW, COL);
		setmine(mine, ROW, COL);
    }

(4)玩家输入坐标显示周围8格雷的数量,踩到雷则游戏结束。

game.h中申明函数

void findmine(char sh[ROWS][COLS],char mi[ROWS][COLS], int row, int col);
int countmine(char mi[ROWS][COLS], int x, int y);

game.c中实现函数

void findmine(char sh[ROWS][COLS], char mi[ROWS][COLS], int row, int col) {
	int x, y;
	int count = ROW * COL - minecount;//无雷区域个数,全部排查完后游戏结束。
	while (count) {
		printf("请输入坐标-->\n");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= ROW&& y >= 1 && y <= COL) {
			if (mi[x][y] != '1') {
				sh[x][y] = countmine(mi, x, y) + '0';
				count--;	
				displayboard(sh, ROW, COL);
			}
			else { displayboard(mi,ROW,COL);
			       printf("很遗憾,你踩到雷了,是否重新开始\n"); 
			       break; 
			}
		}
		else printf("请重新输入");

		if (count == 0) {
			displayboard(mi, ROW, COL);
			printf("恭喜你,排雷成功,是否再玩一次\n");	
		}
	}
}
int countmine(char mi[ROWS][COLS], int x, int y) {//实现统计周围8格雷的数量
	int count=0;
	for (int i = x - 1; i <= x + 1; i++) {
		for (int j = y - 1; j <= y+1; j++) {
			count += mi[i][j] - '0';
		}
	}
	return count;
}

现在的game()函数就是这样。


	void game() 
	{
		char show[ROWS][COLS];
		char mine[ROWS][COLS];
		creatboard(mine, ROWS, COLS, '0');
		creatboard(show, ROWS, COLS, '*');
		displayboard(show, ROW, COL);
		setmine(mine, ROW, COL);
		//isplayboard(mine, ROW, COL);测试用,最终程序中要注释掉
		findmine(show, mine, ROW, COL);
	}

现在各种功能完成了!

我们再把test()中的主函数一写

#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
int main() {
	int enter = 0;
	do {
		menu();
		scanf("%d", &enter);
		switch (enter) {
		case 1: {
			game();
			break;
		}
		case 0: {
			printf("游戏结束\n");
			break;
		}
		default:printf("请输入0或1\n");
		}
	} while (enter);
	return 0;
}

2.3 运行效果

3 附源码

game.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9//设置盘面长度
#define COL 9//设置盘面宽度
#define ROWS ROW+2
#define COLS COL+2
#define minecount 10//设置雷数
void menu();
void game();
void creatboard(char arr[ROWS][COLS], int rows, int cols, char m);
void displayboard(char arr[ROWS][COLS],int row ,int col);
void setmine(char arr[ROWS][COLS], int row, int col);
void findmine(char sh[ROWS][COLS],char mi[ROWS][COLS], int row, int col);
int countmine(char mi[ROWS][COLS], int x, int y);

game.c

#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void menu() {
	printf("***********************\n");
	printf("******* 1--play *******\n");
	printf("******* 0--exit *******\n");
	printf("***********************\n");
	printf("请选择-->\n");
}

void creatboard(char arr[ROWS][COLS], int row, int col,char m) {
	for (int i = 0; i < ROWS; i++) {
		for (int j = 0; j < COLS; j++) {
			arr[i][j] = m;
		}
	}

}

void displayboard(char arr[ROWS][COLS], int row, int col) {
	printf("----------------分界线-----------------\n");
	for (int i = 0; i <= col; i++) printf("%d ", i);
	printf("\n");
	for (int i = 1; i <= row; i++) {
		printf("%d ", i );
		for (int j = 1; j <= col; j++) {
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
}
void setmine(char arr[ROWS][COLS], int row, int col) {
	srand((unsigned int)time(NULL));
	int count = minecount;
	while (count) {
		int x = rand() % 9 + 1;
		int y = rand() % 9 + 1;
		if (arr[x][y] != '1') {
			arr[x][y] = '1';
			count--;
		}
	}

}
void findmine(char sh[ROWS][COLS], char mi[ROWS][COLS], int row, int col) {
	int x, y;
	int count = ROW * COL - minecount;
	while (count) {
		printf("请输入坐标-->\n");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= ROW&& y >= 1 && y <= COL) {
			if (mi[x][y] != '1') {
				sh[x][y] = countmine(mi, x, y) + '0';
				count--;	
				displayboard(sh, ROW, COL);
			}
			else { displayboard(mi,ROW,COL);
			       printf("很遗憾,你踩到雷了,是否重新开始\n"); 
			       break; 
			}
		}
		else printf("请重新输入");

		if (count == 0) {
			displayboard(mi, ROW, COL);
			printf("恭喜你,排雷成功,是否再玩一次\n");	
		}
	}
}
int countmine(char mi[ROWS][COLS], int x, int y) {
	int count=0;
	for (int i = x - 1; i <= x + 1; i++) {
		for (int j = y - 1; j <= y+1; j++) {
			count += mi[i][j] - '0';
		}
	}
	return count;
}



	void game() 
	{
		char show[ROWS][COLS];
		char mine[ROWS][COLS];
		creatboard(mine, ROWS, COLS, '0');
		creatboard(show, ROWS, COLS, '*');
		displayboard(show, ROW, COL);
		setmine(mine, ROW, COL);
		/*displayboard(mine, ROW, COL);*/
		findmine(show, mine, ROW, COL);
	}

test.c

#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
int main() {
	int enter = 0;
	do {
		menu();
		scanf("%d", &enter);
		switch (enter) {
		case 1: {
			game();
			break;
		}
		case 0: {
			printf("游戏结束\n");
			break;
		}
		default:printf("请输入0或1\n");
		}
	} while (enter);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值