短期项目:扫雷——C语言实现

2019/01/10

做出扫雷的基本框架,并且修复了一些bug。

效果预览

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

模块信息

——随机雷数——
B代表总雷数,通过rand()函数生成随机数,分别对行数和列数取模,存入坐标数组中,再与之前存入雷的坐标遍历对比,如果遇到重复的坐标,则重新选取随机数。
待解决问题:B过大,会耗费比较多时间去掉重复坐标。

void _Bomb()//生成地雷
{
	int i,j,r;
	for(i=0;i<B;++i)
	{
		srand(time(NULL));
		mark:r=rand();
		bomb[0][i]=rand()%N;
		bomb[1][i]=rand()%M;
		for(j=0;j<i;++j)
			if(bomb[0][j]==bomb[0][i]&&bomb[1][j]==bomb[1][i])
				goto mark;
	}
	return;
}

——地图初始化——
首先把雷的坐标数组中的坐标标记入map1,然后对该坐标附近八个位置进行遍历,如果不是雷,数值加1。

void _Mark()//构造地图
{
	int i,j;
	int x,y;
	for(i=0;i<B;++i)
	{
		map1[bomb[0][i]][bomb[1][i]]='@';
		for(j=0;j<8;++j)
		{
			x=bomb[0][i]+dx[j];
			y=bomb[1][i]+dy[j];
			if(x>=0&&x<N&&y>=0&&y<M&&map1[x][y]!='@')
				map1[x][y]+=1;
		}
	}
	return;
}

——假如翻出0——
根据扫雷的规则,翻出0,则和它相邻的0均被翻出,直到遇到其它数值(该数值也被翻开)。
此处用一个简单的DFS实现。

void _dfs(int x,int y)//显示'0' 
{
	int i,X,Y;
	for(i=0;i<8;++i)
	{
		X=dx2[i]+x;
		Y=dy2[i]+y;
		if(X>=0&&X<N&&Y>=0&&Y<M&&map1[X][Y]!='0'&&map2[X][Y]!=1)
		{
			map2[X][Y]=map1[X][Y];
			map3[X][Y]=1;
		}
	}
	for(i=0;i<4;++i)
	{
		X=dx2[i]+x;
		Y=dy2[i]+y;
		if(X>=0&&X<N&&Y>=0&&Y<M&&map1[X][Y]=='0'&&map3[X][Y]!=1)
		{
			map2[X][Y]='0';
			map3[X][Y]=1;
			--total;
			_dfs(X,Y);
		}
	}
	return;		
}

——输出函数——
对各个数值和区块上色。

void _cloprint(char c)
{	
	HANDLE op;
	op=GetStdHandle(STD_OUTPUT_HANDLE);
	switch(c)
	{
		case '0':SetConsoleTextAttribute(op,247);printf("  ");break;
		case '1':SetConsoleTextAttribute(op,242);printf("%c ",c);break;
		case '2':SetConsoleTextAttribute(op,243);printf("%c ",c);break;
		case '3':SetConsoleTextAttribute(op,244);printf("%c ",c);break;
		case '4':SetConsoleTextAttribute(op,245);printf("%c ",c);break;
		case '5':SetConsoleTextAttribute(op,246);printf("%c ",c);break;
		case '6':SetConsoleTextAttribute(op,248);printf("%c ",c);break;
		case '7':SetConsoleTextAttribute(op,249);printf("%c ",c);break;
		case '8':SetConsoleTextAttribute(op,250);printf("%c ",c);break;
		case '#':SetConsoleTextAttribute(op,119);printf("%c ",c);break;
		case '@':SetConsoleTextAttribute(op,253);printf("%c ",c);break;
		case 'F':SetConsoleTextAttribute(op,253);printf("%c ",c);break;
		case '?':SetConsoleTextAttribute(op,252);printf("%c ",c);break;
	}
	SetConsoleTextAttribute(op,7);
	return;
}

——操作函数——
该函数包括两部分:
1.输出地图,本部分输出每次操作后map2的情况。
2.各类操作,包含五种情况1.翻地块,2.打问号,3.撤问号,4.打flag,5.撤flag。
待优化问题:函数有点冗长,可以把操作部分分成三个函数,分别负责翻地块、标记问号和标记flag。

