扫雷 C语言实现

一.游戏思路

 首先,创建两个二维数组,满足埋雷和在玩家面前显示的需要. 其次,写函数让玩家选择排查位置,实现扩散效果,要注意各种非法输入判定.然后,实现插旗.最后,判定输赢与否.

二.实现的具体内容

1.模拟登录界面

  "game.c"中写menu()函数.

//菜单
void menu()
{
	printf("**************************\n");
	printf("********* 1.paly *********\n");
	printf("********* 0.exit *********\n");
	printf("**************************\n");
	
}

"test.c"中写do while循环对玩家的选择做出反应.

int main()
{
	srand((unsigned int)time(NULL));
	int choice = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &choice);
		switch (choice)
		{
		case 1:
		{
			printf("扫雷\n");
			game();
			break;
		}
		case 0:
		{
			printf("游戏结束\n");
			break;
		}
		default:
		{
			printf("输入非法,重输\n");
		}
		}

	} while (choice);

2.初始化棋盘

  设置两个9*9棋盘,一个初始化全为'0',为埋雷'1'准备.另一个初始化全为'*',为排查和插旗准备.

//初始化棋盘
void initboard(char arr[ROWS][LINES],int rows,int lines,char set)
{
	int i = 0, k = 0;
	for (i = 0; i < rows; i++)
	{
		for (k = 0; k < lines; k++)
		{
			arr[i][k] = set;
		}
	}
}

这里要注意的是:

1.为了实现调节难度自由,也就是更简单地改变游戏的长和宽,避免改变长宽时要到程序里一个个改.

在头文件"game.h"中设置ROWS = 9,LINES = 9.改变时仅需在头文件改变一次即可.

#define	_CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define ROW 9
#define LINE 9
#define ROWS ROW+2
#define LINES LINE+2

还有一个重要的点,排查是排查周围8格的'1'数量.若仅建立9*9数组,排查最外面一排时会超出数组范围.因此,实际是建立11*11的数组.(要在"game.c"和"test.c"中引用"game.h",如下)

2.有两个数组需初始化,双for迭代的式子中若用常量'*'或'0',要创建两个初始化函数,太麻烦.所以,建立变量set承接不同的需要.

char inboard[ROWS][LINES] = { 0 };
char outboard[ROWS][LINES] = { 0 };
initboard(inboard, ROWS, LINES, '0');
initboard(outboard, ROWS, LINES, '*');

3.画出棋盘

  我们创建了11*11的数组,但呈现在玩家眼中只有9*9.

//画出棋盘
void drawboard(char arr[ROWS][LINES], int rows, int lines)
{
	int i = 0;
	for (i = 0; i < rows - 1; i++)
	{
		printf("%2d ", i);
	}
	printf("\n");
	int h = 0, j = 0;
	for (h = 1; h < rows - 1; h++)
	{
		printf("%2d ", h);
		for (j = 1; j < lines - 1; j++)
		{
			printf("%2c ", arr[h][j]);
		}
		printf("\n");
	}

}

效果如下

因为玩家使用"输入坐标"的方式排查,所以标上行列有助于玩家输入.

这里需要注意的点是:"%2d".若呈现的方阵长宽<10,将2删去没有影响.

但长宽>9,会出现不对齐的状况.

4.埋雷

使用rand函数随机指定数字,经过取余加1使之符合数组下标1-9的需要.

//埋雷
void device(char arr1[ROWS][LINES], int row, int line)
{
	int i = rand() % (row)+1, j = rand() % (line)+1;
	int n = 0;
	for (n = 0; n < COUNT; n++)
	{
		while (arr1[i][j] == '1')
		{
			i = rand() % (row)+1, j = rand() % (line)+1;
		}
		arr1[i][j] = '1';
	}
}

需要注意的是:

1.用rand()需要在"test.c"中调用srand函数(若放在"game.c"中只能产生固定数)

因为srand()需要unsigned int类型,而time是time_t类型所以用()进行强制类型转换.

"NULL"为空指针引用在time中可达到取随机数效果.

最后,别忘了在"game.h"中引用预处理命令,srand是<stdlib.h>,time是<time.h>.

