【C语言步行梯】C语言实现扫雷游戏(含详细分析)

在这里插入图片描述

🎯每日努力一点点,技术进步看得见
🏠专栏介绍:【C语言步行梯】专栏用于介绍C语言相关内容,每篇文章将通过图片+代码片段+网络相关题目的方式编写,欢迎订阅~~


需求分析

以前Windows都会自带一款有趣的游戏,叫做扫雷。今天我们就一起使用C语言来实现一下这个经典的游戏。
在这里插入图片描述
下图演示的是一个9×9的盘面,其中含有10个雷。我们可以看到部分方格中含有数字,以图中红色数字2为例,它所在的9宫格已被涂成黄色。在它所在9宫格范围内有2个雷,故该位置有雷。
在这里插入图片描述
在开始的时候棋盘上已经埋藏好了雷,但玩家不知道雷的具体位置。在玩家点击不是雷的位置后,该位置将显示9宫格范围内的雷的数量。在玩家找出所有不是雷的位置后,玩家获胜。如果不慎点击到雷所在位置,则游戏结束。

举个例子吧!如下图,没有数字的位置是我们还未点击,且不知道是否有雷的区域。有数字的方格是已经点击,并且没有雷的位置。由于有数字的已经点击了,且保证其中没有雷。而1所在9宫格内必定有1个雷,故绿色箭头所指区域一定是雷。我们不能点击这个区域,否则游戏结束。

在这里插入图片描述

具体实现

主函数体

在主函数体内,我们需要调用一个menu函数,用它提醒用户:输入1开始游戏,输入0退出游戏。接收用户输入后,通过switch语句,执行game()函数或者退出游戏。menu和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");
		}
	}while(input);
	return 0;
}

菜单实现

首先,我们需要一个菜单,提示用户:输入1可以开始游戏,输入0会退出游戏。我通过封装一个menu函数实现↓↓↓

void menu()
{
	printf("**************************************************************\n");
	printf("******************* 1.play        0.exit *********************\n");
	printf("**************************************************************\n");
}

游戏实现

计算雷数量

由上面的需求可以知道,玩家在输入某个坐标后,如果该位置没有雷,则需要在该坐标位置显示其所在9宫格内雷的数量。

如下图所示,若用户输入(3,3)坐标时,我们需要统计该坐标所在9宫格内雷的数量。即将数组中的(mine[1][1]+mine[1][2]+mine[1][3]+mine[2][1]+mine[2][3]+mine[3][1]+mine[3][2]+mine[3][3])-'0'*8的数值计算出来即可。

★ps:这里的mine数组存储的字符,所以在8个坐标相加后,需要减’0’*8。如果用户输入的坐标是雷,则游戏结束,不需要统计雷的数量,所以这里不需要统计用户输入的坐标内是否有雷。
在这里插入图片描述
但如果玩家输入的坐标是(1,1)时,此时数组将会越界。为了保证数组不越界,我们将原本为9×9的盘面,使用11×11的数组来存储。(下图中,红色字符0,表示的是多开辟的空间)。
在这里插入图片描述
经过上面的分析后,我们可以写出如下计算雷的函数↓↓↓

int getMindCount(char board[ROWS][COLS], int x, int y)
{
	return (board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] + board[x][y - 1] + board[x][y + 1] + board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1]) - '0' * 8;
}

盘面设计

我们需要一个mine数组记录我们买下的雷的位置(如下图左侧所示)。为了不让用户看到雷的位置,我们需要另个数组,用于显示用户已经点击的位置,并将其展示给用户。
在这里插入图片描述
关于show数组,这里需要额外说明一下。假如用户输入坐标(1,1),此时需要判断mine[1][1]处是否有雷,如果有雷,则游戏结束;如果没有雷,且用户还有将所有没有雷的区域点击完,则游戏继续。在该区域没有雷的情况下,需要调用getMineCount(mine,1,1),计算该位置的雷数量,并将show[1][1]改为雷的数量。

初始化盘面

show数组显示给用户看前,均初始化为’*‘(星号)。而mine数组用于存储雷,在布置雷之前,应全部初始化为’0’(字符0表示没有雷,字符1表示有雷)。因此,我们需要实现一个初始化盘面的函数↓↓↓

//初始化盘面
void initBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}
显示盘面

