C语言编写扫雷游戏

C语言写扫雷游戏——扩展版

通过学习我们已经知道了如何写出一个单机版的扫雷游戏,这里对扫雷程序进行优化,主要有两个方面:1.实现第一次点击时不是雷;2.实现扫雷时的递归展开。
同之前的三子棋游戏一样,扫雷游戏我们依旧采用分文件形式书写,并继续加深对数组的认识。
扫雷规则如下:
1.数字是几就表示此数字位置邻接的八个方向有几个地雷
2.输入坐标
如果这个位置上的数字为1-8 即显示;
如果为数字为0(即空白) 自动延伸八个方向直到遇见数字(1-8)为止;
如果为地雷,游戏失败
4.当地图上的所有数字都点击完毕 游戏胜利
首先说一下对扫雷基本程序的编写:
我们同样利用一个数组来表示扫雷界面,扫雷中又多了一个数组来表示雷,并对它们初始化:

char mine_board[ROW][COL];	//雷的表
char show_board[ROW][COL];	//棋盘界面
memset(mine_board, '0', sizeof(mine_board));	//数组初始化
memset(show_board, '*', sizeof(show_board));	//数组初始化

main.c中写主体,在主函数中写出游戏的主体框架,主要是根据扫雷游戏的玩法来实现,并打印出菜单项;
game.h中写需要的库及几个函数的声明;
game.c中写各个函数的实现;
然后来说扩展的第一点,实现扫雷时输入的第一个坐标不是雷,
玩家输入第一个坐标时,如果是雷,首先确定是玩家第一次扫雷,因为布有20个雷(即NUM设为20),表格为100个,所以当times=80时就是第一次扫雷:

int times = 100 - NUM;

然后遍历周围的坐标,将周围每雷的位置与当前位置的值交换即可.:
需要在布雷函数中通过指针将找到的没雷坐标的值带出,

void SetMine(char board[][COL], int row, int col,int *x_p,int *y_p){...}
int i = 1;
for (; i <= 10; i++){
	int j = 1;
	for (; j <= 10; j++){
		if (board[i][j] == '0'){
			*x_p = i;
			*y_p = j;
			return;
		}
	}
}

在布雷时,传入没雷的两个参数:

int no_x;
int no_y;
SetMine(mine_board,ROW,COL,&no_x,&no_y);	//布雷

然后再将当前位置设为0(即没雷),并将找到的没雷的位置设为雷:

if (times == 80){
	mine_board[x][y] = '0';
	mine_board[no_x][no_y] = '1';
}

最后来说扫雷时递归展开的实现,主要是对函数GetCount的改编,使用递归方法遍历一个做表周围雷的个数,并对其展开,实现如下:

//使用递归遍历周围雷的个数
int GetCount(char board[][COL],char show[][COL], int x, int y)
{
	if (board[x][y] == '0')
	{
		int count = 0;
		if (board[x - 1][y] == '1')
			count++;
		if (board[x - 1][y - 1] == '1')
			count++;
		if (board[x][y - 1] == '1')
			count++;
		if (board[x + 1][y - 1] == '1')
			count++;
		if (board[x + 1][y] == '1')
			count++;
		if (board[x + 1][y + 1] == '1')
			count++;
		if (board[x][y + 1] == '1')
			count++;
		if (board[x - 1][y + 1] == '1')
			count++;
		show[x][y] = (count + '0');      //确定是字符数字
	}
	if (show[x][y] == '0')
	{
		if (show[x - 1][y] == '*')
			GetCount(board, show, x - 1, y);
		if (show[x - 1][y - 1] == '*')
			GetCount(board, show, x - 1, y - 1);
		if (show[x][y - 1] == '*')
			GetCount(board, show, x, y - 1);
		if (show[x + 1][y - 1] == '*')
			GetCount(board, show, x + 1, y - 1);
		if (show[x + 1][y] == '*')
			GetCount(board, show, x + 1, y);
		if (show[x + 1][y + 1] == '*')
			GetCount(board, show, x + 1, y + 1);
		if (show[x][y + 1] == '*')
			GetCount(board, show, x, y + 1);
		if (show[x - 1][y + 1] == '*')
			GetCount(board, show, x - 1, y + 1);
	}
	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] - 8 * '0';
}

然后在Game函数中判断当前坐标不为雷,且周围雷的个数为0时,调用GetCount函数,递归展开坐标周围区域,自动延伸八个方向直到遇见数字(1-8)为止,实现如下:

if (mine_board[x][y] == '0'){
		GetCount(mine_board, show_board, x, y);
		ShowBoard(mine_board, ROW, COL);
}

最终源代码实现如下:
main.c:

#include "game.h"

void Menu()
{
	printf("################################\n");
	printf("######  欢迎来到扫雷游戏  ######\n");
	printf("################################\n");
	printf("###### 1.play      2.exit ######\n");
	printf("################################\n");
	printf("Please Select:> ");
}
int main()
{
	int quit = 0;
	while (!quit){
		Menu();
		int select = 0;
		scanf("%d", &select);
		switch (select){
		case 1:
			Game();
			break;
		case 2:
			quit = 1;	//退出
			break;
		default:
			printf("你选择有误,重新输入!\n");
			break;
		}
	}
	printf("Bye Bye!\n");
	system("pause\n");
	return 0;
}

game.h

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <windows.h>

#pragma warning(disable:4996)

#define ROW 12
#define COL 12
#define NUM 20

