【C语言初阶】扫雷游戏(C语言版)

本文详细介绍了如何使用C语言实现扫雷游戏,包括游戏功能、效果展示、设计思路和实现步骤。从创建初始化棋盘、布雷、打印棋盘到排雷逻辑,每个环节都有详细的代码解释。游戏采用输入坐标方式进行,通过随机函数生成雷的位置,并提供安全的首次下子机制,以及自动展开无雷区域的功能。
摘要由CSDN通过智能技术生成

一、游戏功能

  1. 显示该点周围雷的个数
  2. 第一次下子,不炸死
  3. 坐标周围没雷,可以实现展开

二、效果展示

在这里插入图片描述

三、设计思路

这里由于博主目前能力有限,所以这里就用输入坐标的形式来进行排雷。
要想实现上方游戏功能其实也不难,总体思路就是:我们用几个算法模块来模拟游戏规则,实现上方的功能,然后用函数来调用各个模块使游戏跑起来。
接下来我们就来看看如何用C语言代码来实现游戏吧!

四、游戏实现步骤

1、游戏菜单

首先我们需要打印一份游戏菜单界面,让玩家进行选择是否开始游戏,这里我们使用do…while循环语句,使的玩家不至于玩玩一次之后直接退出。

主函数部分:

int main()
{
	int input = 0;
	do
	{
		menu();//打印菜单的函数
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			game();//游戏主体
			break;
		case 2:
			system("cls");//清屏选项
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新选择\n");
			Sleep(1000);//1000毫秒--一秒
			system("cls");
			break;
		}
	
	} while (input);
	return 0;
}

这里我们用了Windows库函数清屏,如果屏幕上显示的东西太多了,我们可以选择2来清屏,还有一个睡眠函数,如果输出错误会短暂的提示你一秒,告诉你选择错误了,然后清屏。

菜单函数:

void menu()
{
	printf("**************************************************\n");
	printf("*******       Welcome to Minesweeper       *******\n");
	printf("**********          1. 开始游戏         **********\n");
	printf("**********          2. 清空屏幕         **********\n");
	printf("**********          0. 退出游戏         **********\n");
	printf("**************************************************\n");
}

效果如图:
在这里插入图片描述

2、创建初始化棋盘

我们在游戏菜单显示出来后,就可以进行选择开始游戏啦!
想玩扫雷就必须得有一个棋盘,这样我们就可以在上面进行排雷。
在这里我们需要用二维数组来创建两个棋盘,一个用于展示给玩家,并储存排雷信息;一个用于在后台随机生成雷并储存。假如我们要打印9X9的棋盘,那我们的二维数组大小也是9X9的吗?,不能,因为我们在设计算法时需要统计该坐标周围8个方位雷的个数,假如要统计边界坐标周围雷的个数,那么就会有数组越界的问题,那我们就要在9X9的边界多上一圈元素,也就要定义11X11的数组元素,这些多出来的一圈元素我们在打印棋盘的时候进行限制不要打印出来就行,如下图:
在这里插入图片描述
创建棋盘:

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

char mine[ROWS][COLS] = { 0 };//存放雷的信息
char show[ROWS][COLS] = { 0 };//存放排查出的雷的信息

在创建完棋盘后,我们就要对两个棋盘进行初始化:
1、对于存放布置雷的棋盘我们用 字符 ’ 1 ’ 表示雷,用字符 ‘ 0 ’ 表示非雷,这里我们首先全部初始化为非雷,雷的排布我们在布雷的地方讲。
2、对于展示给玩家,并储存排雷信息的棋盘我们用 ‘ * ’ 来初始化。

初始化棋盘函数:

	InitBoard(mine, ROWS,COLS,'0');
	InitBoard(show, ROWS,COLS,'*');

函数的定义:

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;
		}
	}
}

3、布雷

我们将棋盘初始化完后,我们就要进行布雷的操作了。

雷的分布位置:我们在玩扫雷时知道,每次雷的分布的位置是不一样的,是随机分布的,所以我们在布雷操作的时候要调用随机函数rand(),在使用随机函数之前,我们要先在主函数中使用srand()函数生成随机起点,这样就可以保证每次雷的位置不一样了。关于这两个函数的使用,可以去MSDN或者cplusplus中去查询其作用。

然后就是雷的个数:每次分布一个就减少一个。

接着就是布雷的范围:因为我们玩家进行排雷是在9X9的棋盘内进行的,所以我们需限定布雷的范围也在9X9的范围内。

接下来我们来看看到底如何实现的吧!

主函数:

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();//打印菜单
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();//游戏主体
			break;
		case 2:
			system("cls");//清屏选项
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新选择\n");
			Sleep(1000);
			system("cls");
			break;
		}
	
	} while (input);
	return 0;
}

布雷函数:

#define EASY_COUNT 10  //雷的个数

SetMine(mine, ROW, COL);

