【C语言】扫雷小游戏的实现

一、 设计思路

       思路分析

       如果所选位置不是雷,则计算改位置附近八个坐标雷的个数,并记录在该位置,游戏继续;是雷,则被炸死,游戏结束。

       若不是雷的位置都被找出来,则成功,游戏结束。

       在游戏设计初步,大家会想着设置一个棋盘来进行操作,用‘1’来表示雷,‘0’表示非雷。但这样会出现一个问题,就是不知道棋盘中的‘1’是设置的雷还是附近八个坐标某处有雷,因此我们需要设置两个棋盘(即两个二维数组)来进行操纵。一个棋盘用于存放雷的信息,另一个用于存放排查的雷的信息。

     

      本文设计一个小型的扫雷游戏,展示一个9*9的棋盘,那就应该在代码设计时直接设置行数和列数分别为9吗?

      答案是不是的!在玩家选择坐标后若该位置不是雷还应统计该坐标周围的8个坐标的雷数记录在另一张棋盘上。如果是排查下图中绿色位置周围的雷数不会出问题,但如果排查粉色位置处时会有三个坐标出现在棋盘外,对于判断时会造成麻烦,所以为了便于排雷就将棋盘设置为11*11的二维数组,同时为了便于在另一个棋盘上对应位置显示出来雷的数目,将两个数组均设置为11*11的二维数组。

                                    

           如下:

                                   

基本逻辑框架

菜单:

通过此函数选择开始游戏,结束游戏,输入数据非法提示重输

初始化棋盘:

初始化两个棋盘mine和show,将内部元素设为便于区分和使用的字符

展示棋盘:

未选择的位置均为‘*’,已选择的位置处所置数据为周围的雷数

置雷:

通过 rand()函数随机生成一定坐标,将该位置记为雷

排雷:

用户输入想排查的位置,查看mine棋盘中对应位置是否为雷,若是则被炸死;若不是则显示周围的雷数,直至找出所有的雷数

二、函数功能实现

      创建三个文件,源文件 test.c,game.c 及头文件 game.h。

      game.h 中用来集中存放程序所需的头文件以及声明我们所需用到的函数

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.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 c);
//打印棋盘
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);


      <在头函数中定义常量ROW COL ROWS COLS,方便以后修改>
      game.c来集中创建game ( )中所需的函数
      
      游戏的主体逻辑写在test.c中
      要记得在 test.c 以及 game.c 中包含头文件 game.h ,写为 #include"game-a.h"

各类步骤

构建主逻辑框架

void game()
{
	char mine[ROWS][COLS] = { 0 };  //用于保存雷的位置
	char show[ROWS][COLS] = { 0 };  //用于展示出给用户操作
	//初始化棋盘  
	InitBoard(mine,ROWS,COLS,'0');    //'0'表示无雷  ‘1’表示为雷
	InitBoard(show, ROWS, COLS, '*');
	//打印棋盘
	DisplayBoard(show, ROW, COL);
	//置雷
	SetPlay(mine, ROW, COL);
	DisplayBoard(mine, 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 0:
			printf("退出游戏!\n");
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;
		}
	} while (input);
	return 0;
}

由于我们在玩游戏时不一定是玩一局就结束,这时我们就需要在game()外部加一层do-while()循环。

菜单函数

显示相应的功能

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

初始化棋盘函数

先定义两个字符型的二维数组,初始化函数传参时将要给棋盘赋的值也传过去,这样就不用写两个初始化函数了

    char mine[ROWS][COLS] = { 0 };  //用于保存雷的位置
	char show[ROWS][COLS] = { 0 };  //用于展示出给用户操作
	//初始化棋盘  
	InitBoard(mine,ROWS,COLS,'0');    //'0'表示无雷  ‘1’表示为雷
	InitBoard(show, ROWS, COLS, '*');
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char c)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
		for (j = 0; j < cols; j++)
			board[i][j] = c;
}

