数据结构课程设计------扫雷游戏(升级版,可展开)

本程序由团队中的一个人所写,本人看懂并写下此文章

题目:扫雷

3.1问题描述

扫雷游戏
[基本要求]
(1)完成棋盘的初始化并在标准显示器中显示
(2)通过输入行列值确定用户输入
(3)游戏进行给出提示信息
(4)给出游戏的测试程序。

3.2.算法设计与分析

3.2.1设计思路分析
首先定义一个二维数组的棋盘,用伪随机数在棋盘上生成地雷。通过从键盘上输入坐标,判断此位置是不是存在地雷,存在则游戏结束,不存在则展开棋盘。展开的周围显示出附近存在的地雷个数。依次类推,直到找出所有地雷。
3.2.2设计程序流程图(要求图文并茂)
在这里插入图片描述
首先进入菜单,选择开始游戏,生成棋盘与地雷,然后显示棋盘,用户输入相应的位置坐标,程序进行判断该位置是否为地雷,是的话游戏结束,不是的话则展开棋盘,继续输入并统计地雷个数,依次类推,直到地雷数统计完则游戏结束。
3.2.3数据结构定义

#define COUNT 10
#define ROW 9
#define CLO 9
#define ROWS 11
#define CLOS 11
char arr[ROW][CLO] = { 0 };    //显示棋盘
char arrs[ROWS][CLOS] = { 0 };   //真正操作的棋盘

3.2.4算法的时间复杂度分析
棋盘初始化模块的算法,对定义好的二维数组元素进行定义,时间复杂度为O(1)。
模拟棋盘初始化算法,对定义好的二维数组元素进行定义,时间复杂度为O(1)。
打印棋盘的算法,通过依次遍历二维数组打印数据元素,时间复杂度为O(n)。
地雷生成算法,通过伪随机数在棋盘上生成地雷,时间复杂度为O(n)。
判断是否为地雷算法,通过数据匹配的方式进行判断,时间复杂度为O(1)。
统计地雷个数的算法,通过数据匹配的方式进行统计,时间复杂度为O(n)。
棋盘展开算法,每次输入坐标展开棋盘,时间复杂度为O(1)。

3.3源程序清单(带注释)

Game.h
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#define COUNT 10
#define ROW 9
#define CLO 9
#define ROWS 11
#define CLOS 11
void printbroad(char arr[ROW][CLO], int row, int clo);
void arr_init(char arr[ROW][CLO], int row, int clo);
void menu();
void arrs_randinit(char arrs[ROWS][CLOS], int row, int clo);     //生成随机雷
void arrs_init(char arrs[ROWS][CLOS], int row, int clo);
void print(char arrs[ROWS][CLOS], int row, int clo);
void sweep(char arrs[ROWS][CLOS], char arr[ROW][CLO], int row, int clo);  //扫雷
int is_thunder(char arrs[ROWS][CLOS], char arr[ROW][CLO], int row, int clo);
int count(char arrs[ROWS][CLOS], int row, int clo);         //计算周围雷的数目
void movefirst(char arrs[ROWS][CLOS], int row, int clo);
void open(char arrs[ROWS][CLOS], char arr[ROW][CLO], int i, int j);
Game.c
#pragma warning(disable:4996)
#include"game.h"
int num = ROW*CLO - COUNT;
void menu()
{
	printf("****************************************\n");
	printf("***************  1.play   **************\n");
	printf("***************  0.exit   **************\n");
	printf("****************************************\n");

}
//打印扫雷棋盘
void printbroad(char arr[ROW][CLO], int row, int clo)
{
	int i = 0;
	int j = 0;
	printf("   ");
	//打印扫雷棋盘的横坐标
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i );
		
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("--");

	}
	printf("--");
	printf("\n");
	for (i = 0; i < row; i++)
	{
		//打印扫雷棋盘的纵坐标
		printf("%d| ", i+1);
		for (j = 0; j < clo; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}

void arr_init(char arr[ROW][CLO], int row, int clo)
{
	//这个函数的功能是将该数组所有置*
	memset(&arr[0][0], '*', row*clo*sizeof(arr[0][0]));
}
void arrs_init(char arrs[ROWS][CLOS], int row, int clo)
{
	memset(&arrs[0][0], '0', row*clo*sizeof(arrs[0][0]));
}

//生成随机地雷,1代表地雷,0代表没有地雷;
void arrs_randinit(char arrs[ROWS][CLOS], int row, int clo)
{
	int count = COUNT;
	int i = 0;
	int j = 0;
	do
	{
		//生成随机横纵坐标
		i = rand() % 9+1;  //生成1~9的横坐标
		j = rand() % 9+1;  //生成1~9的纵坐标
		if (arrs[i][j] != '1') //如果此处有地雷,则继续循环
		{
			arrs[i][j] = '1';
			count--;
		}
	} while (count);

		

}
void print(char arrs[ROWS][CLOS], int row, int clo)
{
	int i = 0;
	int j = 0;


	printf("   ");
	for (i = 1; i < row - 1; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i < row - 1; i++)
	{
		printf("--");
	}
	printf("--");
	printf("\n");

	printf("1|");
	for (i = 1; i < row-1; i++)
	{
		printf(" ");
		for (j = 1; j < clo-1; j++)
		{
			printf("%c ", arrs[i][j]);
		}
		printf("\n");
		if (i == 9)
		{
			continue;
		}
		printf("%d|", i+1);

	}
	//int i = 0;
	//int j = 0;
	//printf("   ");
	打印扫雷棋盘的横坐标
	//for (i = 1; i <= row-2; i++)
	//{
	//	printf("%d ", i);

	//}
	//printf("\n");
	//for (i = 1; i <= row-2; i++)
	//{
	//	printf("--");

	//}
	//printf("--");
	//printf("\n");
	//for (i = 0; i < row-2; i++)
	//{
	//	//打印扫雷棋盘的纵坐标
	//	printf("%d| ", i + 1);
	//	for (j = 0; j < clo-2; j++)
	//	{
	//		printf("%c ", arrs[i][j]);
	//	}
	//	printf("\n");
	//}
	//printf("\n");
}


int is_thunder(char arrs[ROWS][CLOS],char arr[ROW][CLO], int row, int clo)
{
	if (arrs[row][clo] == '1')
	{
		return 0;
	}
	else
	{
		return 1;
	}
}

//计算周围一圈(8个数)有多少个地雷
int count(char arrs[ROWS][CLOS], int row, int clo)
{
	int count = 0;
	return ((arrs[row - 1][clo - 1] +
		arrs[row - 1][clo] +
		arrs[row - 1][clo + 1] +
		arrs[row][clo - 1] +
		arrs[row][clo + 1] +
		arrs[row + 1][clo - 1] +
		arrs[row + 1][clo] +
		arrs[row + 1][clo + 1])-'0'*8);

}
//当玩家第一次就点到地雷时,得把地雷移走
void movefirst(char arrs[ROWS][CLOS], int row, int clo)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		x = rand() % 9 + 1;
		y = rand() % 9 + 1;
		if (arrs[x][y] != '1')
		{
			arrs[x][y] = '1';
			break;
		}
	}
	arrs[row][clo] = '0';
}