在玩家输入坐标前,我们需要展示当前盘面状态(哪些已经点击过了,哪些还没有点击过),以方便用户输入坐标。下面我们实现一个展示盘面的函数↓↓↓

void displayBoard(char board[ROWS][COLS], int rows, int cols)
{
	for (int i = 1; i < rows - 1; i++)
	{
		if (i == 1)
		{
			for (int j = 0; j < cols - 1; j++)
			{
				if (j == 1)
					printf(" ");
				printf("%2d", j);
			}
			printf("\n");
		}
		printf("%2d ", i);
		for (int j = 1; j < cols - 1; j++)
		{
			printf( " %c", board[i][j]);
		}
		printf("\n");
	}
}

其展示效果如下:
在这里插入图片描述

布置雷

在游戏前,我们需要给mine数组设置雷。需要使用到随机数函数rand()。其实现代码如下↓↓↓

void setMine(char board[ROWS][COLS], int rows, int cols)
{
	int mine = MINE;
	while (mine)
	{
		int x = rand() % rows + 1;
		int y = rand() % cols + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			mine--;
		}
	}
}

排雷函数

我们需要一个函数,用于与玩家进行交互,接受玩家输入的坐标,检查该坐标是否有雷。如果该坐标没有雷,则检查所有非雷坐标是否均被点击,如果均被点击,则玩家获胜;如果未全部点击,则游戏继续。如果该坐标有雷,则玩家游戏失败。
在这里插入图片描述

void findMine(char mine[ROWS][COLS], char show[ROWS][COLS], int rows, int cols)
{
	int min = (rows - 2) * (cols - 2) - MINE;
	int x = 0;
	int y = 0;
	do
	{
		displayBoard(show, ROWS, COLS);
		printf("请输入排雷坐标>");
		scanf("%d %d", &x, &y);
		if (x >= 0 && x <= rows && y >= 0 && y <= cols)
		{
			if (mine[x][y] == '1')
			{
				printf("挑战失败,您被炸死了!\n");
				break;
			}
			else
			{
				int count = getMindCount(mine, x, y);
				show[x][y] = '0' + count;
				min--;
			}
		}
		else
		{
			printf("输入的坐标有误,请重新输入\n");
		}
	} while (min);
	if (min == 0)
		printf("恭喜你!排雷成功!\n");
}

游戏函数主体

在实现了上述各个函数后,我们只要稍加组织,就可以实现游戏了。

void game()
{
	char mine[ROWS][COLS];//存储雷
	char show[ROWS][COLS];//显示给用户看的

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

	//布置雷
	setMine(mine, ROW, COL);

	//开始排除雷
	findMine(mine, show, ROWS, COLS);
}

完整代码

上面代码中主函数中没有加入随机数种子,在下面代码中加入了srand((unsigned int)time(NULL)),同时对于上面出现ROW、COL的宏定义等也在下方代码整体给出。

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

#define ROW 9
#define COL 9
#define MINE 10

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

void menu()
{
	printf("**************************************************************\n");
	printf("******************* 1.play        0.exit *********************\n");
	printf("**************************************************************\n");
}

//初始化盘面
void initBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

//显示盘面
void displayBoard(char board[ROWS][COLS], int rows, int cols)
{
	for (int i = 1; i < rows - 1; i++)
	{
		if (i == 1)
		{
			for (int j = 0; j < cols - 1; j++)
			{
				if (j == 1)
					printf(" ");
				printf("%2d", j);
			}
			printf("\n");
		}
		printf("%2d ", i);
		for (int j = 1; j < cols - 1; j++)
		{
			printf( " %c", board[i][j]);
		}
		printf("\n");
	}
}

//埋雷
void setMine(char board[ROWS][COLS], int rows, int cols)
{
	int mine = MINE;
	while (mine)
	{
		int x = rand() % rows + 1;
		int y = rand() % cols + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			mine--;
		}
	}
}

//计算雷的数量
int getMindCount(char board[ROWS][COLS], int x, int y)
{
	return (board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] + board[x][y - 1] + board[x][y + 1] + board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1]) - '0' * 8;
}

