【C语言】8步实现扫雷游戏

       你有过小时候在学校的计算机课总会偷偷打开电脑上的单机游戏“扫雷”,然后一玩就是一节课的经历吗?那么在学过C语言之后我们有没有办法自己做出一个“扫雷”游戏呢?答案是肯定的,接下来,教你用8步很容易得创造一个扫雷游戏。

实现思路:

首先我们要先创建一个9*9的一个棋盘用来存放雷区块和空区块的信息,在这里我们使用字符'0'表示空区块,用字符'1'表示雷区块。此时,我们很容易会想到用一个二维数组来存放这些信息。如下图:

另外,我们还需要再创建一个9*9的表格用来存放排雷的信息,也就是一个区块周围的8个区块有几个雷。假如所排除的区块旁边有2个雷,那么这个区块所显示的就是字符'2'。但是,如果所排除的区块是一个边缘的区块,那么周围所统计的就不再是8个区块了。例如如果我们所排除的区块是第9行第9个区块,那么周围就只有3个雷需要统计。如下图:

那么,怎么解决这个问题呢?我们可以再在9*9的二维数组外面再加一圈区块,这样,我们无论排除的是哪一个区块,统计周围的区块都会是8个。如下图:

这样,我们就需要作2个11*11的棋盘,一个用来存放雷,我们将其命名为mine数组,另一个用来存放雷信息的数组,我们将其命名为show数组。

思路明确后,该我们用双手来实践了(这里使用的是VS2022版本来演示)

实现步骤:

1.为了提高代码的清晰度和为了使我们的思路更加地清晰,我们采用分文件的方式来编写代码。①test.c文件,用来编写游戏的测试逻辑②game.c文件 ,用来编写游戏中函数的实现③game.h文件,用来编写游戏中所需要的数据类型和函数声明。创建好是如下界面

2.我们开始打印游戏的初始界面,包含在test.c文件里,文件里有包含3个函数,分别是menu函数,用来打印游戏的选项页面,game函数,用来执行游戏(此时我们先不在game函数里面编写代码)以及main函数,用来控制函数的进出,相当于游戏的进出的通道。详细代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
	printf("************************\n");
	printf("*******  1.play  *******\n");
	printf("*******  0.exit  *******\n");
	printf("************************\n");
}
void game()
{
	
}
int main()
{
	int input = 0;
	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;
}

3.我们开始编写游戏的执行代码,首先,我们在test.c文件game函数中写出函数的流程。①.先创建出2个字符类型的二维数组,目的是为了存放mine数组和show数组。②.用initboard函数将2个数组初始化,mine数组初始化的值全部为字符'0',show数组初始化的值全部为字符'*'。③.用setmine函数来打印出show函数来(这里也可以同时把mine打印出来看看效果,后期直接注释掉就可以了)。④.用getmine函数在mine数组里面随机布置几个雷。⑤.用findmine函数来进行最后的排雷游戏。(这里为了方便我们后期想要改变数字较为轻松的话,实参全部使用常量来表示,全部都在game.h文件里面定义)

void game()
{
	char mine[ROWS][COLS] = { 0 };//存放布置好的雷
	char show[ROWS][COLS] = { 0 };//存放排查出雷的信息
	//初始化棋盘
	//1.mine数组最开始全是'0'
	//2.show数组最开始全是'*'
	initboard(mine, ROWS, COLS, '0');
	initboard(show, ROWS, COLS, '*');
	//打印棋盘
	displayboard(mine, ROW, COL);
	displayboard(show, ROW, COL);
	//1. 布置雷
	setmine(mine, ROW, COL);
	displayboard(mine, ROW, COL);
	//2. 排查雷
	findmine(mine, show, ROW, COL);
}

4.我们将这四个函数在game.c文件中编写出来,我们先来说initboard函数,对于这个函数相对简单,需要注意的是,我们在初始化2个数组时,可以多加一组字符变量set,这样可以直接了当的将两个数组区分开来,具体代码如下:

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

5.接着来讲displayboard函数,这个函数可以打印数组,注意点是要在每一行以及每一列都加上行号以及列号。具体代码如下:

void displayboard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("--------扫雷游戏--------\n");
	for (i = 0;i <= col;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");
	}
}

6.然后就是getmine函数,这个函数需要我们随机在9*9的棋盘上面布置10个雷。既然是随机的,那就需要我们引入随机数这个概念了。

C语⾔提供了⼀个函数叫 rand,需要包含⼀个头⽂件是:#include<stdlib.h>。这函数是可以⽣成随机数的rand函数的使⽤,rand函数会返回⼀个伪随机数,这个随机数的范围是在0~32767之间的。我们来用代码演示一遍。随机生成几个随机数:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	printf("%d\n", rand());
	printf("%d\n", rand());
	printf("%d\n", rand());
	printf("%d\n", rand());
	printf("%d\n", rand());
	printf("%d\n", rand());
	printf("%d\n", rand());
	printf("%d\n", rand());
	return 0;
}

但是,当我们多次尝试过后会发现每一次生成的“随机数”都是相同的,这是因为rand函数返回的是一个伪随机数。那么就不能保证我们每一次生成的随机数是一个不同的数字。

