如何利用控制台实现简易版扫雷游戏

目录

声明:博主使用的VS2022集成开发环境

1.思路分析

1.1 扫雷游戏的介绍

1.2 扫雷游戏的思路分析

2.扫雷游戏代码实现思路

2.1 编写主函数并创建菜单功能

2.2 扫雷游戏的游戏部分

2.2.1 创建扫雷棋盘

2.2.2 初始化扫雷棋盘

2.2.3 埋雷

 2.2.4 打印棋盘

2.2.5 扫雷

2.2.6 总结

3 扫雷游戏代码

3.1 game.h 文件

3.2 game.c 文件

 3.3 test.c 文件


声明:博主使用的VS2022集成开发环境

1.思路分析

1.1 扫雷游戏的介绍

        扫雷游戏相信大家都玩过,就是左图这样的形式,通过排查雷的坐标来获得胜利,左侧的扫雷就比较高级些,目前博主技术有限,就先介绍下通过控制台实现的简易版扫雷。

1.2 扫雷游戏的思路分析

        扫雷游戏就相当于一个9*9的二维数组,而我们可以通过创建数组来实现扫雷游戏的棋盘,那么,如何表示是雷还是不是雷,这里博主用 0 表示非雷,用1 表示雷,如果,选择一个位置,就要在该位置上显示周围九宫格里有几个雷。

        

        如上面左图所示,如果我们想要求红色区域周围九宫格的雷的数量的话,就会产生越界,那么如何解决呢,我们可以像右图所示一样创建一个11*11的九宫格,周围都不是雷,用0代替,那么9*9的范围内任意一个格子都能求得周围九宫格里有几个雷了。

左图黄色区域九宫格内有一个1,也就是一个雷,那么中心数字应该变成1,但是这样的话就没办法区分这个1 表示的是该位置是雷,还是说周围九宫格内有一个雷,就会分不清,那么,我们就想到了,可以再创建一个数组,第一个数组用来存放雷,第二个数组用来存放选择排雷的位置周围九宫格内有几个雷。这样就解决了这个问题。

我们就可以创建两个数组,第一个数组是存放地雷的地方,第二个数组是存放该位置周围九个位置有几个雷,当然默认情况下我们是看不见那里是雷,哪里不是雷,因此我们可以将第二个数组用字符 ' * ' 初始化,那么第一个表可以将数字1和0也换成字符类型,方便处理,这样两个表的设定就好了,如下图:

        左边图形代表埋的雷的9*9数组,右边则根据左侧的数组来统计玩家选择的位置周围有几个雷,显示在右侧棋盘中,左侧棋盘是不会展示给玩家看的,玩家会通过右侧棋盘来进行游戏的实现,直到雷全部排完获胜,或者排到了雷被炸死,重新进行游戏。

2.扫雷游戏代码实现思路

        扫雷游戏的代码大概有快200行,如果都写在一个文件里,不太好进行编程,后期找bug也不太容易,后期修改也不容易找到想要修改的位置。因此我们可以创建三个文件来实现扫雷游戏的代码,game.h文件用来对用于游戏的自定义函数的声明,game.c用来实现用于游戏的自定义函数的定义,test.c文件用来放代码的主体main函数并且使用用于游戏的自定义的函数,这样就不会导致代码堆在一个文件里面。

        首先创建test.c,game.c,game.h三个文件(文件名可以根据自己的习惯来命名)。

