C语言“黑框框”下简易扫雷实现

初步了解数组、指针后,实现了一个win32控制台下(黑框框)简易版扫雷程序.


程序中 行数列数雷数可自行定义。


程序用*代表位置未知,空格代表此位置周围无雷,数字代表周围雷个数。


分三个代码共同编译实现,SaoLei.h包含程序所需头文件,宏定义及所用函数声明。SaoLei.c为游戏逻辑代码。Function.c 为自定义函数实现代码。


程序用两个char数组分别表示两块地图,一副地图呈现给玩家,一幅地图用于实现布雷及雷数更新等。两个数组在程序中会时时交互。


程序拥有坐标合法性检查函数,保证输入不合法坐标及重复坐标时程序可以正常运行。


游戏简易思路: 创建两个char数组分别代表两个地图, 一个地图为游戏地图呈现给玩家,一个地图用来布雷(用rand()函数初始化雷坐标)。考虑到需要检测边界处周围雷个数,因此布雷数组比游戏数组多两行两列。 限于win32 平台下 ,因此用输入坐标形式进行交互(输入坐标与数组坐标不同,在程序中有注释)。 玩家每输入一个坐标, 不同函数中两个数组进行传参交互,判断输赢,坐标是否合法,显示坐标周围雷个数, 更新数组, 重新打印地图等。 


程序实现后并未进行优化,所以可能存在下许问题:代码或许会有些冗杂,函数或许未很好的封装导致重复,或本为一个函数模块却分多个函数实现等。尽请见谅。


程序中有详尽注释。


源码如下:

SaoLei.h:

/*
此自定义头文件包含SaoLei.c与Function.c所需头文件及函数原型声明
*/


#ifndef __SaoLei_h__
#define __SaoLei_h__


//程序用到标准输入输出函数
#include <stdio.h>	

/*
程序随机产生雷坐标
*/
#include <stdlib.h>
#include <time.h>


#define ROWS 11									        //定义数组行数(注意! 实际显示给用户的地图行数为ROWS - 2)
#define COLS 11									        //定义数组列数(注意! 实际显示给用户的地图列数为COLS - 2)
#define NUM  10										//定义雷个数

//打印游戏选择菜单
void menu(void);

//初始化两块地图
void init_map(char gamemap[][COLS - 2], char map[][COLS], int rows);			//此处传来的参数rows为map的行数,gamemap的行数应为rows - 2

//打印最新游戏地图
void print_gamemap(char gamemap[][COLS - 2], int rows);					//此处传来的参数rows为gamemap的行数ROWS - 2

//进行游戏
void playgame(char gamemap[][COLS - 2], char map[][COLS], int rows);			//此处传来的参数rows为map的行数,gamemap的行数应为rows - 2

//检测玩家输入坐标是否合法
int check(char gamemap[][COLS - 2], int rows, int x, int y);				//此处传来的参数rows为gamemap的行数ROWS - 2 x y 为玩家输入坐标

//判断雷是否已排完
int count(char gamemap[][COLS - 2], int rows, int num);					//此处传来的参数rows为gamemap的行数ROWS - 2 num为已定义的雷个数

//判断输入坐标身边雷情况
int pailei(char map[][COLS], int x, int y);						//返回-1则此坐标为雷,返回0-8代表此坐标身边雷个数
								                        //参数x y 为玩家输入坐标

//更新gamemap
void gxgamemap(char gamemap[][COLS - 2], int x, int y, int ret);		        //x, y为输入坐标, ret为输入坐标身边雷个数

//打印map地图(供测试用)
void print_map(char map[][COLS], int rows);						//此处传来的参数rows为map的行数ROWS	


#endif

SaoLei.c:

/*
扫雷游戏逻辑
*/


#include "SaoLei.h"

