详细教你用纯c语言实现扫雷游戏(有图)

前言

1、这篇博客面向的是初学者,所以讲得会比较详细(如果哪里没看懂,可以在评论区提问)。

2、为了避免文章过长(其实是自己不想写),本文不会解释十分基础的内容,(如printf函数,scanf函数,循环语句、选择语句的格式,函数如何定义等),请学习对应的知识后再看本文。

3、这个小游戏需要创建两个源文件,一个头文件。在最后有具体的代码展示,要复制代码的话可以最后再复制。

4、博主为大一新生,写出的文章难免有不足之处,欢迎各位大佬指出。
在这里插入图片描述


一:创建扫雷游戏的文件

​​​​​

1、创建结果

1、创建的结果如下

​​​​在这里插入图片描述

2、各个文件的作用

1、game.h是头文件,我们可以在文件里用宏声明变量,或者声明函数

2、game.c 用来具体实现各个函数的功能

3、test.c 编写程序的框架


二:开始编写扫雷游戏

1、打印菜单

(1)、分析打印菜单的原理

目标: 在开始游戏之前,我们需要打印一个菜单,供玩家选择。具体要实现的操作有:

(1)、玩家在键盘上输入一个数字。1表示玩游戏,0表示退出,如果玩家输入了其他数字,打印“选择错误,请重新选择!”

(2)、如果玩家输入0,程序退出。如果玩家输入1,游戏开始。如果玩家输入其他数字,菜单需要重新打印。

(3)、菜单至少要打印一次,玩家选择后才能进行下一步。需要循环语句来实现。

注:我们选择在test.c源文件上实现这个功能。


(2)、用代码实现打印菜单

#include<stdio.h> 

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

}
//-------------------我是分割线----------------------------
int main()                             
{                                       
	int input = 0;                
	do {                             
		menu();                          
		printf("请选择你要的功能:");    
		scanf("%d",&input);                  
        switch(input){
        case 1:
          game();
          break;
        case 0:
          printf("游戏结束");
          break;
        default:
          printf("输入错误,请重新选择!");
          break}	
} while(input);               

}

(3)、分析以上代码

do……while循环是c语言三种循环语句中的一种。
特点:与其他循环语句不同的是,do……while循环的执行次数为1~n,也就是说{ }内的语句一定至少会执行一次

注:c语言中可以用数字表示真假。即0表示假,非0表示真

原理:编译以上代码,控制台会先打印菜单,之后玩家输入一个数字,此时分三种情况。1、玩家输入0,input =0
,0表示假,do……while循环结束,程序结束。

2、玩家输入1,游戏开始。等到游戏结束后,由于input=1,do……while循环并没有结束,控制台会重新打印菜单,供玩家选择。

3、玩家输入其他数字,除了没有开始游戏,其他都和情况2类似。


2、分析game函数要实现的功能

功能如下:1、创建两个二维数组存放数据。mine数组负责存放雷,玩家无法看见。show数组则是给玩家看的。

2、初始化数组 3、放置雷 4、打印棋盘 5、玩家排雷 6、判断是否胜利


3、初始化数组

(1)、原理分析

因为扫雷的棋盘是二维的,所以我们用二维数组接收数据。规定如下

1、在mine数组中,'0’表示非雷,'1’表示雷。由于这一步在放置雷之前,所以mine数组初始化时全部放置’0’

2、玩家在玩游戏时,看到的是show数组。在show数组中,'#‘表示非雷,’*'表示雷,show数组初始化时全部放置’#’


(2)、用代码实现初始化数组

//在test.c源文件中

#include"game.h"//引用自定义的头文件时,需要用"文件名",而不是<文件名>

void game() {
	
	//定义数组用来存放数据
    //设定一个行数为Rows ,列数为Cols的二维数组
	char mine[Rows][Cols];
	char show[Rows][Cols];
		
	
	//初始化棋盘:mine 全部放置'0'
    // show 全部放置'#'
	Initboard(mine,Rows,Cols,'0');  
	Initboard(show, Rows, Cols,'#');
}

解释:Initboard是我们定义的初始化函数 Rows Cols是我们在game.h头文件中用宏定义的变量。 注:用宏定义标志符,能做到“一改则改”。

//在game.h头文件里的代码
#define Col 9  //Col表示列数
#define Row 9  //Row表示行数
//这就是玩扫雷时的控制台显示的棋盘大小

#define Rows Row+2  //为什么+2之后说明
#define Cols Col+2

void Initboard(char board[Rows][Cols],int rows,int cols,char set);
#include "game.h"

//作用:将数组的每个元素都初始化为set
void Initboard(char board[Rows][Cols], int rows, int cols, char set) {
	int i = 0;
	int j = 0;
	for (i = 0; i < rows;i++) {
		for (j = 0; j < cols;j++) {
			board[i][j]=set;
		}
	}     
}