2.1 编写主函数并创建菜单功能

        一般游戏的开始都会有一个菜单,我们就创建一个menu()函数作为菜单,1.play或者2.exit两种功能,当然我们不可能只玩一次就自动退出游戏了,可以选择多次进行游戏,自行来选择退出游戏,这里就用到了do-while循环,当然为防止输入错误(输入的不是整数)导致死循环,这里我还用到了while(getchar() != '\n'; 来清空缓存区,重新输入。

        如果不理解博主为什么要写while(getchar() != '\n'; ,可以参看博主的另一篇博客:

如何解决scanf函数输入错误信息导致的死循环

//在 test.c 文件中

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

void menu()
{
	printf("\n");
	printf("1.play\n");
	printf("2.exit\n");
	printf("\n");
}
int main()
{
	int input = 1;
	do
	{
		menu();
		printf("请选择->\n");
		//防止输入的不是数字,导致死循环,用getchar()清空缓冲区,重新输入数字。
		if (scanf("%d", &input) != 1)
		{
			printf("输入错误,请输入数字!!!\n");
			while (getchar() != '\n');
			continue;
		}
		switch (input)
		{
			case 1:
			{
				//game();
				break;
			}
			case 0:
			{
				printf("退出游戏!!!\n");
				break;
			}
			default:
			{
				printf("输入错误,请重新输入!!!\n");
				break;
			}
		}
	} while (input);
	return 0;
}

2.2 扫雷游戏的游戏部分

        接下来就该实现扫雷游戏的主体部分,这里我自定义的game();函数来实现扫雷游戏的内容。

2.2.1 创建扫雷棋盘

        这里我们就可以创建两个11*11的数组作为扫雷的棋盘,实际上是9*9的棋盘,(外面的一圈是防止后面计算九宫格有几个雷时超出范围)。/

//在 test.c 文件中
void game()
{
	char mine[11][11] = { 0 };
	char show[11][11] = { 0 };
}

        但如果你后面想要修改棋盘大小的话,就需要一个一个的修改数组的大小,太麻烦了,这里介绍一种方法,在game.h文件里,定义行和列的常量,后面就用定义的常量来代替数字。ROW和COL表示的是9*9,ROWS和COLS表示的是11*11。这四个值都是定义的常量。

//在 game.h 文件中
#define ROW 9
#define COL 9

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

        上面的代码是在game.h 头文件中,test.c文件想要使用就需要包含头文件 ,而自定义的头文件包含时应该这样使用:#include "game.h" 用双引号包含头文件。那我们就可以把test.cgame.c需要的头文件都放到game.h头文件中,然后test.cgame.c只需要包含game.h头文件即可。(包含头文件本质就是将头文件里的内容复制一份过来)。

//在 test.c 文件中
void game()
{
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
}
2.2.2 初始化扫雷棋盘

        我们需要初始化两张棋盘,一张是11*11的全为‘0’ 的无雷棋盘,另一张是11*11的全为‘*’的隐藏棋盘。这里我传递了四个参数,数组,行,列,初始化字符。

//在 test.c 文件中
void play()
{
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };

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

         下面是在game.h 文件中声明初始化棋盘函数的。

//在 game.h 文件中
#include <stdio.h>

#define ROW 9
#define COL 9

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

#define AMOUNT 20

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

         下面是在game.c 文件中定义初始化棋盘函数的。

// 在 game.c 文件中
void InitBoard(char me[ROWS][COLS], int rows, int cols, char a)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			me[i][j] = a;
		}
	}
}
2.2.3 埋雷

        我们需要对第一张棋盘进行随机埋雷,这里要用到srandrand函数,下面这篇博客有所介绍 :srand和rand函数介绍

//在 test.c 文件中
void play()
{
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };

	//埋雷
	SetMine(mine, ROW, COL);

}

        下面是在game.h 文件中声明埋雷函数的。 

//在 game.h 文件中

//埋雷
void DisplayBoard(char me[ROWS][COLS], int row, int col);

        下面是在game.h 文件中定义埋雷函数的。 

//在 game.c 文件中
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int i = 0;
	int count = AMOUNT;
	while (count > 0)
	{
		int a = rand() % row + 1;
		int b = rand() % col + 1;
		if (mine[a][b] == '0')
		{
			mine[a][b] = '1';
			count--;
		}
	}
}
 2.2.4 打印棋盘

        这里我们只需要打印第二个棋盘给用户看就行,第一个棋盘显示的是雷的位置,是给我们调试时候显示使用的。

//在 test.c 文件中
void play()
{
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };

	//打印棋盘
	//DisplayBoard(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);
}

        下面是在game.h 文件中声明打印棋盘函数的。 

//在 test.h 文件中

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

        下面是在game.h 文件中定义打印棋盘函数的。 

//在 game.c 文件中
void DisplayBoard(char me[ROWS][COLS], int row, int col)
{
	int i = 0;
	int a = 1;
	printf("-------扫雷--------\n");
	for (i = 0; i <= 9; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", a);
		a++;
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", me[i][j]);
		}
		printf("\n");
	}
}
2.2.5 扫雷
//在 test.c 文件中
void play()
{
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };

	//扫雷
	FindBoard(mine, show, ROW, COL);
}

         下面是在game.h 文件中声明扫雷函数的。 

//在 test.h 文件中

//扫雷
void FindBoard(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col);

        下面是在game.h 文件中定义扫雷函数的。 

        下面我又定义了一个need函数,作用是计算用户选择位置的周围九宫格的雷的总数,返回到扫雷函数,打印在第二个棋盘上,显示给用户。

//在 game.c 文件中