int main(void)
{

	int input;								//用来接受用户输入

	char gamemap[ROWS - 2][COLS - 2];					//gamemap用来向用户展示的地图
	char map[ROWS][COLS];							//map    用来显示雷位置地图,不向用户展示

	menu();									//打印游戏选择菜单

	scanf("%d", &input);				    <span style="white-space:pre">			</span>//接收用户输入 1为进行游戏 0为退出游戏

	while( input )								//对用户输入进行判断
	{
		
		init_map(gamemap, map, ROWS);					/*初始化两块地图  
										此处传过去的参数ROWS为map的行数,gamemap的行数应为ROWS - 2*/

		print_gamemap(gamemap, ROWS - 2);				//打印最新游戏地图
		//print_map(map, ROWS);						//打印map(用于测试)

		playgame(gamemap, map, ROWS);					//进行游戏

		menu();								//再次打印游戏菜单

		scanf("%d", &input);				<span style="white-space:pre">		</span>//再次接收用户输入 1为进行游戏 0为退出游戏							<span style="white-space:pre">	</span>

	}//游戏结束

	printf("\nBye!\n");

	return 0;								//程序结束

}


Function.c:


/*
SaoLei.c中函数实现
*/


#include "SaoLei.h"


//打印游戏菜单
void menu(void)
{
	printf("********************\n");
	printf("****** 1.play ******\n");
	printf("****** 0.exit ******\n");
	printf("********************\n");
}

//初始化两块地图
void init_map(char gamemap[][COLS - 2], char map[][COLS], int rows)		//此处传来的参数rows为map的行数,gamemap的行数应为rows - 2
{

	int r;
	int c;
	int x;								        //保存随机产生雷行标
	int y;									//保存随机产生雷列标
	int count = 0;								//计算雷个数 已定义NUM为雷个数

	srand((unsigned)time(NULL));

	for( r = 0; r < rows - 2; r++ )					<span style="white-space:pre">	</span>//初始化gamemap地图,每个位置设为'*'表示此位置未知
		for( c = 0; c < COLS - 2; c++ )
			gamemap[r][c] = '*';

	for( r = 0; r < rows; r++ )						//初始化没有雷的map棋盘,每个位置设为'0'表示无雷
		for( c = 0; c < COLS; c++ )
			map[r][c] = '0';

	for( count = 0; count < NUM; )				<span style="white-space:pre">	</span>        //对map地图进行布雷,位置为'1'表示有雷
	{		
		/*
		map中首行首列末行末列不放雷,所以x y坐标有限制
		*/
		x = rand() % 9 + 1;						//随机产生雷行标 1 - 9
		y = rand() % 9 + 1;						//随机产生雷列标 1 - 9

		if( map[x][y] != '1' )	
		{
			/*
			如果此坐标未被设置雷, 则将count++, 并将此位置坐标设为雷('1')
			*/
			map[x][y] = '1';
			count++;
		}

	}//退出for循环

}//init_map函数结束


//打印最新游戏地图
void print_gamemap(char gamemap[][COLS - 2], int rows)				//此处传来的参数rows为gamemap的行数,ROWS - 2
{

	int i;
	int r;
	int c;

	/*
	打印行标号与列标号
	*/
	putchar(' ');

	for( i = 0; i < COLS - 2; i++ )
		printf("%d ", i + 1);

	putchar('\n');
	
	/*
	打印gamemap
	*/
	for( r = 0; r < rows; r++ )
	{
		printf("%d", r + 1);

		for( c = 0; c < COLS - 2; c++ )
			printf("%c ", gamemap[r][c]);
	
		printf("\n");
	}

}

//打印map地图(供测试用)
void print_map(char map[][COLS], int rows)					//此处传来的参数rows为map的行数ROWS	
{

	int i;
	int r;
	int	c;

	putchar('\n');
	printf(" ");

	for( i = 0; i < ROWS; i++ )
		printf("%d ", i + 1);

	putchar('\n');

	for( r = 0; r < rows; r++ )
	{	
		printf("%d", r + 1);

		for( c = 0; c < COLS; c++ )
			printf("%c ", map[r][c]);

		putchar('\n');
	}

}