4、打印棋盘

(1)、Rows Row Cols col 设置目的

在具体实现打印棋盘之前,我先解释一下Rows Row Cols Col 的设置目的。

为了方便理解,我将Row Col 都定义为3 ,Rows 为Row+2 Cols为Col+2
在这里插入图片描述
在这里插入图片描述


(2)、确定数组中元素的坐标

//在test.c源文件中

#include"game.h"
void game() {
	char mine[Rows][Cols];
	char show[Rows][Cols];
	Initboard(mine,Rows,Cols,'0');  
	Initboard(show, Rows, Cols,'#');

//打印棋盘
   Displayboard(show,Row,Col);
       //(要打印的棋盘名,行数,列数)
}

为了更好的定位棋盘上元素的位置,我们在棋盘的上方和左边打印坐标

注:数组元素下标从0开始。如:int arr[5]={1,2,3,4,5};

arr[0]表示1 ,arr[4]表示5 。 但在习惯上我们希望数组元素的下标与其在数组的第几行第几列相一致。

在这里插入图片描述
在这里插入图片描述


(3)、分析打印的逻辑

在这里插入图片描述
在这里插入图片描述


(4)、用代码实现打印棋盘

void Displayboard(char board[Rows][Cols],int row,int col) {
	int i = 0;
	int j = 0;
	for (i = 0; i <= col; i++)//这是打印第一行,纯数字行 
    {
		printf("%d ",i);
	}
	    printf("\n"); //记得换行
	
    for (i = 1; i <= row; i++) //接下来还有row行要打印
	{
		printf("%d ",i); //每一行开头的数字
		for (j = 1; j <= col; j++) 
		{
			printf("%c ",board[i][j]); 
		}
		printf("\n"); //每一行打印结束后都要换行
	}
	printf("\n");
}

5、放置雷

//game.h 头文件新增语句

#define Count 10 //表示雷的个数为10
//test.c文件
#include"game.h"
void game() {
	char mine[Rows][Cols];
	char show[Rows][Cols];
	Initboard(mine,Rows,Cols,'0');  
	Initboard(show, Rows, Cols,'#');
    Displayboard(show,Row,Col);

    SetMine(mine,Row,Col); //放置雷
}

(1)、随机(只因)的实现

为了实现随机,我们引入rand函数。
rand函数位于stdlib.h头文件里,作用是产生一个取值范围为0~32767的随机数。

如果我们要缩小范围为0-9呢?那就把产生的数%10,余数范围就为0~9。写作rand()%10
如果范围为1~10呢?那就把产生的数%10+1,写作rand()%10+1

产生雷的范围为:行:1~Row ; 纵: 1~Col
可以记作 rand()%Row+1 ; rand()%Col+1

可是,如果只用rand来实现随机,会有问题。请看以下代码:

#include<stdio.h>
#include<stdlib.h>

int main() {
	int a = rand();
	int b = rand();
	printf("%d %d",a,b);
}

在这里插入图片描述
什么鬼,运行结果是一样的!
如果我们这样设置雷,就会导致每次玩的时候雷生成的位置都相同,这还玩个毛啊。

所以,我们决定用“时间戳”这一概念,来使rand()产生的数字变为完全随机。(范围还是0~32767)
可以用srand((unsigned)time(NULL));来实现,具体原理就不展开讲了。

注:1、srand函数在头文件stdlib.h里 ; time在time.h里 ; NULL在stdio.h里

2、srand((unsigned)time(NULL))这句话只需要执行一次,不需要在循环语句中反复执行

于是,上面的代码就可以改为

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

int main() {
	srand((unsigned)time(NULL));
	int a = rand();
	int b = rand();
	printf("%d %d",a,b);
}

在这里插入图片描述


(2)、用代码实现放置雷

void SetMine(char board[Rows][Cols],int row,int col) {
	
	int count = Count; //这个大写的Count是我们在game.h里定义的Count
	
        while (count) {
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		
     //如果该位置mine数组元素为'0',那么count-1,并且在那个位置放下一颗雷
     /*如果该位置mine数组元素为'1',即该位置已经放了雷,那么if判断的条件为假
     count的值不变。并且while循环正常进行*/
        if (board[x][y] == '0') {
			board[x][y] = '1';
			count--;
		}
	//当count为0,也就是已经放了规定数目的雷时,while循环结束。 
   }
}

6、玩家排雷

