C语言—打字母单机游戏(简易)

分析

需求
制作一个打字母游戏,要求在屏幕中每隔一段时间,下落一定数目的字符,然后由键盘输入对应的字母后,能够达到消除该字符,并称产生新字符的功能。最好能够体现出难度提升的模式。

需求分析
通过需求我们可以获得一下几点信息:
显式信息:1.这是一个打字母游戏
2.字母在显示过程中体现出下落功能
3.从键盘输入字母
4.正确输入可以消除字母并产生新字母
5.能够体现难度升级

隐式信息
1.字母的范围,是否同时出现相同字母(如大小写同时显示在屏幕上)
2.初始状态显示器中显示多少个字母,怎么排列,是否需要随机产生显示位置
3.字母下落的速度
4.产生新的字母的位置(是随机行列,还是固定行列位置)
5.难度升级是否有终点,还是无限生机难度,是以提升速度的方式升级难度,还是以 增加显示的字母数目的方式来增加难度,或者二者兼有
6.如何判定游戏的结束,是以字母显示的下限作为条件,还是以错误的次数作为条件

程序结构设计
1. 该程序的完成,主要因由三个模块完成。分别是显示模块,初始化模块,字母匹配重置模块,管理和难度升级模块下面仔细分析四个模块的内容。
2.在分析四个模块之前,需要解决的问题是字母以何种形式存储,首先由于字母(算上大小写)总共只有52个,那么由于元素个数并不是特别多,最理想的方式就是以数组的方式存储这52个字母。(当然也可以随机生成字母的方式,但是这种方式与我结构化的设计思路并不相符,所以不谈。)另外字母因该有与之对应的属性,如是否已经存在与当前的显示屏中,或是是否被销毁。其次还需要的属性就是行和列,这里行列属性的要求与我后面的显示模块的需求有关。那么很显然,我需要的字母元素具有多种属性而不是单一结构自然就需要用到结构体来设计字母类型的结构体数组。在说明完字母元素的类型后,开始正式分析三个模块。

显示模块
显示模块是整个程序的只管交互模块,那么就需要确定显示的方式和过程。
1.以二维数组的方式表现游戏的字母显示范围,其中数组的行列可变
2.除根据字母元素的行列属性显示字母在数组对应的位置外,其余行列的元素以空格显示。
3.为了满足下落显示的功能,所以二维数组的显示应在某个时间间隔后进行重复调用,从而达到视觉效果的“下落功能”。
4.最重要的一点,如何使用结构体数组来表示52个不同的字母,采用方式为下标查找法,由于数组的最大优点便是查表速度快,所以使用52个结构体数组的下标值,通过计算得到对应的小写或大写的字母。

初始化模块
1.确定了以结构体数组的方式,通过下标计算来代替直接存储字母的方式,那么在初始化时,我们需要初始化结构体的目标就可以通过修改结构体的“存在”属性来选择某个位置的元素是否被初始化。同时对其“行列”属性赋值来确定其在显示模块中的位置。
2.需要确定最初初始化元素的数目,也就是屏幕刚开始的显示个数
3.由于需要考虑字母的产生位置是随机的,那么我们在初始化时,就必须排除,初始化的是否出现重复而倒置初始化的数目不够。而重复的情况有两种。一种是下标重复,倒置初始化相同位置的数组元素。第二种是列重复,倒置上一个被初始化的元素被后初始化的元素覆盖,从而无法从屏幕中显示上一个被初始化的字母。那么解决这个问题就需要,将每次初始化的元素下标进行“存在”属性的排除,在为每个初始化的字母元素保存”列“属性,通过查表”列“属性是否存在来排除列重复的问题。

字母匹配重置模块
1.由于需要有输入数据和程序的保存数据进行比较的功能,那么就要考虑在输入数据是,程序进入阻塞状态是否会对程序的运行要求产生冲突,而根据需求来看我们在输入数据时采用getch函数来防止由于回车未输入而导致的程序阻塞。
2.字母的显示过程中不能每次不论是否有输入都进行数据比较,所以应有判断是否有从键盘的输入出现,kbhit函数的使用就具有必要性。
3.由于存在字母消除和产生新字母需求,那么在该模块中自然因该为消除提供字母匹配查表的功能和字母重置的功能。而需要关注的重点是,字母重置后同样会产生下标相同的元素重复问题,和列属性重复问题,所以在该模块也因有查重功能。