5.玩家输入坐标

玩家输入坐标后要进行"输入是否非法"和"是否踩到炸弹"的判定.

//人玩
void play(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int row, int line)
{
	int a = 0, b = 0;
	while (arr1[a][b] == '0')
	{
		printf("请输入坐标:>");
		scanf("%d,%d", &a, &b);
		while (1)
		{
			if (a < 0 || a > 9 || b < 0 || b > 9)
			{
				printf("输入非法,重输\n");
				scanf("%d,%d", &a, &b);
			}
			else
				break;
		}
		if (arr1[a][b] == '1')
		{
			printf("你被炸死了\n");
			break;
		}

此函数play将会包含一整个游戏的进行过程,包含输入\判输\扩散\判赢.(除了初始化棋盘,和埋雷)

6.探测周围8格的炸弹数量

创建一个count函数存放炸弹数量,将周围8格依次判定,为'1'则count+1.

//探测
int detect(char arr1[ROWS][LINES], int a, int b)
{
	int count = 0;
	if (arr1[a - 1][b - 1] == '1')
		count += 1;
	if (arr1[a - 1][b] == '1')
		count += 1;
	if (arr1[a - 1][b + 1] == '1')
		count += 1;
	if (arr1[a][b - 1] == '1')
		count += 1;
	if (arr1[a][b + 1] == '1')
		count += 1;
	if (arr1[a + 1][b - 1] == '1')
		count += 1;
	if (arr1[a + 1][b] == '1')
		count += 1;
	if (arr1[a + 1][b + 1] == '1')
		count += 1;
	return count;

}

这是排查和扩散的基础.

7.扩散效果

  我们需要在玩家选择一个坐标后,排查周围8格的地雷数量,若为0,则周围8格依次排查周围8格的地雷数量,直到排查出周围8格的地雷数量不为0,实现扩散效果.需要递归.

//扩散
void dif(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int row, int line, int a, int b)
{
	int d = detect(arr1, a, b);
	if (a > 0 && a <= row && b > 0 && b <= line)
	{
		if (d == 0)
		{
			arr2[a][b] = '0';
			int i = 0;
			for (i = a - 1; i <= a + 1; i++)
			{
				int j = 0;
				for (j = b - 1; j <= b + 1; j++)
				{
					if (arr2[i][j] != '0')
					{
						dif(arr1, arr2, row, line, i, j);
					}
				}
			}
		}
		else
		{
			arr2[a][b] = d + '0';
		}
	}
}

迭代两个for中的式子,若周围已排查过为'0',则无需再排查,减少计算.

8.插旗

  玩家每排查一个雷后,可进行无限次插旗.有五种情况:(1)玩家输入下标超出范围(2)玩家输入的下标在数组中的字符为数字(已排查过)(3)玩家输入的下标在数组中的字符为'#'(4)玩家想结束插旗(5)玩家插旗成功.

(1)(2)一样,让玩家重输.(3)则改'#'回'*',相当于把旗拔了.(4)玩家输入"0,0",使得a = 0,while的条件为假,停止循环.(5)玩家输入的下标正常,继续进行下一次插旗.

  插完旗画出棋盘让玩家看的更清晰.接着进行两个获胜判定的插旗判定,后面讲到.

//标记
void signal(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int rows, int lines)
{
	int a = 1, b = 1;
	while (a)
	{
		printf("请标记(若弃权则输入\"0,0\") :>");
		scanf("%d,%d", &a, &b);
		if (a < rows - 1 && a > 0 && b < lines - 1 && b > 0)
		{
			arr2[a][b] = '#';
		}
		else if (arr2[a][b] != '*')
		{
			if (arr2[a][b] == '#')
			{
				arr2[a][b] = '*';
			}
			printf("输入非法,重输\n");
		}
		else if (a >= rows - 1 || a < 0 || b >= lines - 1 || b < 0)
		{
			printf("输入非法,重输\n");
		}
		drawboard(arr2, rows, lines);
		int d = is_win1(arr1, arr2, rows, lines);
		if (d == 1)
		{
			break;
		}
	}

}

9.获胜判定

  获胜判定有两种:剩余判定和插旗判定.

(1)剩余判定

  玩家将除雷外的所有格子全部排查过即判定为胜利.(如9*9数组中布置10个雷,画面中有71个格子为数字,即胜利.)

  插入位置:玩家完成一次排查之后,插旗之前.

//剩余判定
int is_win2(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int rows, int lines)
{
	int i = 0, j = 0, K = 0;
	for (i = 1; i < rows - 1; i++)
	{
		for (j = 1; j < lines - 1; j++)
		{
			if (arr2[i][j] != '*' && arr2[i][j] != '#')
			{
				K++;
			}
		}
	}
	if (K == ROW * LINE - COUNT)
	{
		return 1;
	}
	else
		return 0;
}

这里用的是非数字格子的数量,若等同于地雷数量,则判定为胜利.

*COUNT在"game.h"中设定为雷的数量10.

(2)插旗判定

  若玩家将所有地雷位置的字符改为'#'(即第一个数组中为'1',第二个数组中为'#'同时成立),则判定胜利.

//插旗判定
int is_win1(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int rows, int lines)
{
	int i = 0, j = 0, count = 0;
	for (i = 1; i < rows - 1; i++)
	{
		for (j = 1; j < lines - 1; j++)
		{
			if (arr1[i][j] == '1' && arr2[i][j] == '#')
			{
				count += 1;
			}
		}
	}
	if (count == COUNT)
	{
		return 1;
	}
	else
		return 0;
}

  这样,一个简单的排雷程序就写完了!

三.完整程序

test.c(存放主流程)

#include "game.h"
void game()
{
	char inboard[ROWS][LINES] = { 0 };
	char outboard[ROWS][LINES] = { 0 };
	initboard(inboard, ROWS, LINES, '0');
	initboard(outboard, ROWS, LINES, '*');
	drawboard(outboard, ROWS, LINES);
	device(inboard, ROW, LINE);
	/*drawboard(inboard, ROWS, LINES);*/
	play(inboard, outboard, ROW, LINE);
}

int main()
{
	srand((unsigned int)time(NULL));
	int choice = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &choice);
		switch (choice)
		{
		case 1:
		{
			printf("扫雷\n");
			game();
			break;
		}
		case 0:
		{
			printf("游戏结束\n");
			break;
		}
		default:
		{
			printf("输入非法,重输\n");
		}
		}

	} while (choice);

	return 0;
}

