结合 数组 与 函数 制作一个简易版“扫雷”游戏

扫雷游戏的制作

1.功能说明

扫雷的棋盘是9*9的格子

默认随机布置十个雷

可以排查雷:
(1)如果位置不是雷,就显示周围有几个雷(九宫格);
(2)如果位置是雷,就炸死,游戏结束;
(3)把除了10个雷之外的所有非雷都找出来,排雷成功,游戏结束。

2. 制作思路

(1)首先创建一个头文件和两个源文件
在这里插入图片描述

game.h  --------------  函数的声明、类型的声明
game.c  --------------  函数是如何实现的
test.c  --------------  用于测试代码

(2)然后在test.c文件搭建一个代码框架(思路会在代码注释里解释):

#include <stdio.h>
//声明自己写的头文件时用 " "
#include "game.h"	//参考 3.函数和变量的声明

//首先给出游戏菜单
void menu()
{
	printf("************************\n");
	printf("******  1.play  ********\n");//输入1开始游戏
	printf("*****   0.exit  ********\n");//输入0结束游戏
	printf("************************\n");

}

//接着就是一个超简洁的main函数
int main()
{
	test();	//调用test函数,那我们就写一个test函数
	return 0;
}


//书接上回,写一个测试函数
void test()
{
	
	int input = 0;//创建一个变量存放输入值
	srand((unsigned)time(NULL));
	//前面猜数字游戏已经详细介绍过,由于rand函数返回的是伪随机数,
	//而srand用来初始化随机数的生成器,使rand产生一个真正的随机数

	//由于可以一直玩,知道输入0退出游戏,这里我们写一个do while循环,先执行再判断
	do
	{
		menu();	//调用menu函数,打印菜单
		printf("请输入整数:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();//这里要调用一个game函数,所以还要写一个game函数
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default :
			printf("输入有误,请重新输入\n");
			break;
		}

	} while (input);	//while里放input,这样当输入0时即可终止循环
}


//再书接test()函数,写一个game函数
void game()
{
	//由于是9*9的棋盘,我们创建二维数组存储棋盘
	//由于我们既需要存放雷的信息,又要存放排查雷的信息,容易混淆,
	//这里我们创建两个二维数组,一个存放雷的信息,一个存放排查雷的信息
	//这里我们以 * 显示未排查的区域,为了统一操作数组,我们使用了字符数组
	char mine[ROWS][COLS] = { 0 };	//用于存放布置雷的信息
	char show[ROWS][COLS] = { 0 };	//用于存放排查出的雷的个数信息

	//初始化棋盘
	//为了统一操作,增加一个初始值参数
	//ROWS、ROW和COLS、COL是行和列的变量名,在头文件里会定义
	InitBoard(mine, ROWS, COLS, '0');	//以字符'0'初始化存放雷的棋盘
	InitBoard(show, ROWS, COLS, '*');	//以字符'*'初始化显示排查信息的棋盘

	//打印棋盘
	DisplayBoard(show, ROW, COL );//我们需要打印到屏幕上的是排查雷的信息,所以实参为show
	//DisplayBoard(mine, ROW, COL);	//测试一下


	//布置雷
	SetMine(mine, ROW, COL);//雷实在mine棋盘上布置的,所以传了mine
	//DisplayBoard(mine, ROW, COL);	//测试一下

	//排查雷的信息
	FindMine(mine, show, ROW, COL);//在game.c文件会详细阐述如何实现
}

在这里插入图片描述

搭建好了框架,我们就可以根据game()函数中要实现的功能写出相应的函数了。

3. 函数和变量的声明

//在这里包含头文件,在源文件引用该头文件时也能包含这些头文件
#include <stdio.h>
#include <stdlib.h>//srand()函数和rand()函数
#include <time.h>

//变量的声明
#define ROW 9	//棋盘行数为9
#define COL 9	//棋盘列数为9

//因为在排查边界位置时为了防止越界访问需要扩大一圈
#define ROWS ROW+2	
#define COLS ROW+2

//简单模式,雷的个数为10个
#define EASY_COUNT 10

//函数的声明
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);	
//不能用int ROWS和int COLS,因为一旦这样就分不清ROWS是在形参中定义的还是在define中定义的了

//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);

//布置雷
void SetMine(char mine[ROWS][COLS], int row,int col);


//排查出的雷的信息
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

在这里插入图片描述

4.game()函数中功能的实现 (注意代码注释)

首先包含头文件

#include "game.h"

(1)实现棋盘的初始化

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;	//初始化为传给set的字符
		}
	}
}

在这里插入图片描述

(2)实现棋盘的打印

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("---------扫雷---------\n");

	//打印出列标
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	
	//打印出9*9的棋盘
	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");//每打印一行就换行
		
	}
}

(3)实现在棋盘上布置雷