void _game()
{
	HANDLE op;
	op=GetStdHandle(STD_OUTPUT_HANDLE);
	int i,j,x,y,I,J;
	int flag1=B,flag2=B;
	int mr1,mr2;
	int wtd;
	char c;
	while(flag1)
	{
		SetConsoleTextAttribute(op,142);
		printf("/ ");
		for(I=0;I<N;++I)
		{
			if(I<10)
				printf("%2d",I);
			else
				printf("%2c",'A'-10+I);
		}
		printf(" ");
		SetConsoleTextAttribute(op,7);
		printf("\n"); 
		for(I=0;I<N;++I)
		{
			SetConsoleTextAttribute(op,142);
			if(I<10)
				printf("%2d ",I);
			else
				printf("%2c ",'A'-10+I);
			SetConsoleTextAttribute(op,7);
			for(J=0;J<M;++J)
				_cloprint(map2[I][J]);
			printf("\n");
		}
		printf("\n");
		mark2:scanf("%d",&wtd);
		if(wtd==1)//翻开地块 
		{
			scanf("%d %d",&i,&j);
			if(i<0||i>=N||j<0||j>=M)
			{
				printf("the size of map is %d %d ! Please again!\n",N,M);
				goto mark2;
			}
			if(map2[i][j]!='#'&&map2[i][j]!='?'&&map2[i][j]!='F')
			{
				printf("The square has been scanned! Please again!\n");
				goto mark2;
			}
			if(map2[i][j]=='F')
				++flag2;
			map2[i][j]=map1[i][j];
			if(map1[i][j]=='@')
			{
				SetConsoleTextAttribute(op,0x0004);
				printf("YOU LOSE\n");
				SetConsoleTextAttribute(op,0x0007);
				break;
			}
			else if(map1[i][j]=='0')
			{
				--total;
				_dfs(i,j);
			}
			else  
				--total;
		}
		if(wtd==2)//打“?”标记 
		{
			scanf("%d %d",&i,&j);
			if(i<0||i>=N||j<0||j>=M)
			{
				printf("the size of map is %d %d ! Please again!\n",N,M);
				goto mark2;
			}
			if(map2[i][j]=='?'||map2[i][j]!='#')
			{
				printf("The square has been marked or scanned! Please again!\n");
				goto mark2;
			}
			if(map2[i][j]!='#')
				map2[i][j]='?';
		}
		if(wtd==3)//撤回标记"?" 
		{
			scanf("%d %d",&i,&j);
			if(i<0||i>=N||j<0||j>=M)
			{
				printf("the size of map is %d %d ! Please again!\n",N,M);
				goto mark2;
			}
			if(map2[i][j]!='?')
			{
				printf("The square has not been marked! Please again!\n");
				goto mark2;
			}
			map2[i][j]='#';
		} 
		if(wtd==4)//打“F”标记
		{
			scanf("%d %d",&i,&j);
			if(i<0||i>=N||j<0||j>=M)
			{
				printf("the size of map is %d %d ! Please again!\n",N,M);
				goto mark2;
			}
			if(map2[i][j]=='F'||map2[i][j]!='#')
			{
				printf("The square has been marked or scanned! Please again!\n");
				goto mark2;
			}
			else 
			{
				map2[i][j]='F';
				if(map1[i][j]=='@')
					--flag1;
				--flag2;
			}
		}
		if(wtd==5)//撤回标记"F"
		{
			scanf("%d %d",&i,&j);
			if(i<0||i>=N||j<0||j>=M)
			{
				printf("the size of map is %d %d ! Please again!\n",N,M);
				goto mark2;
			}
			if(map2[i][j]!='F')
			{
				printf("The square has not been marked! Please again!\n");
				goto mark2;
			}
			if(map1[i][j]=='@')
				++flag1;
			--flag2;
			map2[i][j]='#';
		} 
		if(total==B)
			flag1=0;
		system("cls"); 
	}
	if(flag1==0)
	{
		SetConsoleTextAttribute(op,0x000a);
		printf("YOU WIN\n");
		SetConsoleTextAttribute(op,0x0007);
	}
	return;
} 