打印棋盘函数

玩家每选一个坐标就打印一次show[ROWS][COLS]数组

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

      因为我们为了便于排雷所以在设置数组时多加了两行两列, 但是对于玩家而言只需要打印出中间9*9的棋盘,所以在嵌套的for循环中i,j都是从1开始的。打印完一行后还要换行,因此在第一层for循环快结束时加上printf("\n");语句。为了更加便于使用再加上一个for循环打印出列号。

置雷函数

//置雷
void SetPlay(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while(count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}

用 rand()函数随机生成坐标( x , y ) 并将该位置置为雷,因为 rand()生成的是随机数,但坐标是有范围的,所以给 rand()%row将生成一个0 ~ row - 1的数,因此 x = rand () % row + 1,同理 y = rand()% y + 1。不仅如此,还要确保每次生成的坐标处不是雷,否则会造成棋盘上雷的数目小于EASY_COUNT。

注意:使用 rand()函数时要在主函数中加上 srand ( ( unsigned int ) time ( NULL ) ) ;

排雷函数

  排雷需要同时操作两个数组,所以在参数部分应将两个数组都传过去。

FindMine(mine, show, ROW, COL);

 玩家输入坐标后,应先判断位置是否合法,然后再做选择

       if (x>=1 && x<=row && y>=1 && y<=col)  //坐标合法
		

如果坐标不合法,提示玩家坐标错误,重新输入,因此在出错后不能直接结束,而是加上一个 while()循环,直至找出所有雷或者被雷炸死就结束。那么非雷的位置就有ROW*COL-EAST_COUNT个,实现代码如下:

while (count<row*col-EASY_COUNT)
	{
		printf("请输入坐标-> ");
		scanf("%d %d", &x, &y);
		if (x>=1 && x<=row && y>=1 && y<=col)  //坐标合法
		{
			if (mine[x][y] == '1') //该坐标处是雷
			{
				printf("被炸死,游戏结束!\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else  //不是雷,计算周围8个坐标有几个雷
			{
				int c = GetMineCount(mine,x,y);
				show[x][y] = c + '0';
				DisplayBoard(show, ROW, COL);
				count++;
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}

同时还需要一个计算周围雷数量的函数 GetMineCount(),将周围八个坐标处的值相加后减去8*'0'所得值就为雷的数目。

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

三、源代码

game.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.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 c);
//打印棋盘
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 c)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
		for (j = 0; j < cols; j++)
			board[i][j] = c;
}
//打印棋盘
DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("--------扫雷--------\n");
	for (i = 0; i <= col; i++)   //打印列号
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);    //打印行号
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("--------扫雷--------\n");
}
//置雷
void SetPlay(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while(count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}
//排雷
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + 
		mine[x][y - 1] + mine[x][y + 1] + 
		mine[x + 1][y - 1] + mine[x + 1][y] + 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 count = 0;
	while (count<row*col-EASY_COUNT)
	{
		printf("请输入坐标-> ");
		scanf("%d %d", &x, &y);
		if (x>=1 && x<=row && y>=1 && y<=col)  //坐标合法
		{
			if (mine[x][y] == '1') //该坐标处是雷
			{
				printf("被炸死,游戏结束!\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else  //不是雷,计算周围8个坐标有几个雷
			{
				int c = GetMineCount(mine,x,y);
				show[x][y] = c + '0';
				DisplayBoard(show, ROW, COL);
				count++;
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}
	if (count == row * col - EASY_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 };  //用于展示出给用户操作
	//初始化棋盘  
	InitBoard(mine,ROWS,COLS,'0');    //'0'表示无雷  ‘1’表示为雷
	InitBoard(show, ROWS, COLS, '*');
	//打印棋盘
	DisplayBoard(show, ROW, COL);
	//置雷
	SetPlay(mine, 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 0:
			printf("退出游戏!\n");
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;
		}
	} while (input);
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

今天学习了吗•

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值