void sweep(char arrs[ROWS][CLOS], char arr[ROW][CLO], int row, int clo)
{
	//判断是否为第一次扫雷的标志位
	int first = 0;
	int x = 0;
	int y = 0;
	int flag = 1;
	int ret = 0;
	while (flag)
	{
		printf("请输入坐标>\n");
		scanf("%d%d", &x, &y);
		//如果是雷,则返回0,否则返回1;
		ret = is_thunder(arrs, arr, x, y);
		if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
		{
			first++;
			switch (ret)
			{
			case 1:
				if (count(arrs, x, y) == 0)
				{
					open(arrs, arr, x - 1, y - 1);
				}
				else {
					arr[x - 1][y - 1] = count(arrs, x, y) + '0';
					num--;
				}
				printbroad(arr, ROW, CLO);
				//printf("\n%d\n", num);
				break;
			case 0:
				//如果第一次就碰到了雷,就将雷移走,提升游戏体验
				if (first == 1)
				{
					movefirst(arrs, x, y);
					if (count(arrs, x, y) == 0)
					{
						open(arrs, arr, x - 1, y - 1);
					}
					else {
						arr[x-1][y-1] = count(arrs, x, y) + '0';
						num--;
					}
					printbroad(arr, ROW, CLO);
					break;
				}
				printf("你被炸掉了\nGAMEOVER!!!!!!!!\n");
				print(arrs, ROWS, CLOS);
				flag = 0;
				break;
			}
			if (0 == num)
			{
				printf("游戏结束,玩家赢!!!\n");
				break;
			}
		}
		else
		{
			printf("输入错误,请重新输入:\n");
		}
	}
}
int right(int i, int j)
{
	if (  (i + 1)>=1 && (i + 1)<=9 && (j + 1)>=1 &&(j + 1)<= 9  )
	{
		return 1;
	}
	return 0;
}