——完整程序——
包括主函数

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#include<conio.h>
#include<windows.h>
#define N 10
#define M 10
#define B 10
int dx[8]={-1,-1,-1,0,0,1,1,1},dy[8]={-1,0,1,-1,1,-1,0,1};
int dx2[4]={-1,0,1,0},dy2[4]={0,-1,0,1};
char map1[N][M];
char map2[N][M];
char map3[N][M];
int bomb[2][100];
//int N,M,B;
int total;
void _Bomb()//生成地雷
{
	int i,j,r;
	for(i=0;i<B;++i)
	{
		srand(time(NULL));
		mark:r=rand();
		bomb[0][i]=rand()%N;
		bomb[1][i]=rand()%M;
		for(j=0;j<i;++j)
			if(bomb[0][j]==bomb[0][i]&&bomb[1][j]==bomb[1][i])
				goto mark;
	}
	return;
}
void _Mark()//构造地图
{
	int i,j;
	int x,y;
	for(i=0;i<B;++i)
	{
		map1[bomb[0][i]][bomb[1][i]]='@';
		for(j=0;j<8;++j)
		{
			x=bomb[0][i]+dx[j];
			y=bomb[1][i]+dy[j];
			if(x>=0&&x<N&&y>=0&&y<M&&map1[x][y]!='@')
				map1[x][y]+=1;
		}
	}
	return;
}
void _dfs(int x,int y)//显示'0' 
{
	int i,X,Y;
	for(i=0;i<8;++i)
	{
		X=dx2[i]+x;
		Y=dy2[i]+y;
		if(X>=0&&X<N&&Y>=0&&Y<M&&map1[X][Y]!='0'&&map2[X][Y]!=1)
		{
			map2[X][Y]=map1[X][Y];
			map3[X][Y]=1;
		}
	}
	for(i=0;i<4;++i)
	{
		X=dx2[i]+x;
		Y=dy2[i]+y;
		if(X>=0&&X<N&&Y>=0&&Y<M&&map1[X][Y]=='0'&&map3[X][Y]!=1)
		{
			map2[X][Y]='0';
			map3[X][Y]=1;
			--total;
			_dfs(X,Y);
		}
	}
	return;		
}
void _cloprint(char c)
{	
	HANDLE op;
	op=GetStdHandle(STD_OUTPUT_HANDLE);
	switch(c)
	{
		case '0':SetConsoleTextAttribute(op,247);printf("  ");break;
		case '1':SetConsoleTextAttribute(op,242);printf("%c ",c);break;
		case '2':SetConsoleTextAttribute(op,243);printf("%c ",c);break;
		case '3':SetConsoleTextAttribute(op,244);printf("%c ",c);break;
		case '4':SetConsoleTextAttribute(op,245);printf("%c ",c);break;
		case '5':SetConsoleTextAttribute(op,246);printf("%c ",c);break;
		case '6':SetConsoleTextAttribute(op,248);printf("%c ",c);break;
		case '7':SetConsoleTextAttribute(op,249);printf("%c ",c);break;
		case '8':SetConsoleTextAttribute(op,250);printf("%c ",c);break;
		case '#':SetConsoleTextAttribute(op,119);printf("%c ",c);break;
		case '@':SetConsoleTextAttribute(op,253);printf("%c ",c);break;
		case 'F':SetConsoleTextAttribute(op,253);printf("%c ",c);break;
		case '?':SetConsoleTextAttribute(op,252);printf("%c ",c);break;
	}
	SetConsoleTextAttribute(op,7);
	return;
}
void _game()
{
	HANDLE op;
	op=GetStdHandle(STD_OUTPUT_HANDLE);
	int i,j,x,y,I,J;
	int flag1=B,flag2=B;
	int mr1,mr2;
	int wtd;
	char c;
	while(flag1)
	{
		SetConsoleTextAttribute(op,142);
		printf("/ ");
		for(I=0;I<N;++I)
		{
			if(I<10)
				printf("%2d",I);
			else
				printf("%2c",'A'-10+I);
		}
		printf(" ");
		SetConsoleTextAttribute(op,7);
		printf("\n"); 
		for(I=0;I<N;++I)
		{
			SetConsoleTextAttribute(op,142);
			if(I<10)
				printf("%2d ",I);
			else
				printf("%2c ",'A'-10+I);
			SetConsoleTextAttribute(op,7);
			for(J=0;J<M;++J)
				_cloprint(map2[I][J]);
			printf("\n");
		}
		printf("\n");
		mark2:scanf("%d",&wtd);
		if(wtd==1)//翻开地块 
		{
			scanf("%d %d",&i,&j);
			if(i<0||i>=N||j<0||j>=M)
			{
				printf("the size of map is %d %d ! Please again!\n",N,M);
				goto mark2;
			}
			if(map2[i][j]!='#'&&map2[i][j]!='?'&&map2[i][j]!='F')
			{
				printf("The square has been scanned! Please again!\n");
				goto mark2;
			}
			if(map2[i][j]=='F')
				++flag2;
			map2[i][j]=map1[i][j];
			if(map1[i][j]=='@')
			{
				SetConsoleTextAttribute(op,0x0004);
				printf("YOU LOSE\n");
				SetConsoleTextAttribute(op,0x0007);
				break;
			}
			else if(map1[i][j]=='0')
			{
				--total;
				_dfs(i,j);
			}
			else  
				--total;
		}
		if(wtd==2)//打“?”标记 
		{
			scanf("%d %d",&i,&j);
			if(i<0||i>=N||j<0||j>=M)
			{
				printf("the size of map is %d %d ! Please again!\n",N,M);
				goto mark2;
			}
			if(map2[i][j]=='?'||map2[i][j]!='#')
			{
				printf("The square has been marked or scanned! Please again!\n");
				goto mark2;
			}
			if(map2[i][j]!='#')
				map2[i][j]='?';
		}
		if(wtd==3)//撤回标记"?" 
		{
			scanf("%d %d",&i,&j);
			if(i<0||i>=N||j<0||j>=M)
			{
				printf("the size of map is %d %d ! Please again!\n",N,M);
				goto mark2;
			}
			if(map2[i][j]!='?')
			{
				printf("The square has not been marked! Please again!\n");
				goto mark2;
			}
			map2[i][j]='#';
		} 
		if(wtd==4)//打“F”标记
		{
			scanf("%d %d",&i,&j);
			if(i<0||i>=N||j<0||j>=M)
			{
				printf("the size of map is %d %d ! Please again!\n",N,M);
				goto mark2;
			}
			if(map2[i][j]=='F'||map2[i][j]!='#')
			{
				printf("The square has been marked or scanned! Please again!\n");
				goto mark2;
			}
			else 
			{
				map2[i][j]='F';
				if(map1[i][j]=='@')
					--flag1;
				--flag2;
			}
		}
		if(wtd==5)//撤回标记"F"
		{
			scanf("%d %d",&i,&j);
			if(i<0||i>=N||j<0||j>=M)
			{
				printf("the size of map is %d %d ! Please again!\n",N,M);
				goto mark2;
			}
			if(map2[i][j]!='F')
			{
				printf("The square has not been marked! Please again!\n");
				goto mark2;
			}
			if(map1[i][j]=='@')
				++flag1;
			--flag2;
			map2[i][j]='#';
		} 
		if(total==B)
			flag1=0;
		system("cls"); 
	}
	if(flag1==0)
	{
		SetConsoleTextAttribute(op,0x000a);
		printf("YOU WIN\n");
		SetConsoleTextAttribute(op,0x0007);
	}
	return;
} 
int main()
{
	//printf("WELCOME! Print 'N' 'M' the size of map and and 'B' the number of bombs:\n(Exp:10 10 10)\n");
	while(1/*~scanf("%d %d %d",&N,&M,&B)*/)
	{
		int i,j;
		total=N*M;
		memset(map1,'0',sizeof(map1));
		memset(map2,'#',sizeof(map2));
		memset(map3,0,sizeof(map3));
		memset(bomb,0,sizeof(bomb));
		_Bomb();
		_Mark();
		//printf("The map has been prepared\n\n");
		printf("Here are the rules:\nEvery time you have five decisions\n"); 
		printf("1)Print 1 x y to scan (x,y)\n");
		printf("2)Print 2 x y to mark a '?' at (x,y). It means you are unsure about weather the square is a bomb\n");
		printf("3)Print 3 x y to delete '?'\n");
		printf("4)Print 4 x y to mark a 'F' at (x,y). It means you think the square is a bomb. You can mark a 'F' for B times.\n");
		printf("5)Print 5 x y to delete 'F'.It means you gain a 'F'.\n\n");
		printf("If you mark all the bombs successfully or scan all the square which are not bombs, you will win.\n");
		printf("If you scan a bomb, you will boom.\n");
		_game();
		printf("The map is:\n");
		for(i=0;i<N;++i)
		{
			for(j=0;j<M;++j)
				_cloprint(map1[i][j]);
			printf("\n");
		}	
		getchar();
		getchar();	
	}
	return 0;
} 