管理和难度升级模块
1.该模块作为其他模块的调用和数据处理的功能,首先要确定的是显示区域的大小以及最初下落速度,以及最初初始化字母个数的问题。
2.对每次显示模块的调用后,字母元素的行列属性做出调整,方便下次调用显示模块的正确执行。
3.对游戏的难度变化和设置做出规划,以及游戏结束条件的设置。设定难度是由什么变化引起的改变,若为显示字母的数目,则因设置一个计数器来计算成功消除字母的个数,从而提升难度,若为速度变化,则设定速度等级来调整sleep函数的睡眠时间。
4.如何实现字母个数的增加,或者sleep时间的减少。首先通过难度升级时,改变改变初始化字母的个数(size变量),调用初始化模块进行对上一难度的清除,重新设定显示字母个数。其次改变sleep参数(time变量)的大小来提升下落的速度。

四个模块设计完毕后,通过主函数的调用。实现各个模块之间的联系,而这种模块化的设计可以使设计思路更加清晰,从侧面减少了设计难度,在调试修改时也降低了修改范围和编译时间,从而提升了工作效率提升设计完整性。


#define rows 15
#define cols 30
#define lettersize 52
#define none 0
#define exist 1
struct let{
	int tag;//0不存在,1存在
	int row;
	int col;
};

void init_array(let*ar, int size)
{
	assert(ar != NULL);
	int arr[lettersize] = { 0 };//存放col的数组
	int i = 0;
	while (i<size)
	{
		int index = rand() % lettersize;
		if (ar[index].tag == none)//防止出现相同的数
		{
			ar[index].tag = exist;
			ar[index].row = 0;
			ar[index].col = rand() % cols;
			for (int j = 0; j < size; j++)//防止两个字符出现在同一列,通过arr数组来将字符隔开
			{
				if (arr[j] == ar[index].col)
				{
					j = -1;
					ar[index].col = rand() % cols;
				}
			}
			arr[i] = ar[index].col;
			i++;
		}
	}
}

void show(let *ar)
{
	assert(ar != NULL);
	system("cls");
	char data[rows][cols + 1] = { 0 };//加一为了给每一行末尾赋'\0'方便以%s输出
	for (int i = 0; i < rows; i++)//置空格
	{
		memset(data[i], ' ', sizeof(char)*cols);
	}
	for (int i = 0; i < lettersize; i++)//以二维数组的方式输出字符
	{
		if (ar[i].tag == exist)
		{
			if (i < 26)
			{
				data[ar[i].row][ar[i].col] = 'A'+i;//通过下标加减来代替字母显示
			}
			else data[ar[i].row][ar[i].col] = 'a' + (i - 26);
		}
	}
	for (int j = 0; j < rows; j++)//因为上面的二维数组是按照字符串的格式进行赋值,同时每个一维数组的最后为赋值为0,那么就可以使用字符串的格式打印
	{
		printf("%s\n", data[j]);
	}

}

int main()
{
	srand((unsigned)time(NULL));
	char ch;
	let ar[lettersize] = { 0 };
	int size =5;
	int leval = 1;
	int number = 0;
	init_array(ar, size);
	int num = 0;
	int time = 5000;
	while (1)
	{
		show(ar);
		Sleep(time);
		while (kbhit())//kbhit按下返回非0,这里使用while循环调用getch,这样在多次输入错误答案情况下,可以在循环中快速排除
		{
			ch = getch();
			if (isalpha(ch))
			{
				int index;
				if (isupper(ch))
				{
					index = ch - 'A' ;
				}
				if (islower(ch))
				{
					index = ch - 'a' + 26;
				}

				if (ar[index].tag == exist)//消除所打字符并创建新的字符
				{
					ar[index].tag = none;
					number += 1;
					while (ar[index = rand() % lettersize].tag);// 0 25 防止出现同下标字符,从而将为消灭字符重置
					ar[index].tag = exist;
					ar[index].row = -1;
					ar[index].col = rand() % cols;

					if (number == size*leval)
					{
						number = 0;
						size += 2;	
						leval += 1;
						memset(ar, 0, sizeof(let)*lettersize);
						init_array(ar, size);
						//time -= 300;
					}

				}
			}
		}
		for (int i = 0; i <lettersize; i++)//row+1,造成字符下移的效果
		{
			if (ar[i].tag != none)
			{
				ar[i].row++;
				if (ar[i].row > rows)
				{
					cout << "over" << endl;
					return 0;
				}
			}
		}
	}
	return 0;
}
  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值