int need(char mine[ROWS][COLS],int x,int y)
{
	int i = 0;
	int sum = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		int j = 0;
		for (j = y - 1; j <= y + 1; j++)
		{
			sum += mine[i][j] - 48;
		}
	}
	return sum;
}

void FindBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int amount = 0;
	while (amount < row * col - AMOUNT)
	{
		while (1)
		{
			printf("请输入要扫雷的行和列:\n");
			if (scanf("%d %d", &x, &y) != 2)
			{
				printf("输入错误,请输入数字!!!\n");
				while (getchar() != '\n');
				continue;
			}
			break;
		}
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] != '1')
			{
				if (show[x][y] == '*')
				{
					int a = need(mine, x, y);
					printf("%d\n", a);
					show[x][y] = a + '0';
					DisplayBoard(show, ROW, COL);
					amount++;
				}
				else
				{
					printf("已排查,请重新输入!!!\n");
				}
			}
			else
			{
				printf("很遗憾,你被炸死了!!!\n");
				break;
			}	
		}
		else
		{
			printf("输入错误,请重新输入!!!\n");
		}
	}
	if (amount == row * col - AMOUNT)
	{
		printf("恭喜你,扫雷成功!!!\n");
	}
}
2.2.6 总结

        以上就是我对扫雷游戏的整体分析,分为了三个文件来进行编程,test.c ,game.c ,game.h 三个文件,test.c 存放的main函数,以及游戏所需函数的使用,game.h 存放的是游戏所需函数的声明,game.c 存放的是游戏所需函数的定义。

3 扫雷游戏代码

3.1 game.h 文件

#pragma once

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

#define ROW 9
#define COL 9

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

#define AMOUNT 20

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

//埋雷
void DisplayBoard(char me[ROWS][COLS], int row, int col);

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

//扫雷
void FindBoard(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col);

3.2 game.c 文件

#define _CRT_SECURE_NO_WARNINGS

#include "game.h"

void InitBoard(char me[ROWS][COLS], int rows, int cols, char a)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			me[i][j] = a;
		}
	}
}

void DisplayBoard(char me[ROWS][COLS], int row, int col)
{
	int i = 0;
	int a = 1;
	printf("-------扫雷--------\n");
	for (i = 0; i <= 9; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", a);
		a++;
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", me[i][j]);
		}
		printf("\n");
	}
}

void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int i = 0;
	int count = AMOUNT;
	while (count > 0)
	{
		int a = rand() % row + 1;
		int b = rand() % col + 1;
		if (mine[a][b] == '0')
		{
			mine[a][b] = '1';
			count--;
		}
	}
}

int need(char mine[ROWS][COLS],int x,int y)
{
	int i = 0;
	int sum = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		int j = 0;
		for (j = y - 1; j <= y + 1; j++)
		{
			sum += mine[i][j] - 48;
		}
	}
	return sum;
}
void FindBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int amount = 0;
	while (amount < row * col - AMOUNT)
	{
		while (1)
		{
			printf("请输入要扫雷的行和列:\n");
			if (scanf("%d %d", &x, &y) != 2)
			{
				printf("输入错误,请输入数字!!!\n");
				while (getchar() != '\n');
				continue;
			}
			break;
		}
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] != '1')
			{
				if (show[x][y] == '*')
				{
					int a = need(mine, x, y);
					printf("%d\n", a);
					show[x][y] = a + '0';
					DisplayBoard(show, ROW, COL);
					amount++;
				}
				else
				{
					printf("已排查,请重新输入!!!\n");
				}
			}
			else
			{
				printf("很遗憾,你被炸死了!!!\n");
				break;
			}	
		}
		else
		{
			printf("输入错误,请重新输入!!!\n");
		}
	}
	if (amount == row * col - AMOUNT)
	{
		printf("恭喜你,扫雷成功!!!\n");
	}
}

 3.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 play()
{
	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);

	//扫雷
	FindBoard(mine, show, ROW, COL);

}

int main()
{
	srand((unsigned int)time(NULL));
	int input = 1;
	do
	{
		menu();
		printf("请选择->\n");
		if (scanf("%d", &input) != 1)
		{
			printf("输入错误,应该输入数字!!!\n");
			while (getchar() != '\n');
			continue;
		}
		switch (input)
		{
			case 1:
			{
				play();
				break;
			}
			case 2:
			{
				printf("退出游戏!\n");
				break;
			}
			default:
			{
				printf("输入错误,请重新输入!!!\n");
				break;
			}
		}
	} while (input);
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值