函数的定义:

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int count = EASY_COUNT;//雷的个数
	while (count)
	{
		//生成随机下标(1~9)
		x = rand() % row + 1;
		y = rand() % col + 1;
	
		if (board[x][y] != '1')
		{
			board[x][y] = '1';
			count--;
		}

	}
}

4、打印棋盘

我们将上方操作完成之后,就需要在屏幕上打印出棋盘了,但这里一共有两个棋盘,我们需要打印的棋盘是专门展示给玩家,并储存排雷信息的棋盘即用 ‘ * ’ 初始化的棋盘。
还有就是我们需打印的大小是9X9的范围,而不是全部范围11X11的。
还有我们需要打印棋盘的行数和列数,以便玩家看坐标。

打印棋盘函数:

	DisplayBoard(show, ROW, COL);

函数定义:

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("----------------\n");
	for (i = 0; i <= 9; i++)
	{
		printf("%d ", i);//打印列标
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		printf("%d ", i);//打印行标
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
			
		}
		printf("\n");
	}
	printf("----------------\n");
}

5、排雷

完成上方所有操作后,就到我们最精彩,也是最重要的部分了。
要求:
1、输入排查坐标要在打印的棋盘范围内;
2、统计排查坐标周边八个位置的雷的个数;
3、保证第一次输入坐标绝对安全,不炸死;
4、坐标周围无雷则进行自动展开

排雷主逻辑函数:

FindMine(mine, show, ROW, COL);

统计排查坐标周边八个位置的雷的个数的函数:

GetMineCount(char mine[ROWS][COLS], int x, int y)

第一次安全函数:

safe(char mine[ROWS][COLS], int row,int col,int x, int y)

展开函数

OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)

函数定义(从上往下):

排雷主逻辑函数:

//主逻辑函数
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;//统计排雷的个数
	int count = 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')
			{
				if (0 == win)//第一次踩到雷,重新布雷
				{
					safe(mine, ROW,COL,x, y);
					//DisplayBoard(mine, ROW, COL);
					count = GetMineCount(mine, x, y);

					if (count == 0)
					{
						show[x][y] = ' ';
						win++;
						OpenMine(mine, show, ROW, COL, x, y,&win);//如果周围没有雷,进行扩展
						DisplayBoard(show, row, col);
					}
					else
					{
						show[x][y] = count + '0';
						DisplayBoard(show, row, col);
					}
				}
				else
				{
					printf("很遗憾,你被炸死了\n");
					DisplayBoard(mine, ROW, COL);
					break;
				}
				
			}
			else
			{
				count = GetMineCount(mine, x, y);
				if (count == 0)
				{
					show[x][y] = ' ';
				}
				else
				{
					show[x][y] = count + '0';
				}
				win++;
				OpenMine(mine, show, ROW, COL, x, y,&win);
				DisplayBoard(show, ROW, COL); 
			}
		}
		else
		{
			printf("坐标不在范围内,请重新输入\n");
		}
	}
	if (win == row*col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
	}
}

统计周围雷的个数:

//统计排查坐标周边八个位置的雷的个数
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x - 1][y] +
		mine[x - 1][y - 1] +
		mine[x][y - 1] +
		mine[x + 1][y - 1] +
		mine[x + 1][y] +
		mine[x + 1][y + 1] +
		mine[x][y + 1] +
		mine[x - 1][y + 1] - 8 * '0');
}

第一次下子,不炸死,则重新布雷:

//第一次安全
void safe(char mine[ROWS][COLS], int row,int col,int x, int y)  
{
	mine[x][y] = '0';
	int count = 1;
	while (count)
	{
		//生成随机下标(1~9)
		int i = rand() % row + 1;
		int j = rand() % col + 1;
		if ((mine[i][j] != '1') && i != x && j != y)
		{
			mine[i][j] = '1';
			count--;
		}
	}
}

坐标周围没雷,可以实现展开:

//展开函数
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y,int* p)
{
	int i = -1;
	int j = -1;
	for (i = -1; i < 2; i++)//边界
	{
		for (j = -1; j < 2; j++)
		{
			if (i != 0 || j != 0) // 避免排到自己注意此处的逻辑关系
			{
				if (x + i >= 1 && x + i <= row && y + j >= 1 && y + j <= col)
				{
					if (show[x + i][y + j] == '*' && mine[x + i][y + j] != '1')
					{
						int count = GetMineCount(mine, x + i, y + j);
						if (count != '0')
						{
							show[x + i][y + j] = count + '0';
							(*p)++;
						}
						else
						{
							show[x + i][y + j] = ' ';
							(*p)++;
							OpenMine(mine, show,ROW,COL, x + i, y + j, p);
						}
					}
				}
			}
		}
	}
}

五、总结

和三子棋一样,将整个工程分为game.c,game.h和test.c三个文件。如下图
在这里插入图片描述
1、在头文件game.h主要包括各个函数的声明还有调用库函数所需的头文件以及棋盘行数列数的宏定义,方便以后我们如果想修改行或者列数目,直接修改宏定义的内容即可。
2、源文件game.c中则包括各种函数的实现,该文件中要引用头文件game.h
3、test.c中则包括游戏开始菜单的打印和调用game.c中的函数