附件下载

链接:扫雷01.10,22,45
提取码:6oa9













2019/01/14

添加新模块,并进行些许优化。
更新内容:
1.重置生成雷的函数,不会上来就LOSE
2.五种操作简化为三种,并包装成三个函数

部分新模块信息

——生成地雷——

void _Bomb(int x,int y)//生成地雷
{
	int i,j,r;
	for(i=0;i<B;++i)
	{
		srand(time(NULL));
		bomb[0][i]=rand()%N;
		bomb[1][i]=rand()%M;
		while(map1[bomb[0][i]][bomb[1][i]]=='@'||bomb[0][i]==x&&bomb[1][i]==y)
		{
			bomb[0][i]=rand()%N;
			bomb[1][i]=rand()%M;
		}
		map1[bomb[0][i]][bomb[1][i]]='@';
	}
	return;
}

在_game()函数中的语段

scanf("%d %d %d",&wtd,&i,&j);
		if(fftfb==1&&i>=0&&i<N&&j>=0&&j<M)
		{
			_Bomb(i,j);
			_Mark();
			fftfb=0;
		}

读入一个正常坐标后开始初始化地雷位置,用fftfb记录是否已经被初始化。

——翻地块——

void _ScanASquare(int i,int j)//操作一 翻开地面 
{
	HANDLE op;
	op=GetStdHandle(STD_OUTPUT_HANDLE);
	if(i<0||i>=N||j<0||j>=M)
	{
		printf("The size of map is %d %d ! Please konck 'Enter' to input again!\n",N,M);
		getchar();
		getchar();
		return;
	}
	if(map2[i][j]!='#'&&map2[i][j]!='?'&&map2[i][j]!='F')
	{
		printf("The square has been scanned! Please konck 'Enter' to input again!\n");
		getchar();
		getchar();
		return;
	}
	if(map2[i][j]=='F')
		++flag2;
	map2[i][j]=map1[i][j];
	if(map1[i][j]=='@')
	{
		SetConsoleTextAttribute(op,0x0004);
		printf("YOU LOSE\n");
		SetConsoleTextAttribute(op,0x0007);
		flag1=B+1;
		return;
	}
	else if(map1[i][j]=='0')
		_dfs(i,j);
	--total;
}