//在game.h里定义函数
void Minefind(char mine[Rows][Cols],char show[Rows][Cols],int row,int co
//test.c文件
#include"game.h"
void game() {
	char mine[Rows][Cols];
	char show[Rows][Cols];
	Initboard(mine,Rows,Cols,'0');  
	Initboard(show, Rows, Cols,'#');
    Displayboard(show,Row,Col);
    SetMine(mine,Row,Col); 

    Minefind(mine, show, Row, Col);
}

(1)、分析这个函数的要求

1、打印“请输入你要排查的坐标”,后用键盘输入两个数字,分别代表要查找坐标的x轴和y轴。
2、如果输入的数字不在数组的范围里,打印“输入的坐标不合适”,之后重新输入数字。
3、如果查找坐标对应的元素正好是雷,打印“很遗憾,你被炸死了!”,之后游戏结束
4、如果查找的坐标不是炸弹,对应坐标的元素就要改为周围一周的炸弹个数
5、定义一个变量win=0,由于查找坐标需要重复进行,所以要用一个循环包括这个过程。每次排查出一个非炸弹的坐标,win+1。直到win==Row*Col-count


(2)、用代码实现玩家排雷

int win = 0;
void Minefind(char mine[Rows][Cols], char show[Rows][Cols], int row, int col) {
	while (win<Row*Col-Count) {
		printf("请输入你要排查的坐标:");
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);

		if (x <= 0 || x > row || y <= 0 || y > col) 
		{
			printf("输入坐标不合适!");
		}
		else {
			if (mine[x][y] == '1') {
				printf("很遗憾,你被炸死了!\n");
				break;
			}
			else {

				show[x][y] = mine[x - 1][y - 1] + mine[x][y - 1] + mine[x - 1][y] + mine[x][y + 1] + mine[x + 1][y] + mine[x + 1][y + 1] + mine[x - 1][y + 1] + mine[x + 1][y - 1] - 7 * 48;        // '1'=1+'0'  '0'=48  
//假设有两个字符'1'相加,要得到字符'2', 就要'1'+'1'- 1*48  
				Displayboard(show, Row, Col); //打印show数组
				win++;
			}
		}

7、完整代码展示

//game.h
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define Col 9
#define Row 9
#define Count 10

#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[Row][Col],int row,int col);

void Minefind(char mine[Rows][Cols],char show[Rows][Cols],int row,int col);
//test.c

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

}

void game() {
	
	//定义数组用来存放数据
	char mine[Rows][Cols];
	char show[Rows][Cols];
		
	//mine   0 表示不是雷  1表示雷
	//show   存放排查出雷的信息
	
	//初始化棋盘:mine 全部放置'0'
    // show 全部放置'#'
	Initboard(mine,Rows,Cols,'0');
	Initboard(show, Rows, Cols,'#');

	//打印棋盘
	Displayboard(show,Row,Col);

	//放置雷
	SetMine(mine,Row,Col);
	// 测试能否正常完成任务 Displayboard(mine, Row, Col);
	

		Minefind(mine, show, Row, Col);
		
		
	 
}

int input = 0;
int main() {
	srand((unsigned)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;
}
//game.c
#include "game.h"

void Initboard(char board[Rows][Cols], int rows, int cols, char set) {
	int i = 0;
	int j = 0;
	for (i = 0; i < rows;i++) {
		for (j = 0; j < cols;j++) {
			board[i][j]=set;
		}
	}
}

void Displayboard(char board[Rows][Cols],int row,int col) {
	int i = 0;
	int j = 0;
	for (i = 0; i <= col; i++) {
		printf("%d ",i);
	}
	    printf("\n");
	for (i = 1; i <= row; i++) 
	{
		printf("%d ",i);
		for (j = 1; j <= col; j++) 
		{
			printf("%c ",board[i][j]); 
		}
		printf("\n");
	}
	printf("\n");
}


void SetMine(char board[Rows][Cols],int row,int col) {
	//要放置雷的范围  行:1--row   列: 1--col
	
	int count = Count;
	while (count) {
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0') {
			board[x][y] = '1';
			count--;
		}
	}
}

int win = 0;
void Minefind(char mine[Rows][Cols], char show[Rows][Cols], int row, int col) {
	while (win<Row*Col-Count) {
		printf("请输入你要排查的坐标:");
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);

		if (x <= 0 || x > row || y <= 0 || y > col) 
		{
			printf("输入坐标不合适!");
		}
		else {
			if (mine[x][y] == '1') {
				printf("很遗憾,你被炸死了!\n");
				break;
			}
			else {

				show[x][y] = mine[x - 1][y - 1] + mine[x][y - 1] + mine[x - 1][y] + mine[x][y + 1] + mine[x + 1][y] + mine[x + 1][y + 1] + mine[x - 1][y + 1] + mine[x + 1][y - 1] - 7 * 48;
				Displayboard(show, Row, Col);
				win++;
			}
		}

	}
	if (win == Row * Col - Count) {
		printf("你赢了!\n");
	}
}

在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值