//布雷
void SetMine(char board[][COL], int row, int col, int *x_p, int *y_p);
//显示棋盘
void ShowBoard(char board[][COL], int row, int col);
//统计周围雷的个数
int GetCount(char board[][COL], char show[][COL], int x, int y);
void Game();

#endif

game.c

#include "game.h"

//布雷
void SetMine(char board[][COL], int row, int col,int *x_p,int *y_p)
{
	int count = NUM;
	while (count > 0){
		int x = rand() % (row - 2) + 1;
		int y = rand() % (col - 2) + 1;
		//放雷
		if (board[x][y] == '0'){
			count--;
			board[x][y] = '1';
		}
	}
	int i = 1;
	for (; i <= 10; i++){
		int j = 1;
		for (; j <= 10; j++){
			if (board[i][j] == '0'){
				*x_p = i;
				*y_p = j;
				return;
			}
		}
	}
}
//显示棋盘界面
void ShowBoard(char board[][COL], int row, int col)
{
	printf("   ");
	int i = 1;
	for (; i <= 10; i++){
		printf(" %d  ", i);
	}
	printf("\n");
	for (i = 1; i <= 10; i++){
		printf("----");
	}
	printf("---\n");
	for (i = 1; i <= 10; i++){
		printf("%2d|", i);
		int j = 1;
		for (; j <= 10; j++){
			printf(" %c |", board[i][j]);
		}
		printf("\n");
		int k = 1;
		for (k = 1; k <= 10; k++){
			printf("----");
		}
		printf("---\n");
	}
}
//使用递归遍历周围雷的个数
int GetCount(char board[][COL],char show[][COL], int x, int y)
{
	if (board[x][y] == '0')
	{
		int count = 0;
		if (board[x - 1][y] == '1')
			count++;
		if (board[x - 1][y - 1] == '1')
			count++;
		if (board[x][y - 1] == '1')
			count++;
		if (board[x + 1][y - 1] == '1')
			count++;
		if (board[x + 1][y] == '1')
			count++;
		if (board[x + 1][y + 1] == '1')
			count++;
		if (board[x][y + 1] == '1')
			count++;
		if (board[x - 1][y + 1] == '1')
			count++;
		show[x][y] = (count + '0');      //确定是字符数字
	}
	if (show[x][y] == '0')
	{
		if (show[x - 1][y] == '*')
			GetCount(board, show, x - 1, y);
		if (show[x - 1][y - 1] == '*')
			GetCount(board, show, x - 1, y - 1);
		if (show[x][y - 1] == '*')
			GetCount(board, show, x, y - 1);
		if (show[x + 1][y - 1] == '*')
			GetCount(board, show, x + 1, y - 1);
		if (show[x + 1][y] == '*')
			GetCount(board, show, x + 1, y);
		if (show[x + 1][y + 1] == '*')
			GetCount(board, show, x + 1, y + 1);
		if (show[x][y + 1] == '*')
			GetCount(board, show, x, y + 1);
		if (show[x - 1][y + 1] == '*')
			GetCount(board, show, x - 1, y + 1);
	}
	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] - 8 * '0';
}

void Game()
{
	srand((unsigned long)time(NULL));	//埋随机种子
	char mine_board[ROW][COL];	//雷的表
	char show_board[ROW][COL];	//棋盘界面
	memset(mine_board, '0', sizeof(mine_board));	//数组初始化
	memset(show_board, '*', sizeof(show_board));	//数组初始化
	//SetMine(mine_board, ROW, COL);	//布雷

	int no_x;
	int no_y;
	SetMine(mine_board,ROW,COL,&no_x,&no_y);	//布雷

	int x = 0;
	int y = 0;
	int times = 100 - NUM;
	do{
		system("cls");
		ShowBoard(show_board, ROW, COL);	//显示棋盘界面
		printf("Please Enter<x,y>:");
		scanf("%d %d", &x, &y);
		//判断输入是否合法
		if (x<1 || x>10 || y<1 || y>10){
			printf("你输入的坐标有误,请重新输入!\n");
			continue;
		}
		//判断输入位置是否被排过
		if (show_board[x][y] != '*'){
			printf("该位置已被排除,请重新输入!\n");
			continue;
		}
		//检查是否有雷
		if (mine_board[x][y] == '1'){
			//判断第一次扫雷时
			if (times == 80){
				mine_board[x][y] = '0';
				mine_board[no_x][no_y] = '1';
			}
			else{
				printf("Game Over!\n");
				ShowBoard(mine_board, ROW, COL);
				break;
			}
		}
		if (mine_board[x][y] == '0'){
			GetCount(mine_board, show_board, x, y);
			ShowBoard(mine_board, ROW, COL);
		}

		int count = GetCount(mine_board,show_board, x, y);//统计周围雷的个数
		show_board[x][y] = count + '0';
		times--;
	} while (times > 0);
}

最后就是写完该游戏的总结了,具体实现在前面已经说了,收获点有几个:
1.认识到了一个初始化函数memset(),要在game.h中添加相应的库,可以通过Cplusplus.com手册中查看它的使用方法:

#include <string.h>
memset(mine_board, '0', sizeof(mine_board));	//数组初始化
memset(show_board, '*', sizeof(show_board));	//数组初始化

2.游戏的整个编写中都用到了二维数组的使用,继三子棋后继续加深对数组的认识;
3.埋随机种子,随机数的使用。
扫雷游戏还可以继续做更多的优化,比如闯关模式,雷的标记,MFC界面化实现等等,这些就需要我在多加学习之后再去实现了。
博客有什么问题希望大家可以在评论区提出。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值