C语言扫雷小游戏制作(最后有全部源代码)

目录

扫雷游戏实现分析

扫雷游戏主体框架

各个游戏功能实现函数


一:扫雷游戏分析

1.游戏展示由命令控制台显示如图:

将数据存入二维数组打印展示,则需要用到两个二维数组:

        (1)mine数组用与存放雷的信息的棋盘。

        (2)show数组用于保存排一次雷后的情况并打印该棋盘。

2.排雷采用坐标输入的形式。

3.输入坐标,首先会判断该坐标的情况:

        (1)若该坐标不是雷则计算周围类的数量,*如果周围没有雷则需要展开一大片区域(这个功能的实现采用递归实现在后面会着重讲述)。

        (2)若该坐标是雷则该轮游戏结束。

4.一轮游戏结束判断:

        (1)排雷成功结束。

        (2)踩雷结束。

二:游戏主体框架

  1. 由于在实现各种功能时需要为函数传输相应数据,则最好将重要的数据定义为全局变量方便操作:

    int ROW;
    int COL;
    int ROWS;
    int COLS;
    int Count;
    char mine[999][999];
    char show[999][999];

    ROW:游戏棋盘的行;

         COL:游戏棋盘的列;

         ROWS和COLS的作用是将游戏棋盘扩大一圈是用来处理计算雷数量时可能会出现数组越界的问题,具体作用在后面计算周围雷数量的函数会提到。

         Count:代表一轮游戏中雷的数量;

         mine数组和show数组的作用前面讲过了;

       2.先创建游戏选择功能采用switch循环实现

	do
	{

		srand((unsigned)time(NULL));//为随机布置雷做准备
		quit();
		scanf("%d", &m);
		clock_t t1 = clock();//计算时间用
		switch (m)
		{
		case 1://简单难度
			ROW = 9;
			COL = 9;
			ROWS = ROW + 2;
			COLS = COL + 2;
			Count = 10;
			game();
			clock_t t2 = clock();

			printf("所用时间为:%d", (t2 - t1) / 1000);
			printf("\n");
			break;
		case 2://中等难度
			ROW = 16;
			COL = 16;
			ROWS = ROW + 2;
			COLS = COL + 2;
			Count = 40;
			game();
			clock_t t3 = clock();


			printf("所用时间为:%d", (t3 - t1) / 1000);
			printf("\n");
			break;
		case 3://困难难度
			ROW = 32;
			COL = 32;
			ROWS = ROW + 2;
			COLS = COL + 2;
			Count = 150;
			clock_t t4 = clock();

			printf("所用时间为:%d", (t4 - t1) / 1000);
			printf("\n");
			game();
			break;
		case 4://自定义难度
			printf("请输入棋盘大小:");
			scanf("%d %d", &ROW, &COL);
			ROWS = ROW + 2;
			COLS = COL + 2;
			printf("请输入雷的数量:");
			scanf("%d", &Count);
			clock_t t6 = clock();
			game();
			clock_t t5 = clock();

			printf("所用时间为:%d", (t5 - t6) / 1000);
			printf("\n");
			break;
		case 0://退出游戏
			break;
		default://输入不正确提示一下
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (m);
	return 0;
}

最外面的do......while循环是实现启动一次可以玩多次的,不想玩了就输入0退出程序。

case :1 2 3 4 0 代表不同的游戏选择

        1、2、3、4  :分别给ROW,COL,Count赋值代表了游戏难度,然后进入game函数进行游戏;

        0 :退出游戏;

这就是主体框架:一场游戏的开始到结束。

三:各个游戏功能实现函数

        1.首先就是打印一个游戏菜单程序用自定义quit函数实现;

void quit()
{
	printf("******************************\n");
	printf("*********  1.简单      *******\n");
	printf("*********  2.中等      *******\n");
	printf("*********  3.困难      *******\n");
	printf("*********  4.自定义    *******\n");
	printf("*********  0.Exit      *******\n");
	printf("******************************\n");
	printf("请选择:");
}

        2.然后是game函数(游戏的核心区域)

void game()
{

	//棋盘初始化
	InitBoard(mine, ROWS, COLS, '0');//
	InitBoard(show, ROWS, COLS, '*');//
	//布置雷
	SetMine(mine, ROW, COL, Count);
	PrintShow(show, ROW, COL);
	//PrintMine(mine, ROW, COL);
	//排查雷
	FindMine(mine, show, ROW, COL, Count);
	//
}

        (1)InitBoard函数是将游戏棋盘初始化形成基本框架:采用简单的循环就可实现就不解释了

//棋盘初始化
void InitBoard(char borad[999][999], int rows, int cols, char set)
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			borad[i][j] = set;
		}
	}
}

        (2)SetMine函数为布置雷,将雷的信息放在mine数组里:

//设置雷
void SetMine(char mine[999][999], int row, int col, int count)
{
	
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

由于每一次游戏布置的雷需要随机的所以在函数里执行了一次  srand((unsigned)time(NULL));用来改变rand函数的种子实现随机;

        rand()%row+1 是实现把这个数控制在1—row这个区域内不然会由越界情况;

后面的if语句是为了防止当出现了随机的坐标重复的情况,如果坐标满足就给mine数组的mine[x][y]赋值为字符‘1’代表一个雷;

count代表雷的数量;

        (3)PrintShow函数是将Show数组打印出来:

//打印show棋盘
void PrintShow(char borad[999][999], int row, int col)
{
	int i, j;
	printf("-------扫雷游戏-------\n");
	for (i = 0; i <= col; i++)
	{
		printf("\033[33m%2d\033[0m ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("\033[33m%2d\033[0m ", i);
		for (j = 1; j <= col; j++)
		{
			if (borad[i][j] != '*')
			{
				printf("\033[32m%2c\033[0m ", borad[i][j]);
			}
			else
			{
				printf("\033[31m%2c\033[0m ", borad[i][j]);
			}
		}
		printf("\n");
	}
}

这里为了让打印出来的棋盘好看一点就给打印出来的数据加上了颜色如                       printf("\033[33m%2d\033[0m ", i);打印出来的是黄色的。具体的用法请自行查阅资料。

这里打印也是简单的循环。

从这里开始就正式进入游戏了

        (4)FindMine函数用来实现 坐标的输入与雷的查找:

//查找雷
void FindMine(char mine[999][999], char show[999][999], int row, int col, int count)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < row * col - count)//游戏结束条件
	{
		printf("请输入你要排查的坐标:");

		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标合法
		{
			if (show[x][y] == '*')//排除重复排查的坐标
			{
				if (mine[x][y] == '1')
				{
					system("cls");
					printf("很遗憾,你被炸死了!\n");//被炸死结束
					PrintMine(mine, row, col);
					break;
				}
				else
				{
					UnfloodMineArround(mine, show, row, col, x, y);
					system("cls");
					int count1 = Success(show, row, col);//判断排雷成功则跳出循环结束游戏
					if (count1 == count)
					{
						system("cls");
						printf("恭喜你,排雷成功!\n");
						PrintMine(mine, row, col);
						break;
					}
					PrintShow(show, row, col);
					win++;

				}
			}
			else
			{
				printf("该坐标已经排查了,请重新输入\n");
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	}
	if (win == row * col - count)//结束
	{
		system("cls");
		printf("恭喜你,排雷成功!\n");
		PrintMine(mine, row, col);
	}
}

这里就是关键的地方了

        首先就是输入需要排查的坐标

        if (x >= 1 && x <= row && y >= 1 && y <= col)这个if语句是判断输入的坐标是否合法

                  if (show[x][y] == '*')这个if语句是排除重复排查的坐标

        坐标合法且不重复就开始判断是否踩雷 这里用mine[x][y]是否为字符1来判断,因为在前面讲了mine数组是用来存放雷的信息的;

                如果判断踩雷则游戏结束了

                反之先调用UnfloodMineArround函数

void  UnfloodMineArround(char  mine[999][999], char show[999][999], int row, int col, int x, int y)
{
	if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标合法
	{
		int count = GetMineCount(mine, x, y);
		show[x][y] = count + '0';
		if (count == 0)
		{
			for (int i = x - 1; i <= x + 1; i++)
			{
				for (int j = y - 1; j <= y + 1; j++)
				{
					if (show[i][j] == '*')
					{
						UnfloodMineArround(mine, show, row, col, i, j);
					}
				}
			}
		}
		else
		{
			return;
		}
	}
	else
		return;

}

                这个函数首先判断坐标合法然后调用Get MineCount函数计算周围雷的数量

/*int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x][y - 1] + mine[x][y + 1] + mine[x - 1][y - 1] +mine[x - 1][y] + mine[x - 1][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y + 1] + mine[x + 1][y] - 8 * '0');
}*/
//获取周围雷的数量
int GetMineCount(char mine[999][999], int x, int y)
{
	int i = 0;
	int count = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (int j = y - 1; j <= y + 1; j++)
		{
			if (mine[i][j] == '1')
				count++;
		}

	}
	return count;
}

 这里计算雷的数量由两个方法:

          第一种:由于我们的非雷区域存的是字符‘0’,而雷是字符’1‘,则计算是可以直接将该坐标周围的八个坐标加起来在减去一个8*‘0’就可以计算出雷的数量。

        第二种:直接用循环遍历这九个坐标由字符‘1’计数器就加1,一样可以实现。

雷的数量计算出来之后就判断是否该坐标周围没有雷

注:特殊情况 如果输入的坐标在边界上的时候在计算周围雷的个数时会出现越界的问题所以前面的ROWS与COLS的作用就是来解决它,我们整个棋盘是ROWS行COLS列但是游戏所需的只有除去外围一圈的大小

像这样我们游戏的真正棋盘是红色以类的部分外面一圈就是用来出来越界问题的。

计算结束后进入是否展开判断

        1. 如果周围有雷则不进行展开

        2.若周围没有雷则让这九个坐标调用UnfloodMineArround函数实现递归

这里讲一下这个递归:

                  这里重要就是   if (show[i][j] == '*')  这个if语句,数组show原先保存的是字符‘*’如果排查了就会变成雷的个数,这里只有没有排查过的才会进入UnfloodMineArround函数,然后递归结束当然当计算出有周围有雷也会结束递归,保证了每个点只排查一次。

每一次排查雷结束后会调用Success函数来判断是否排雷成功

//排雷成功的判断
int Success(char show[999][999], int row, int col)
{
	int count = 0;
	int i = 0;
	int j = 0;
	for (i = 1; i <= row; i++)
	{

		for (j = 1; j <= col; j++)
		{
			if (show[i][j] == '*')
				count++;
		}
	}
	return count;
}

这里就是计算show数组里剩余的*的个数然后返回统计的结果

如果返回值对于Count(雷的个数)则本轮游戏结束。

到这里整个框架就结束了;

然后补充说明一下:1.整个游戏采用二维数组实现;

                                 2.该游戏也粗略设置了一下时间的计录用的是clock()函数实现;

                                 3.代码里运用了一些函数实现优化界面,例如每次打印前会执行一下system(“cls”)这个是用来清除空之前控制台打印的棋盘这样就可以让界面保持整洁;

                                 4.游戏不足在与没有设置玩家标记雷的功能,此功能会在以后能够实现用图像操作时在进行补充;

                                 5.还有一个不足就是当自定义棋盘大小时如果棋盘行、列为3位数以上会导致棋盘与外层坐标对不齐;

感谢阅读!

既然都看到这里了就点个赞吧!谢谢!

最后呈上源代码:

test.c://游戏主体

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
int ROW;
int COL;
int ROWS;
int COLS;
int Count;
char mine[999][999];
char show[999][999];
void quit()
{
	printf("******************************\n");
	printf("*********  1.简单      *******\n");
	printf("*********  2.中等      *******\n");
	printf("*********  3.困难      *******\n");
	printf("*********  4.自定义    *******\n");
	printf("*********  0.Exit      *******\n");
	printf("******************************\n");
	printf("请选择:");
}
void game()
{

	//棋盘初始化
	InitBoard(mine, ROWS, COLS, '0');//
	InitBoard(show, ROWS, COLS, '*');//
	//布置雷
	SetMine(mine, ROW, COL, Count);
	PrintShow(show, ROW, COL);
	//PrintMine(mine, ROW, COL);
	//排查雷
	FindMine(mine, show, ROW, COL, Count);
	//
}
int main()
{
	int m;
	do
	{

		srand((unsigned)time(NULL));
		quit();
		scanf("%d", &m);
		clock_t t1 = clock();
		switch (m)
		{
		case 1:
			ROW = 9;
			COL = 9;
			ROWS = ROW + 2;
			COLS = COL + 2;
			Count = 10;
			game();
			clock_t t2 = clock();

			printf("所用时间为:%d", (t2 - t1) / 1000);
			printf("\n");
			break;
		case 2:
			ROW = 16;
			COL = 16;
			ROWS = ROW + 2;
			COLS = COL + 2;
			Count = 40;
			game();
			clock_t t3 = clock();


			printf("所用时间为:%d", (t3 - t1) / 1000);
			printf("\n");
			break;
		case 3:
			ROW = 32;
			COL = 32;
			ROWS = ROW + 2;
			COLS = COL + 2;
			Count = 150;
			clock_t t4 = clock();

			printf("所用时间为:%d", (t4 - t1) / 1000);
			printf("\n");
			game();
			break;
		case 4:
			printf("请输入棋盘大小:");
			scanf("%d %d", &ROW, &COL);
			ROWS = ROW + 2;
			COLS = COL + 2;
			printf("请输入雷的数量:");
			scanf("%d", &Count);
			clock_t t6 = clock();
			game();
			clock_t t5 = clock();

			printf("所用时间为:%d", (t5 - t6) / 1000);
			printf("\n");
			break;
		case 0:
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (m);
	return 0;
}

       game.c://功能实现函数

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

//棋盘初始化
void InitBoard(char borad[999][999], int rows, int cols, char set)
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			borad[i][j] = set;
		}
	}
}
//打印show棋盘
void PrintShow(char borad[999][999], int row, int col)
{
	int i, j;
	printf("-------扫雷游戏-------\n");
	for (i = 0; i <= col; i++)
	{
		printf("\033[33m%2d\033[0m ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("\033[33m%2d\033[0m ", i);
		for (j = 1; j <= col; j++)
		{
			if (borad[i][j] != '*')
			{
				printf("\033[32m%2c\033[0m ", borad[i][j]);
			}
			else
			{
				printf("\033[31m%2c\033[0m ", borad[i][j]);
			}
		}
		printf("\n");
	}
}
//打印mine棋盘
PrintMine(char borad[999][999], int row, int col)
{
	int i, j;
	printf("-------扫雷游戏-------\n");
	for (i = 0; i <= col; i++)
	{
		printf("\033[33m%2d\033[0m ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("\033[33m%2d\033[0m ", i);
		for (j = 1; j <= col; j++)
		{
			if (borad[i][j] == '1')
			{
				//printf("\033[32m%2c\033[0m ", borad[i][j]);
				printf("\033[31m%2c\033[0m ", borad[i][j]);

			}
			else
			{
				//printf("\033[31m%2c\033[0m ", borad[i][j]);
				printf("\033[32m%2c\033[0m ", borad[i][j]);

			}
		}
		printf("\n");
	}
}
//设置雷
void SetMine(char mine[999][999], int row, int col, int count)
{
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}
//int GetMineCount(char mine[ROWS][COLS], int x, int y)
//{
//	return (mine[x][y - 1] + mine[x][y + 1] + mine[x - 1][y - 1] +mine[x - 1][y] + mine[x - 1][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y + 1] + mine[x + 1][y] - 8 * '0');
//}
//获取周围雷的数量
int GetMineCount(char mine[999][999], int x, int y)
{
	int i = 0;
	int count = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (int j = y - 1; j <= y + 1; j++)
		{
			if (mine[i][j] == '1')
				count++;
		}

	}
	return count;
}
// 周围都不是雷则展开一片
void  UnfloodMineArround(char  mine[999][999], char show[999][999], int row, int col, int x, int y)
{
	if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标合法
	{
		int count = GetMineCount(mine, x, y);
		show[x][y] = count + '0';
		if (count == 0)
		{
			for (int i = x - 1; i <= x + 1; i++)
			{
				for (int j = y - 1; j <= y + 1; j++)
				{
					if (show[i][j] == '*')
					{
						UnfloodMineArround(mine, show, row, col, i, j);
					}
				}
			}
		}
		else
		{
			return;
		}
	}
	else
		return;

}
//查找雷
void FindMine(char mine[999][999], char show[999][999], int row, int col, int count)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < row * col - count)//游戏结束条件
	{
		printf("请输入你要排查的坐标:");

		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标合法
		{
			if (show[x][y] == '*')//排除重复排查的坐标
			{
				if (mine[x][y] == '1')
				{
					system("cls");
					printf("很遗憾,你被炸死了!\n");//被炸死结束
					PrintMine(mine, row, col);
					break;
				}
				else
				{
					UnfloodMineArround(mine, show, row, col, x, y);
					system("cls");
					int count1 = Success(show, row, col);//判断排雷成功则跳出循环结束游戏
					if (count1 == count)
					{
						system("cls");
						printf("恭喜你,排雷成功!\n");
						PrintMine(mine, row, col);
						break;
					}
					PrintShow(show, row, col);
					win++;
				}
			}
			else
			{
				printf("该坐标已经排查了,请重新输入\n");
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	}
	if (win == row * col - count)//结束
	{
		system("cls");
		printf("恭喜你,排雷成功!\n");
		PrintMine(mine, row, col);
	}
}
//排雷成功的判断
int Success(char show[999][999], int row, int col)
{
	int count = 0;
	int i = 0;
	int j = 0;
	for (i = 1; i <= row; i++)
	{

		for (j = 1; j <= col; j++)
		{
			if (show[i][j] == '*')
				count++;
		}
	}
	return count;
}

game.h://功能实现函数声明

#pragma once
#include<time.h>
#include<stdlib.h>
#include<stdio.h>
//#include<Windows.h>
//#define ROW 9
//#define COL 9
//#define COLS COL+2
//#define ROWS ROW+2
#define Easy_count 10
//声明棋盘初始化函数
void InitBoard(char borad[999][999], int rows, int cols, char set);
//声明打印函数
//void Print(char borad[999][999], int row, int col);
//声明布置雷函数
void SetMine(char mine[999][999], int row, int col,int count);
//声明排查雷函数
void FindMine(char mine[999][999], char show[999][999], int row, int col, int count);
//声明打印排查出雷的信息
void PrintShow(char borad[999][999], int row, int col);
//声明打印全部棋盘(有雷的信息)
PrintMine(char borad[999][999], int row, int col);
//声明展开一片函数
void  UnfloodMineArround(char  mine[999][999], char show[999][999], int row, int col, int x, int y);
//声明游戏成功结束的函数
int Success(char show[999][999], int row, int col);

编程小白在努力!

冲!

冲!

冲!

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值