//检测玩家输入坐标是否合法
int check(char gamemap[][COLS - 2], int rows, int x, int y)			//此处传来的参数rows为gamemap的行数ROWS - 2. x y 为玩家输入行列坐标
{
	if( ((x >= 1) && (x <= 9)) && ((y >= 1) && (y <= 9)) )			//x y范围是否满足
		if( gamemap[x - 1][y - 1] == '*' )				//此坐标是否已被使用过
			return 0;

	return 1;								//返回0输入坐标合法, 返回1输入坐标不合法!
}

//判断雷是否已排完
int count(char gamemap[][COLS - 2], int rows, int num)				//此处传来的参数rows为gamemap的行数ROWS - 2 num为已定义的雷个数
{
	int r;
	int c;
	int n = 0;								//目前雷数目
	
	for( r = 0; r < rows; r++ )
		for( c = 0; c < COLS - 2; c++ )
			if( gamemap[r][c] == '*')
				n++;														

	if( n == num )
		return 0;															

	return 1;								//返回0 则雷已排完 返回 1 则雷未排完 
}

/*
判断输入坐标身边雷情况
返回-1则此坐标为雷,返回0-8代表此坐标身边雷个数
参数 x y 为玩家输入坐标
'1'代表雷
*/
int pailei(char map[][COLS], int x, int y)
{
	int ret = 0;								//保存雷个数

	if( map[x][y] == '1' )							//此坐标为雷,返回-1
		return -1;
	
	/*
	计算雷个数,并返回值
	*/

	if( map[x][y + 1] == '1')
		ret++;

	if( map[x + 1][y + 1] == '1')
		ret++;

	if( map[x - 1][y + 1] == '1')
		ret++;

	if( map[x][y - 1] == '1')
		ret++;

	if( map[x + 1][y - 1] == '1')
		ret++;
	
	if( map[x - 1][y - 1] == '1')
		ret++;

	if( map[x + 1][y] == '1')
		ret++;

	if( map[x - 1][y] == '1')
		ret++;

	return ret;
}

//更新gamemap
void gxgamemap(char gamemap[][COLS - 2], int x, int y, int ret)			//ret为输入坐标身边雷个数, x y为输入坐标
{

	while( 1 )
	{
		if( ret == 0 )
		{
			gamemap[x - 1][y - 1] = ' ';
			
			break;
		}
		
		gamemap[x - 1][y - 1] = '0' + ret;

		break;
	}

}



//进行游戏
void playgame(char gamemap[][COLS - 2], char map[][COLS], int rows)		//此处传来的参数rows为map的行数,gamemap的行数应为rows - 2
{
	int x;									//
	int y;									//存放用户输入坐标
	int ret;								//存放返回数值,用来说明用户输入的坐标处情况

	while( count(gamemap, ROWS - 2, NUM) )				        //返回0则雷已排完,游戏胜利,返回1则雷未排完. 																	
	{

		printf("请输入坐标:");
		scanf("%d %d", &x, &y);

		while( check(gamemap, ROWS - 2, x, y) )			        //检测玩家输入坐标是否合法
		{
			printf("坐标不合法!\n请重新输入坐标:");
			scanf("%d %d", &x, &y);
		}

		ret = pailei(map, x, y);					//返回-1则此坐标为雷,返回0-8代表此坐标身边雷个数

		if( ret == -1 )							//游戏失败
		{
			printf("呵呵!\n");
			
			break;
		}
		
		gxgamemap(gamemap, x, y, ret);					//根据返回值更新gamemap, x y 为输入的坐标
		
		print_gamemap(gamemap, ROWS - 2);			        //打印最新游戏地图
		//print_map(map, ROWS);						//打印map地图(用于测试)

	}//退出最外层while循环

	if( (count(gamemap, ROWS - 2, NUM)) == 0 )				//判断是胜利后跳出while循环还是失败后跳出,若是胜利,函数返回值为0
		printf("你赢了!\n");
}//退出playgame函数








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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值