//找雷
void findMine(char mine[ROWS][COLS], char show[ROWS][COLS], int rows, int cols)
{
	int min = (rows - 2) * (cols - 2) - MINE;
	int x = 0;
	int y = 0;
	do
	{
		displayBoard(show, ROWS, COLS);
		printf("请输入排雷坐标>");
		scanf("%d %d", &x, &y);
		if (x >= 0 && x <= rows && y >= 0 && y <= cols)
		{
			if (mine[x][y] == '1')
			{
				printf("挑战失败,您被炸死了!\n");
				break;
			}
			else
			{
				int count = getMindCount(mine, x, y);
				show[x][y] = '0' + count;
				min--;
			}
		}
		else
		{
			printf("输入的坐标有误,请重新输入\n");
		}
	} while (min);
	if (min == 0)
		printf("恭喜你!排雷成功!\n");
}

//游戏
void game()
{
	char mine[ROWS][COLS];//存储雷
	char show[ROWS][COLS];//显示给用户看的

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

	//布置雷
	setMine(mine, ROW, COL);

	//开始排除雷
	findMine(mine, show, ROWS, COLS);
}

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");
		}
	} while (input);
	return 0;
}

分文件编写

可以创建game.h保存头文件,及各函数的函数声明;game.c保存各个函数的具体实现;main.c中保存主函数。

game.h ↓↓↓

#pragma once

#define _CRT_SECURE_NO_WARNINGS 1

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

#define ROW 9
#define COL 9
#define MINE 10

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

//菜单
void menu();

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

//显示盘面
void displayBoard(char board[ROWS][COLS], int rows, int cols);

//埋雷
void setMine(char board[ROWS][COLS], int rows, int cols);

//计算雷的数量
int getMindCount(char board[ROWS][COLS], int x, int y);

//找雷
void findMine(char mine[ROWS][COLS], char show[ROWS][COLS], int rows, int cols);

//游戏
void game();

game.c ↓↓↓

#include "game.h"

void menu()
{
	printf("**************************************************************\n");
	printf("******************* 1.play        0.exit *********************\n");
	printf("**************************************************************\n");
}

//初始化盘面
void initBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

//显示盘面
void displayBoard(char board[ROWS][COLS], int rows, int cols)
{
	for (int i = 1; i < rows - 1; i++)
	{
		if (i == 1)
		{
			for (int j = 0; j < cols - 1; j++)
			{
				if (j == 1)
					printf(" ");
				printf("%2d", j);
			}
			printf("\n");
		}
		printf("%2d ", i);
		for (int j = 1; j < cols - 1; j++)
		{
			printf( " %c", board[i][j]);
		}
		printf("\n");
	}
}

//埋雷
void setMine(char board[ROWS][COLS], int rows, int cols)
{
	int mine = MINE;
	while (mine)
	{
		int x = rand() % rows + 1;
		int y = rand() % cols + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			mine--;
		}
	}
}

//计算雷的数量
int getMindCount(char board[ROWS][COLS], int x, int y)
{
	return (board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] + board[x][y - 1] + board[x][y + 1] + board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1]) - '0' * 8;
}

//找雷
void findMine(char mine[ROWS][COLS], char show[ROWS][COLS], int rows, int cols)
{
	int min = (rows - 2) * (cols - 2) - MINE;
	int x = 0;
	int y = 0;
	do
	{
		displayBoard(show, ROWS, COLS);
		printf("请输入排雷坐标>");
		scanf("%d %d", &x, &y);
		if (x >= 0 && x <= rows && y >= 0 && y <= cols)
		{
			if (mine[x][y] == '1')
			{
				printf("挑战失败,您被炸死了!\n");
				break;
			}
			else
			{
				int count = getMindCount(mine, x, y);
				show[x][y] = '0' + count;
				min--;
			}
		}
		else
		{
			printf("输入的坐标有误,请重新输入\n");
		}
	} while (min);
	if (min == 0)
		printf("恭喜你!排雷成功!\n");
}

//游戏
void game()
{
	char mine[ROWS][COLS];//存储雷
	char show[ROWS][COLS];//显示给用户看的

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

	//布置雷
	setMine(mine, ROW, COL);

	//开始排除雷
	findMine(mine, show, ROWS, COLS);
}

main.c ↓↓↓

#include "game.h"

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

🚩这篇文章结束了~~
如果文章中出现了错误,欢迎私信或留言。(๑•̀ㅂ•́)و✧
有任何疑问请评论或私信哦~~o( ̄▽ ̄)ブ

  • 27
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值