game.c(存放函数)

#include "game.h"

//菜单
void menu()
{
	printf("**************************\n");
	printf("********* 1.paly *********\n");
	printf("********* 0.exit *********\n");
	printf("**************************\n");

}

//初始化棋盘
void initboard(char arr[ROWS][LINES], int rows, int lines, char set)
{
	int i = 0, k = 0;
	for (i = 0; i < rows; i++)
	{
		for (k = 0; k < lines; k++)
		{
			arr[i][k] = set;
		}
	}
}

//画出棋盘
void drawboard(char arr[ROWS][LINES], int rows, int lines)
{
	int i = 0;
	for (i = 0; i < rows - 1; i++)
	{
		printf("%2d ", i);
	}
	printf("\n");
	int h = 0, j = 0;
	for (h = 1; h < rows - 1; h++)
	{
		printf("%2d ", h);
		for (j = 1; j < lines - 1; j++)
		{
			printf("%2c ", arr[h][j]);
		}
		printf("\n");
	}

}

//埋雷
void device(char arr1[ROWS][LINES], int row, int line)
{
	int i = rand() % (row)+1, j = rand() % (line)+1;
	int n = 0;
	for (n = 0; n < COUNT; n++)
	{
		while (arr1[i][j] == '1')
		{
			i = rand() % (row)+1, j = rand() % (line)+1;
		}
		arr1[i][j] = '1';
	}
}