这时,我们就需要再次引入一个srand函数。srand函数⽤来初始化随机数的⽣成器的,程序中在调⽤ rand 函数之前先调⽤ srand 函数,通过 srand 函数的参数来设置rand函数⽣成随机数的时候的“种子”,只要“种子”变化,每次⽣成的随机数序列也就变化起来了。

相信玩过《我的世界》的朋友对“种子”这个概念并不陌生,只要种子不一样,生成的地图也就不一样。那么,我们只要找到一个不断变化的数字,就可以生成不同的“种子”。

这时,我们需要引入一个time函数的概念:在C语⾔中有⼀个函数叫 time ,需要包含的一个头文件是:#include<time.h>,time 函数会返回当前的⽇历时间,其实返回的是1970年1⽉1⽇0时0分0秒到现在程序运⾏时间之间的差值,单位是秒。这段时间差通常也被叫做:时间戳,时间戳用time(NULL)来表示。让我们结合time,strand,rand来生成几个个随机数,代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
	srand((unsigned int)time(NULL));
	//seand中包含的是种子,种子不同生成的随机数不同
	//srand中所包含的类型是unsigned int,(unsigned int)表示强制类型转换
	//time(NULL)表示时间戳
	printf("%d\n", rand());
	printf("%d\n", rand());
	printf("%d\n", rand());
	printf("%d\n", rand());
	printf("%d\n", rand());
	return 0;
}

随机数的概念在我另一篇文章也有详细的说明,叫三步实现猜数字游戏,有兴趣的朋友可以去看一下。【C语言】3步实现猜数字游戏-CSDN博客

然后,我们开始编写setmine函数,具体代码如下:

void  setmine(char board[ROWS][COLS], int row, int col)
{
	int count = COUNT;
	while (count)
	{
		int i = rand() % row + 1;//范围是1-9
		int j = rand() % col + 1;
		if (board[i][j] == '0')
		{
			board[i][j] = '1';
			count--;
		}
	}
}

7.我们开始编写扫雷游戏的进行代码findmine函数。这个函数,我们采用多个选择语句来运行,首先排除掉错误的坐标,在进行排雷。若排到的第一个就是雷,那么直接break退出本局游戏。若排到的是空区块,则这个空区块需要表明周围雷的信息,那么我们怎么统计空区块周围雷的信息呢?这时候,我们需要借助另一个函数赖统计空区块周围雷的信息。

这时候需要我们在findmine函数前面添加一个整数类型的函数getminecount函数,用来统计所排除区块周围有几个雷。循序渐进,我们首先来统计所排区块周围一个区块的信息。根据前文,我们知道,在棋盘上的“雷区”我们用的都是字符'1'来表示的,那么,如果我们用1的ASCII码值减去0的ASCII码值,就可以统计出其中一个区块雷的数量。我们设所排除雷区块的坐标是(x,y),然后依次再将周围8个区块的信息依次统计出来:

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

最后,我们开始编写最后的findmine函数:

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 - COUNT)
	{
		printf("请输入你要排雷的坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了");
				displayboard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = getminecount(mine, x, y);
				show[x][y] = count + '0';
				displayboard(show, ROW, COL	);
				win++;
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	}
	if (win == row * col - COUNT)
	{
		printf("恭喜你,排雷成功\n");
		displayboard(mine, ROW, COL);
	}
}

8.将这些全部完成之后,我们需要在game.h文件中将在game.c文件中的函数全部声明一遍。

实现结果:

让我们来看一看最后的整体代码:

game.h:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define 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 board[ROWS][COLS], int row, int col);

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

game.c:

#define _CRT_SECURE_NO_WARNINGS 1
#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 <= col;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");
	}
}
void  setmine(char board[ROWS][COLS], int row, int col)
{
	int count = COUNT;
	while (count)
	{
		int i = rand() % row + 1;//范围是1-9
		int j = rand() % col + 1;
		if (board[i][j] == '0')
		{
			board[i][j] = '1';
			count--;
		}
	}
}
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 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 - COUNT)
	{
		printf("请输入你要排雷的坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了");
				displayboard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = getminecount(mine, x, y);
				show[x][y] = count + '0';
				displayboard(show, ROW, COL	);
				win++;
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	}
	if (win == row * col - COUNT)
	{
		printf("恭喜你,排雷成功\n");
		displayboard(mine, ROW, COL);
	}
}

test.c:

#define _CRT_SECURE_NO_WARNINGS 1
#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 };//存放排查出雷的信息
	//初始化棋盘
	//1.mine数组最开始全是'0'
	//2.show数组最开始全是'*'
	initboard(mine, ROWS, COLS, '0');
	initboard(show, ROWS, COLS, '*');
	//打印棋盘
	//displayboard(mine, ROW, COL);
	displayboard(show, ROW, COL);
	//1. 布置雷
	setmine(mine, ROW, COL);
	//displayboard(mine, ROW, COL);
	//2. 排查雷
	findmine(mine, show, ROW, COL);
}
void test()
{
	int input = 0;
	srand((unsigned int)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;
}
  • 27
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值