——标记/撤除’?’——

void _Mark2(int i,int j)//操作二 标记/撤除问号 
{
	if(i<0||i>=N||j<0||j>=M)
	{
		printf("The size of map is %d %d ! Please konck 'Enter' to input again!\n",N,M);
		getchar();
		getchar();
		return; 
	}
	if(map2[i][j]=='#')
		map2[i][j]='?';	
	else if(map2[i][j]=='F')
	{
		map2[i][j]='?';
		if(map1[i][j]=='@')
			++flag1;
		++flag2;
	}
	else if(map2[i][j]=='?')
		map2[i][j]='#';
	else
	{
		printf("The square has been scanned! Please konck 'Enter' to input again!\n");
		getchar();
		getchar();
		return;
	}
}

——标记/撤除’F’——

void _Mark3(int i,int j)//标记/撤除flag 
{
	if(i<0||i>=N||j<0||j>=M)
	{
		printf("The size of map is %d %d ! Please konck 'Enter' to input again!\n",N,M);
		getchar();
		getchar();
		return; 
	}
	else if(map2[i][j]=='#'&&flag2>0)
	{
		map2[i][j]='F';
		if(map1[i][j]=='@')
			--flag1;
		--flag2;
	}
	else if(map2[i][j]=='?'&&flag2>0)
	{
		map2[i][j]='F';
		if(map1[i][j]=='@')
			--flag1;
		--flag2;
	}
	else if(map2[i][j]=='F')
	{
		map2[i][j]='#';
		if(map1[i][j]=='@')
			++flag1;
		++flag2;
	}
	else if(flag2<=0)
	{
		printf("The 'F' has been uesd out ! Please konck 'Enter' to input again!\n");
		getchar();
		getchar();
		return;
	}
	else
	{
		printf("The square has been scanned! Please konck 'Enter' to input again!\n");
		getchar();
		getchar(); 
		return;
	}
}

附件下载

链接:扫雷01.14,13.04.c
提取码:g9iz

链接:扫雷01.14,13.04.exe
提取码:493r

猜你喜欢

转载自blog.csdn.net/weixin_43843835/article/details/86304960
今日推荐