void open(char arrs[ROWS][CLOS], char arr[ROW][CLO], int i, int j)
{

	//如果当前位置没有地雷,而且当前位置在棋盘上没有被显示,则显示出周围一圈雷的数量;
	if (arrs[i+1][j+1] == '0' && arr[i][j] == '*'&&right(i,j))
	{
		arr[i][j] = count(arrs, i+1, j+1) + '0';
		num--;
	}
	//左一位置
	if (arrs[i + 1][j] == '0' && arr[i][j - 1] == '*'&&right(i, j-1))
	{
		arr[i][j-1] = count(arrs, i+1, j) + '0';
		num--;
		if (count(arrs, i+1, j) == 0)
		{
			open(arrs, arr, i, j-1);
		}
	}
	//右一位置
	if (arrs[i + 1][j + 2] == '0' && arr[i][j + 1] == '*'&&right(i, j+1))
	{
		arr[i][j + 1] = count(arrs, i+1, j + 2) + '0';
		num--;
		if (count(arrs, i+1, j + 2) == 0)
		{
			open(arrs, arr, i, j + 1);
		}
	}
	//上一位置
	if (arrs[i][j + 1] == '0' && arr[i - 1][j] == '*'&&right(i-1, j))
	{
		arr[i - 1][j] = count(arrs, i, j+1) + '0';
		num--;
		if (count(arrs, i, j+1) == 0)
		{
			open(arrs, arr, i-1, j);
		}
	}
	//左上位置
	if (arrs[i][j] == '0' && arr[i - 1][j - 1] == '*'&&right(i-1, j-1))
	{
		arr[i - 1][j - 1] = count(arrs, i, j) + '0';
		num--;
		if (count(arrs, i, j) == 0)
		{
			open(arrs, arr, i - 1, j - 1);
		}
	}
	//右上
	if (arrs[i][j + 2] == '0' && arr[i - 1][j + 1] == '*'&&right(i-1, j+1))
	{
		arr[i - 1][j + 1] = count(arrs, i, j + 2) + '0';
		num--;
		if (count(arrs, i, j + 2) == 0)
		{
			open(arrs, arr, i-1, j + 1);
		}
	}
	//右下
	if (arrs[i + 2][j + 2] == '0' && arr[i + 1][j + 1] == '*'&&right(i+1, j+1))
	{
		arr[i + 1][j + 1] = count(arrs, i + 2, j + 2) + '0';
		num--;
		if (count(arrs, i + 2, j + 2) == 0)
		{
			open(arrs, arr, i + 1, j + 1);
		}
	}
	//下
	if (arrs[i + 2][j + 1] == '0' && arr[i + 1][j] == '*'&&right(i+1, j))
	{
		arr[i + 1][j] = count(arrs, i + 2, j+1) + '0';
		num--;
		if (count(arrs, i + 2, j+1) == 0)
		{
			open(arrs, arr, i + 1, j);
		}
	}
	//左下
	if (arrs[i + 2][j] == '0' && arr[i + 1][j - 1] == '*'&&right(i+1, j-1))
	{
		arr[i + 1][j - 1] = count(arrs, i + 2, j) + '0';
		num--;
		if (count(arrs, i + 2, j) == 0)
		{
			open(arrs, arr, i + 1, j - 1);
		}
	}
}
Test.c
#pragma warning(disable:4996)
#include"game.h"
void playgame()
{
	char arr[ROW][CLO] = { 0 };    //显示棋盘
	char arrs[ROWS][CLOS] = { 0 };   //真正操作的棋盘
	arr_init(arr, ROW, CLO);
	arrs_init(arrs, ROWS, CLOS);
	arrs_randinit(arrs, ROWS, CLOS);
	printbroad(arr, ROW, CLO);
	print(arrs, ROWS, CLOS);
	sweep(arrs, arr, ROW, CLO);
	
}
int main()
{
	srand((unsigned int)time(NULL));
	int key = 0;
	do
	{
		menu();
		scanf("%d", &key);
		if (key == 1)
			playgame();
		else if (key == 0)
			printf("退出游戏\n");
		else
			printf("输入有误,请重新输入:\n");
	} while (key);
	return 0;
}

3.4执行结果

在这里插入图片描述
生成棋盘与地雷
在这里插入图片描述
输入坐标进行判断
在这里插入图片描述
输入坐标进行判断
在这里插入图片描述
游戏结束
在这里插入图片描述

3.5存在问题分析

(1)没有实现插旗的功能。
(2)不能统计以往的游戏信息,没有实现数据保存。
(3)没有排行榜功能。
(4)不能进行游戏难度的设定。

3.6结论

这次的扫雷程序代码量比较大,一开始给我们小组造成了一些困难,但经过我们的查找资料,相互交流,将这些困难一一克服,其中的展开函数经过我们的不断调试,终于将正确的参数确定。完成了这项代码,我觉得我的代码能力提高了很多,对问题的分析有了进一步的提升,即使出现问题,经过不断的调试,耐心的对待,最后总会成功的。在以后的学习中更要脚踏实地,不怕困难。

  • 12
    点赞
  • 111
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值