void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	int x = 0;	//行标
	int y = 0;	//列标
	
	//以count作为判断条件,当布置完雷后,count为0,跳出循环
	while (count)	
	{
		x = rand() % row + 1;	//1~9
		y = rand() % col + 1;	//1~9
		
	//如果要放的坐标没有雷,就放一个雷,由此可知,while循环至少执行10次
		if (mine[x][y] != '1')	
		{
			mine[x][y] = '1';	//布置雷
			count--;
		}
	}
}

在这里插入图片描述
(4)排查出的雷的信息

//排查出的雷的信息
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;	//排查出非雷的个数
	
	//循环条件:win<(row*col-EASY_COUNT)
	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	//没有踩到雷的情况,返回周围一圈雷的个数
			{
				//返回排查的信息,这里调用了GetMindCount()函数,
				//所以还要写一个GetMindCount()函数实现该功能,
				//我们可以直接在当前函数的前面写一个GetMindCount()函数,这样调用GetMindCount()函数也不会报错
				int count = GetMindCount(mine, x, y);	
				
				show[x][y] = count +'0';//show是字符数组,得到'count'
				DisplayBoard(show, ROW, COL);//打印排查出的雷的信息
				win++;
			}
		}

		else	//提醒玩家正确输入行列
		{
			printf("输入行列超出范围,x(1~9) y(1~9)\n");
		}
	}
	
	//循环结束后,判断玩家是因为炸死才结束的循环还是因为将雷全都排出结束的循环
	if (win == (row * col - EASY_COUNT))
	{
		printf("恭喜你,排雷成功\n");
		
		//排雷成功后,打印出布置雷的棋盘
		DisplayBoard(mine, ROW, COL);
	}
}

(4.1)所写的排查出的雷的信息的函数调用了GetMindCount()函数,接下来我们需要实现该功能


//排查位置周围一圈雷的个数
//该函数有返回值,返回类型为 int
int GetMindCount(char mine[ROWS][COLS], int x, int y)
{
	int count = 0;//将周围一圈雷的个数存放到count中
	
	//这是第一种算法
	int i = 0;
	for (i = -1; i <= 1; i++)
	{
		int j = 0;
		for (j = -1; j <= 1; j++)
		{
			count += (mine[x+i][y+j] - '0');// '1'-'0'=1 (49-48=)
		}
	}
	return count;	//返回count

	//算法2:
	//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 - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0');

}

在这里插入图片描述

5. 源码

话不多说,直接上源码:

(1)game.h

#pragma once	//VS编译器头文件自带的东西,使用其他编译器只需将下面的代码复制粘贴到头文件中即可

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

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS ROW+2
#define EASY_COUNT 10

//初始化棋盘
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 mine[ROWS][COLS], int row,int col);


//排查出的雷的信息
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

(2)game.c

#define _CRT_SECURE_NO_WARNINGS	//VS编译器为了正常使用scanf()函数加上的定义


#include "game.h"

//实现棋盘的初始化
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++)
	{
		printf("%d ", i);
	}
	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");
	}
}

//实现在棋盘上布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	int x = 0;
	int y = 0;
	
	while (count)
	{
		x = rand() % row + 1;
		y = rand() % col + 1;
		if (mine[x][y] != '1')
		{
			mine[x][y] = '1';	//布置雷
			count--;
		}
	}
}

//排查位置周围一圈雷的个数
//在FindMine()中会调用此函数,可以写在FindMine()函数前面,算是一种特殊的声明
int GetMindCount(char mine[ROWS][COLS], int x, int y)
{
	int count = 0;
	int i = 0;
	for (i = -1; i <= 1; i++)
	{
		int j = 0;
		for (j = -1; j <= 1; j++)
		{
			count += (mine[x+i][y+j] - '0');// '1'-'0'=1
		}
	}
	return count;
	
	//另一种算法:
	//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 - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 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
			{
				int count = GetMindCount(mine, x, y);	//返回排查的信息
				show[x][y] = count +'0';
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("输入行列超出范围,x(1~9) y(1~9)\n");
		}
	}
	if (win == (row * col - EASY_COUNT))
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}
}

(3)test.c

#define _CRT_SECURE_NO_WARNINGS


#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 };	//用于存放排查出的雷的个数信息

	//初始化棋盘
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');

	//打印棋盘
	DisplayBoard(show, ROW, COL );
	//DisplayBoard(mine, ROW, COL);	//测试一下

	//布置雷
	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, ROW, COL);	//测试一下

	//排查雷的信息
	FindMine(mine, show, ROW, COL);
}

void test()
{
	int input = 0;
	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);
}

int main()
{

	test();
	return 0;
}

6. 小结

先给各位画个大饼:
在这里插入图片描述

在线扫雷游戏: https://www.minesweeper.cn/

以上就是一个简易版扫雷的制作过程了,当然确实有些穷酸了。不过随着我对C
语言更深入的学习,有机会一定会再对其升级,做出一个功能更加齐全的扫雷游
戏,然后和大家分享制作过程。如果小编的文章对你有用的话,还请给小编一个
大大的赞👍,谢谢xdm!
  • 11
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值