//人玩
void play(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int row, int line)
{
	int a = 0, b = 0;
	while (arr1[a][b] == '0')
	{
		printf("请输入坐标:>");
		scanf("%d,%d", &a, &b);
		while (1)
		{
			if (a < 0 || a > 9 || b < 0 || b > 9)
			{
				printf("输入非法,重输\n");
				scanf("%d,%d", &a, &b);
			}
			else
				break;
		}
		if (arr1[a][b] == '1')
		{
			printf("你被炸死了\n");
			break;
		}
		else
		{
			dif(arr1, arr2, row, line, a, b);
			drawboard(arr2, ROWS, LINES);

			int k = is_win2(arr1, arr2, row + 2, line + 2);
			if (k == 1)
			{
				printf("你赢了\n");
				break;
			}

			signal(arr1, arr2, row + 2, line + 2);
			}

		}


	}



}

//探测
int detect(char arr1[ROWS][LINES], int a, int b)
{
	int count = 0;
	if (arr1[a - 1][b - 1] == '1')
		count += 1;
	if (arr1[a - 1][b] == '1')
		count += 1;
	if (arr1[a - 1][b + 1] == '1')
		count += 1;
	if (arr1[a][b - 1] == '1')
		count += 1;
	if (arr1[a][b + 1] == '1')
		count += 1;
	if (arr1[a + 1][b - 1] == '1')
		count += 1;
	if (arr1[a + 1][b] == '1')
		count += 1;
	if (arr1[a + 1][b + 1] == '1')
		count += 1;
	return count;

}

//标记
void signal(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int rows, int lines)
{
	int a = 1, b = 1;
	while (a)
	{
		printf("请标记(若弃权则输入\"0,0\") :>");
		scanf("%d,%d", &a, &b);
		if (a < rows - 1 && a > 0 && b < lines - 1 && b > 0)
		{
			arr2[a][b] = '#';
		}
		else if (arr2[a][b] != '*')
		{
			if (arr2[a][b] == '#')
			{
				arr2[a][b] = '*';
			}
			printf("输入非法,重输\n");
		}
		else if (a >= rows - 1 || a < 0 || b >= lines - 1 || b < 0)
		{
			printf("输入非法,重输\n");
		}
		drawboard(arr2, rows, lines);
		int d = is_win1(arr1, arr2, rows, lines);
		if (d == 1)
		{
			break;
		}
	}

}

//插旗判定
int is_win1(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int rows, int lines)
{
	int i = 0, j = 0, count = 0;
	for (i = 1; i < rows - 1; i++)
	{
		for (j = 1; j < lines - 1; j++)
		{
			if (arr1[i][j] == '1' && arr2[i][j] == '#')
			{
				count += 1;
			}
		}
	}
	if (count == COUNT)
	{
		return 1;
	}
	else
		return 0;
}

//剩余判定
int is_win2(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int rows, int lines)
{
	int i = 0, j = 0, K = 0;
	for (i = 1; i < rows - 1; i++)
	{
		for (j = 1; j < lines - 1; j++)
		{
			if (arr2[i][j] != '*' && arr2[i][j] != '#')
			{
				K++;
			}
		}
	}
	if (K == ROW * LINE - COUNT)
	{
		return 1;
	}
	else
		return 0;
}

//扩散
void dif(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int row, int line, int a, int b)
{
	int d = detect(arr1, a, b);
	if (a > 0 && a <= row && b > 0 && b <= line)
	{
		if (d == 0)
		{
			arr2[a][b] = '0';
			int i = 0;
			for (i = a - 1; i <= a + 1; i++)
			{
				int j = 0;
				for (j = b - 1; j <= b + 1; j++)
				{
					if (arr2[i][j] != '0')
					{
						dif(arr1, arr2, row, line, i, j);
					}
				}
			}
		}
		else
		{
			arr2[a][b] = d + '0';
		}
	}
}

game.h(存放预处理命令和函数声明)

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define ROW 9
#define LINE 9
#define ROWS ROW+2
#define LINES LINE+2
#define COUNT 10
void menu();
void initboard(char arr[ROWS][LINES], int rows, int lines, char set);
void drawboard(char arr[ROWS][LINES], int rows, int lines);
void device(char arr[ROWS][LINES], int rows, int lines);
void play(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int row, int line);
void signal(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int rows, int lines);
void dif(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int row, int line, int a, int b);
int is_win2(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int rows, int lines);
int is_win1(char arr1[ROWS][LINES], char arr2[ROWS][LINES], int rows, int lines);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值