game.h内容

#pragma once

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

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


//雷的个数
#define EASY_COUNT 10

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);

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

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

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

//第一次安全
void safe(char mine[ROWS][COLS], int row, int col, int x, int y);

//统计排查坐标周边八个位置的雷的个数
int GetMineCount(char mine[ROWS][COLS], int x, int y);

//坐标周围展开函数
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y,int* p); 

game.c内容

#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 SetMine(char board[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int count = EASY_COUNT;
	while (count)
	{
		//生成随机下标(1~9)
		x = rand() % row + 1;
		y = rand() % col + 1;

		if (board[x][y] != '1')
		{
			board[x][y] = '1';
			count--;
		}

	}
}

//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("----------------\n");
	for (i = 0; i <= 9; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
			
		}
		printf("\n");
	}
	printf("----------------\n");
}

//排雷主逻辑
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;//统计排雷的个数
	int count = 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')
			{
				if (0 == win)//第一次踩到雷,重新布雷
				{
					safe(mine, ROW,COL,x, y);
					//DisplayBoard(mine, ROW, COL);
					count = GetMineCount(mine, x, y);

					if (count == 0)
					{
						show[x][y] = ' ';
						win++;
						OpenMine(mine, show, ROW, COL, x, y,&win);//如果周围没有雷,进行扩展
						DisplayBoard(show, row, col);
					}
					else
					{
						show[x][y] = count + '0';
						DisplayBoard(show, row, col);
					}
				}
				else
				{
					printf("很遗憾,你被炸死了\n");
					DisplayBoard(mine, ROW, COL);
					break;
				}
				
			}
			else
			{
				count = GetMineCount(mine, x, y);
				if (count == 0)
				{
					show[x][y] = ' ';
				}
				else
				{
					show[x][y] = count + '0';
				}
				win++;
				OpenMine(mine, show, ROW, COL, x, y,&win);
				DisplayBoard(show, ROW, COL); 
			}
		}
		else
		{
			printf("坐标不在范围内,请重新输入\n");
		}
	}
	if (win == row*col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
	}
}

//统计排查坐标周边八个位置的雷的个数
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x - 1][y] +
		mine[x - 1][y - 1] +
		mine[x][y - 1] +
		mine[x + 1][y - 1] +
		mine[x + 1][y] +
		mine[x + 1][y + 1] +
		mine[x][y + 1] +
		mine[x - 1][y + 1] - 8 * '0');
}

//保证第一次下子安全
void safe(char mine[ROWS][COLS], int row,int col,int x, int y)  
{
	mine[x][y] = '0';
	int count = 1;
	while (count)
	{
		//生成随机下标(1~9)
		int i = rand() % row + 1;
		int j = rand() % col + 1;
		if ((mine[i][j] != '1') && i != x && j != y)
		{
			mine[i][j] = '1';
			count--;
		}
	}
}

//展开函数
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y,int* p)
{
	int i = -1;
	int j = -1;
	for (i = -1; i < 2; i++)//边界
	{
		for (j = -1; j < 2; j++)
		{
			if (i != 0 || j != 0) // 避免排到自己注意此处的逻辑关系
			{
				if (x + i >= 1 && x + i <= row && y + j >= 1 && y + j <= col)
				{
					if (show[x + i][y + j] == '*' && mine[x + i][y + j] != '1')
					{
						int count = GetMineCount(mine, x + i, y + j);
						if (count != '0')
						{
							show[x + i][y + j] = count + '0';
							(*p)++;
						}
						else
						{
							show[x + i][y + j] = ' ';
							(*p)++;
							OpenMine(mine, show,ROW,COL, x + i, y + j, p);
						}
					}
				}
			}
		}
	}
}

test.c内容

#include "game.h"
void menu()
{
	printf("**************************************************\n");
	printf("*******       Welcome to Minesweeper       *******\n");
	printf("**********          1. 开始游戏         **********\n");
	printf("**********          2. 清空屏幕         **********\n");
	printf("**********          0. 退出游戏         **********\n");
	printf("**************************************************\n");
}

void game()
{
	char mine[ROWS][COLS] = { 0 };//存放雷的信息
	char show[ROWS][COLS] = { 0 };//存放排查出的雷的信息
	//初始化一下棋盘
	InitBoard(mine, ROWS,COLS,'0');
	InitBoard(show, ROWS,COLS,'*');

	//布置雷
	SetMine(mine, ROW, COL);
	//打印棋盘
	//DisplayBoard(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);
	//排查雷
	FindMine(mine, show, ROW, COL);

}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();//打印菜单
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();游戏主体
			break;
		case 2:
			system("cls");//清屏选项
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新选择\n");
			Sleep(1000);
			system("cls");
			break;
		}
	
	} while (input);
	return 0;
}
  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clumsy、笨拙

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值