一、效果图和源码 

1.先看运行效果图

 2.给出源码。

#include<stdio.h>
#include<time.h>
#include<ctype.h>
#include<windows.h>
#include<conio.h>#define NUM 16 //字串总数(页面能存在的最大字串数) struct Block{char strings[20]; //用于存储字串,20是最大长度 int x; //记录字串的横坐标 int y; //记录字串的纵坐标 int Color[20]; //记录每个字符的颜色
}block[NUM];enum Difficulty_Level{Difficulty = 2, Medium = 3, Easy = 4
} speed; //难度等级,字串移动速度,单位秒,由玩家给出 int n = 0; //通过的字串个数
int k = 0; //一个字串输入正确的字符数
double t1, t2; //t用来计时 ,用时间计时方式进行字串下移 void window(int w, int h); //设置窗口大小 void HideCursor(void); //隐藏光标  void color(int c);void gotoxy(int x, int y);void StartInterface(void);void InitInterface(void);void InitBlock(void);void Startgame(void);void JudgeString(int i); void UpdateBlock(int i);void MoveBlock(void);void OtherChoice(int choice); //暂停,重开,结束游戏的选择 void Gameover(void);int UpdateScore(void);void window(int w,int h) //设置窗口大小
{HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//获取标准输出的句柄,命令行的程序会把字符输出到屏幕上。COORD size = {w, h};//设置窗口的大小。SetConsoleScreenBufferSize(hOut, size);//重新设置缓冲区大小。SMALL_RECT rc = {1,1, w, h};//重置窗口位置和大小。SetConsoleWindowInfo(hOut ,true ,&rc);//重置窗口大小system("cls");//清理屏幕return;
}void HideCursor(void) //隐藏光标
{CONSOLE_CURSOR_INFO curInfo; //定义光标信息的结构体变量curInfo.dwSize = 1;  //如果没赋值的话,隐藏光标无效curInfo.bVisible = FALSE; //将光标设置为不可见HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄SetConsoleCursorInfo(handle, &curInfo); //设置光标信息
}void color(int c)  //  改变字体颜色
{SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);/* 0=黑色                8=灰色  1=蓝色                9=淡蓝色                                      2=绿色                10=淡绿色             3=湖蓝色              11=淡浅绿色    4=红色                12=淡红色       5=紫色                13=淡紫色             6=黄色                14=淡黄色               7=白色                15=亮白色  */
}void gotoxy(int x, int y)  //设置光标位置
{COORD xy;xy.X=x;xy.Y=y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),xy);
}void StartInterface(void) //提供难度选择
{system("cls");system("color 07");n = 0; //重置n值,为使再玩一次时n为0 color(7); gotoxy(30, 12);printf("欢迎来到 “打 字 游 戏 ”!");gotoxy(30, 15);printf("请选择游戏难度:");gotoxy(30, 18);printf("1.简单   2.中等   3.困难");gotoxy(47, 15);char ch = getchar();while(ch != '1' && ch != '2' && ch != '3'){fflush(stdin);gotoxy(47, 15);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(47, 15);color(12);printf("输入不合法,请重新输入!");color(7);Sleep(1000);gotoxy(47, 15);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(47, 15);ch = getchar();   }fflush(stdin);if(ch == '1')speed = Easy;if(ch == '2')speed = Medium;if(ch == '3')speed = Difficulty;InitInterface(); //初始化游戏界面
}void InitInterface(void)
{system("cls"); const int x = 80, y = 30;for(int i = 0; i < x-1; i++){for(int j = 0; j < y; j++){if(i == 0 || i == x-2 || j == 0 || j == y-1 || i == x-30){gotoxy(i, j);printf("■");}}  } for(int i = 50; i < 80; i++){gotoxy(i, 13);printf("■");}color(14);gotoxy(55, 2);printf("通过个数: %d 个", n);gotoxy(55, 4);printf("暂停: 空格");gotoxy(55, 6);printf("暂停后任意键继续");gotoxy(55, 8);printf("重新开始: Tab");gotoxy(55, 10);printf("结束游戏: ESC"); gotoxy(55, 16);printf("游戏说明:");gotoxy(59, 18);printf("在字串下落之前,正");gotoxy(59, 20);printf("确输入字串所有字符");gotoxy(59, 22);printf("可得一分"); gotoxy(55, 25);color(4);if(speed == Easy)printf("当前难度: 简单");else if(speed == Medium)printf("当前难度: 中等");elseprintf("当前难度: 困难");InitBlock(); //初始化字串
}void InitBlock(void)
{srand(unsigned(time(NULL)));const int size = 5; //初始长度为5 for(int i = 0; i < NUM; i++){for(int j = 0; j < size; j++){block[i].strings[j] = 97 + rand() % (122 - 97 + 1);    //  a ——z 的ASSCI序列是 97 ——122block[i].x = 2 + rand() % 43; //x表示字串的初始横坐标 block[i].y = -2*i - 3; //y表示纵坐标block[i].Color[j] = 7; //将所有字符颜色置为 7(白色)  }block[i].strings[size] = '\0'; //构成字符串,才能使用strlen()函数 }Startgame(); //游戏主体函数
}void Startgame(void) //主体函数 (包含主要算法)
{t1 = clock(); //开始计时 while(1){    int t = 25000 * speed; //经测试25000下运行时间大约为1秒 while(--t){if(kbhit() != 0) //敲击了键盘break; } //未敲击键盘时,出循环,t 为 0 if(t != 0) //敲击了键盘,找到要输入的字串,通过首字符确定要输入的字串 {char ch = getch();if(ch == 32 || ch == 9 || ch == 27) //暂停,退出,重开 {OtherChoice(ch);continue;}/*遍历所有的字串,寻找距离底线最近的首字符相符的字串*/int index[NUM], ans, y_max = 0, j = 0; //前两变量用于记录下标 for(int i = 0; i < NUM; i++){if(0 < block[i].y && block[i].y < 30 && block[i].strings[0] == ch){index[j] = i; //记录字串下标  j++;}}int next = 0;while(j) //存在符合条件的首字符时,j不为0 {j--;next = 1; //运行了该while()则必然找到了符合要求的字串,才能进行下一步操作 if(y_max < block[index[j]].y){ans = index[j]; //记录最优下标 y_max = block[index[j]].y;}  }if(next == 1) {k = 1; //正确输入了一个字符 /*先输出第一个字符,再对该字串验证后续输入的正确性*/block[ans].Color[0] = 10; color(block[ans].Color[0]);gotoxy(block[ans].x, block[ans].y);putchar(block[ans].strings[0]);JudgeString(ans); //判断后续字串输入的正确性 }fflush(stdin); // 敲错后刷新缓冲区,防止乱敲键盘,导致程序一直运行上述代码 }t2 = clock();if((t2 - t1) / CLOCKS_PER_SEC > speed) //时间间隔超过 SPEED 秒,字串下移 MoveBlock();}
}void JudgeString(int i) //主体函数 (包含主要算法)
{while(1) { t2 = clock();if((t2 - t1) / CLOCKS_PER_SEC > speed) //超时,字串下移 MoveBlock();int t = 25000 * speed; while(--t){if(kbhit() != 0) //敲击了键盘break; } //未敲击键盘时,出循环后,t 为 0 if(t != 0) //以下为敲击键盘的情况,未敲击时直接继续死循环 {char ch = getch();if(ch == 32 || ch == 9 || ch == 27) //暂停,退出,重开 {OtherChoice(ch);continue;}if(0 < block[i].y && block[i].y < 30 && block[i].strings[k] == ch) //下一个字符输入正确 {block[i].Color[k] = 10; color(block[i].Color[k]);gotoxy(block[i].x + k, block[i].y);putchar(block[i].strings[k]);k++;   }else //输入错误,则该字串重置, 即将整个字串的颜色改为白色 {for(int j = 0; j < k; j++){block[i].Color[j] = 7; color(block[i].Color[j]);gotoxy(block[i].x + j, block[i].y);putchar(block[i].strings[j]);} k = 0; //k也重置, 即正确的字符为0个 break; }int size = strlen(block[i].strings);if(k == size) //完全正确,用空格消除字串 {putchar('\a'); //蜂鸣gotoxy(block[i].x, block[i].y);while(k > 0){putchar(' ');k--; //退出循环后,k恢复到 0 }n++; //正确数加一 color(15); gotoxy(65, 2);printf("%d", n);UpdateBlock(i); //更新该字串 break;} }}
}void MoveBlock(void) //字串下移
{   /*先删除原位置,再进行下移*/ for(int i = 0; i < NUM; i++){int size = strlen(block[i].strings);if(block[i].y > 0)   /* 用空格替代原字串,以实现下次循环时字串的下移 */ {gotoxy(block[i].x, block[i].y);while(size > 0){putchar(' ');size--;}}block[i].y += 2; //下移          }for(int i = 0; i < NUM; i++){if(block[i].y > 30) //字串超过底线,游戏结束 Gameover();if(block[i].y > 0) //字串可以输出 {int size = strlen(block[i].strings);for(int j = 0; j < size; j++){gotoxy(block[i].x + j, block[i].y);color(block[i].Color[j]);putchar(block[i].strings[j]);} }}t1 = clock(); //重新计时
}void UpdateBlock(int i) //更新字串
{int size = 5 + n / 7; //难度系数,每对7个,单个字串的字符数加一 int last_y = 0; //寻找排在最后的字串for(int order = 0; order < NUM; order++)last_y = last_y < block[order].y ? last_y : block[order].y; for(int j = 0; j < size; j++){block[i].Color[j] = 7; //初始颜色为白色 block[i].strings[j] = 97 + rand() % (122 - 97 + 1);    //  a ——z 的ASSCI序列是 97 ——122block[i].x = 2 + rand() % (48 - size); //x表示字串的初始横坐标 block[i].y = last_y - 2; //将更新后的字串排在最后 } block[i].strings[size] = '\0'; //构成字符串,才能使用strlen()函数
}void OtherChoice(int choice)
{switch(choice){case 32: //暂停 system("pause>nul"); //暂停 t1 = clock(); //暂停结束后重新计时 break;case 27: //退出Gameover();break; default: //重新开始StartInterface();}
} void Gameover(void)
{if(kbhit())fflush(stdin);system("cls");gotoxy(28, 14);color(3);printf("游  戏  结  束 !");Sleep(1200);int max = UpdateScore(); //记录最高分,写到文件中,并返回最终的最高分 system("cls");system("color 7C");int i = 0; /* 绘制爱心 */ for (float y = 1.3f; y > -1.3f; y -= 0.1f, i++) {gotoxy(7, i);for (float x = -1.5f; x < 1.5f; x += 0.05f) {float a = x * x + y * y - 1;putchar(a * a * a - x * x * y * y * y <= 0.0f ? '*' : ' ');}putchar('\n');}gotoxy(27, 24);printf("  得   分   : %d  ", n);gotoxy(27, 26);printf("  最   高   分  : %d  ", max);double a1, a2; //计时a1 =  clock();while(1) //使爱心频闪 {a2 = clock();if((a2 - a1) / CLOCKS_PER_SEC > 1.8) //频闪1.8秒后{gotoxy(29, 28);printf("按任意键继续"); } Sleep(100);system("color 7E");Sleep(100);system("color 7B");Sleep(100);system("color 79");Sleep(100);system("color 7D");Sleep(100);system("color 74");Sleep(100);system("color 76");Sleep(100);system("color 71");Sleep(100);system("color 72");Sleep(100);system("color 74");if(kbhit()){char ch = getch();break;}}gotoxy(29, 28);printf("再来一次?  Y / N : ");char ch = getchar();ch = tolower(ch);while(ch != 'y' && ch != 'n'){fflush(stdin);gotoxy(49, 28);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(49,28);printf("输入不合法,请重新输入!");Sleep(1000);gotoxy(49, 28);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(49, 28);ch = getchar();ch = tolower(ch);  }fflush(stdin);if(ch == 'y')StartInterface();if(ch == 'n')exit(1);
}int UpdateScore(void) //记录最高分,写到文件中,并返回最终的最高分
{int score[3];FILE *fp = fopen("打字游戏最高分记录.txt", "r"); //只读if(fp == NULL) //创建新文件并初始化成绩 {fp = fopen("打字游戏最高分记录.txt", "w+"); //创建文件,可读写,但会清除原文件 for(int i = 0; i < 3; i++)score[i] = 0;} elsefor(int i = 0; i < 3; i++)fscanf(fp, "%d", &score[i]);fclose(fp);fp = fopen("打字游戏最高分记录.txt", "r+"); //读写 if(speed == Easy) //简单{score[0] = score[0] > n ? score[0] : n;  for(int i = 0; i < 3; i++)fprintf(fp, "%d ", score[i]);fputs("\n上面分别为三种难度系数的最高分,不要乱改!!!,若修改后导致程序运行错误\n\
或者最高分为乱码数字,将此.txt文件永久删除即可。", fp);fclose(fp);return score[0];}if(speed == Medium) //中等 {score[1] = score[1] > n ? score[1] : n;   for(int i = 0; i < 3; i++)fprintf(fp, "%d ", score[i]);fputs("\n上面分别为三个难度系数的最高分,不要乱改!!!", fp);fclose(fp);return score[1];}if(speed == Difficulty) //困难 {score[2] = score[2] > n ? score[2] : n; for(int i = 0; i < 3; i++)fprintf(fp, "%d ", score[i]);fputs("\n上面分别为三个难度系数的最高分,不要乱改!!!", fp);fclose(fp);return score[2];}
}int main(void)
{   window(80, 30); //设置窗口尺寸,意思是设置一个x = 80, y = 30尺寸的cmd窗口HideCursor(); //隐藏光标StartInterface(); //询问游戏难度环节 return 0;
}

二、核心算法

由运行效果图可以看出,算法核心在于字串的下落、玩家输入正确性和字串之间的交互,以及两者之间的结合。

1.字串的下落可以用过计时控制,字串下落前,使用t1 = clock()开始计时,完成一些操作后,使用t2 = clock(), if( (t2-t1) / CLOCKS_PER_SEC  > t), 字串下落。意思是如果运行这一些操作后,时间间隔大于t秒,就让字串下落。

2.玩家输入的正确性判断时,读取玩家输入的第一个字符,寻找在游戏窗口中第一个字符符合且距离底线最近的字串,然后单独把这个字串拿出来,判断玩家后续输入的正确性。

3.字串下落和玩家输入的结合则可以用kbhit()函数和getch()函数配合使用,前者判断是否敲击了键盘,后者直接读取敲击的字符而无需按回车键。算法实现:如果玩家敲击了键盘,就读取输入然后寻找匹配的字串,并验证输入的正确性,期间不断计时,根据时间间隔下移字串。

三、代码结构讲解

1.头文件,宏,全局变量

#include<stdio.h>
#include<time.h>
#include<ctype.h>
#include<windows.h>
#include<conio.h>#define NUM 16 //字串总数(页面能存在的最大字串数) struct Block{char strings[20]; //用于存储字串,20是最大长度 int x; //记录字串的横坐标 int y; //记录字串的纵坐标 int Color[20]; //记录每个字符的颜色
}block[NUM];enum Difficulty_Level{Difficulty = 2, Medium = 3, Easy = 4
} speed; //难度等级,字串移动速度,单位秒,由玩家给出 int n = 0; //通过的字串个数
int k = 0; //一个字串输入正确的字符数
double t1, t2; //t用来计时 ,用时间计时方式进行字串下移

(1)头文件

1.<time.h> 中使用了time()来获取随机数,clock()用于计时;

2.<ctype.h> 中使用了tolower(), 将大写字母变成小写;

3.<windows.h> 中使用了控制台句柄来实现改变光标位置和改变颜色,即color()和gotoxy()函数,同时还使用了system()函数中的清屏、改变颜色(前景加背景)、屏幕冻结等功能,使用Sleep()来实现睡眠;

4.<conio.h> 主要用到了getch(),该函数的作用是不按回车直接读取键盘输入,kbhit()用于判断是否敲击键盘。

(2)宏

全局一个宏NUM,表示字串的数量。给定数值16的原因是游戏窗口高度为30,每个字串的间隔为1,为了让字串占满窗口,设置为16。

(3)全局变量

结构体block代表一个字串,总共16个;

枚举speed表示三种难度;

int n表示输入正确的字串数;

int k表示单个字串中,输入正确的字符数;所以 n 和 k 需要一开始就置位0;

double t1, t2 用于计时,此处不能用int类型,因为返回的时间是带小数点的。

2.辅助功能函数:

这四个函数只起辅助作用,会贯穿整个代码,但是其本身没有什么算法。所以后续不会做详细介绍

void window(int w, int h); //设置窗口大小 void HideCursor(void); //隐藏光标  void color(int c); //改变字体颜色 void gotoxy(int x, int y); //设置光标位置 

3.main()函数:

main()函数很简单,在整个代码的最下方。

功能:  设置一个窗口,隐藏光标,和打开整个游戏的入口函数,即StartInterface()函数。

int main(void)
{   window(80, 30); //设置窗口尺寸,意思是设置一个x = 80, y = 30尺寸的cmd窗口 HideCursor(); //隐藏光标StartInterface(); //询问游戏难度环节 return 0;
}

4.void StartInterface(void)  询问游戏难度

使用fflush()(清除缓冲区)优化输入,防止输入错误,和缓冲区残留字符干扰游戏,最后通过InitInitface(),进行游戏界面初始化。初始化比较简单就不单独介绍,直接跳过了。

void StartInterface(void) //提供难度选择
{system("cls"); //清屏system("color 07");  //黑底白字n = 0; //重置n值,为使再玩一次时n为0 color(7); gotoxy(30, 12);printf("欢迎来到 “打 字 游 戏 ”!");gotoxy(30, 15);printf("请选择游戏难度:");gotoxy(30, 18);printf("1.简单   2.中等   3.困难");gotoxy(47, 15);char ch = getchar();while(ch != '1' && ch != '2' && ch != '3'){fflush(stdin);gotoxy(47, 15);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(47, 15);color(12);printf("输入不合法,请重新输入!");color(7);Sleep(1000);gotoxy(47, 15);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(47, 15);ch = getchar();  }fflush(stdin);if(ch == '1')speed = Easy;if(ch == '2')speed = Medium;if(ch == '3')speed = Difficulty;InitInterface(); //初始化游戏界面
}

5.void InitBlock(void)初始化字串

使用srand()用时间作为种子,再通过rand()获取随机数,字串的横坐标为随机数,纵坐标出初始为负数,是因为窗口的纵坐标范围是0——30,并让字串的纵坐标依次排列,最后再初始化字串每个字符的颜色。

注意字串必须构成字符串,因为后续需要使用到strlen()函数。

void InitBlock(void)
{srand(unsigned(time(NULL)));const int size = 5; //初始长度为5 for(int i = 0; i < NUM; i++){for(int j = 0; j < size; j++){block[i].strings[j] = 97 + rand() % (122 - 97 + 1);    //  a ——z 的ASSCI序列是 97 ——122block[i].x = 2 + rand() % 43; //x表示字串的初始横坐标 block[i].y = -2*i - 3; //y表示纵坐标block[i].Color[j] = 7; //将所有字符颜色置为 7(白色)  }block[i].strings[size] = '\0'; //构成字符串,才能使用strlen()函数}Startgame(); //游戏主体函数
}

6. void Startgame(void) 游戏主体函数

1.总体思想:若玩家开始输入,捕获输入内容的第一个字符,然后去寻找第一个字符匹配的字串,若找到了,将这个字串单独拿出来,进行后续输入的判断,没有找到,则什么也不做,并不断重复这些操作直到游戏结束。

2.需要克服的问题: (1)玩家输入不需要按回车结束输入; (2)字串必须定时下落;        (3) 玩家输入不能影响字串的下落。

3.算法实现:clock()函数返回时间; kbhit()函数当敲击键盘时返回非零值; getch()函数直接读取输入不需要回车键;

4.细节优化: 寻找字串时,需优先寻找距离下限最近的,因为当窗口中有两个首字符一样的字串时,玩家更期望先消除更下面的那个。

int t = 25000 * speed; //经测试25000下运行时间大约为1秒 while(--t){if(kbhit() != 0) //敲击了键盘break; } 

使用while(--t)判断 t 遍是否有键盘输入,原因:假设只判断一遍,会立即返回0值(没有敲击键盘)。令t = 25000 * speed, 可以使当没有任何输入时,while(--t)大约运行 speed 秒, 就可以保证没有输入时,每经过 speed 秒字串下移。

void Startgame(void) //主体函数 (包含主要算法)
{t1 = clock(); //开始计时 while(1){    int t = 25000 * speed; //经测试25000下运行时间大约为1秒 while(--t){if(kbhit() != 0) //敲击了键盘break; } //未敲击键盘时,出循环,t 为 0 if(t != 0) //敲击了键盘,找到要输入的字串,通过首字符确定要输入的字串 {char ch = getch();if(ch == 32 || ch == 9 || ch == 27) //暂停,退出,重开 {OtherChoice(ch);continue;}/*遍历所有的字串,寻找距离底线最近的首字符相符的字串*/int index[NUM], ans, y_max = 0, j = 0; //前两变量用于记录下标 for(int i = 0; i < NUM; i++){if(0 < block[i].y && block[i].y < 30 && block[i].strings[0] == ch){index[j] = i; //记录字串下标  j++;}}int next = 0;while(j) //存在符合条件的首字符时,j不为0 {j--;next = 1; //运行了该while()则必然找到了符合要求的字串,才能进行下一步操作 if(y_max < block[index[j]].y){ans = index[j]; //记录最优下标 y_max = block[index[j]].y;}  }if(next == 1) {k = 1; //正确输入了一个字符 /*先输出第一个字符,再对该字串验证后续输入的正确性*/block[ans].Color[0] = 10; color(block[ans].Color[0]);gotoxy(block[ans].x, block[ans].y);putchar(block[ans].strings[0]);JudgeString(ans); //判断后续字串输入的正确性 }fflush(stdin); // 敲错后刷新缓冲区,防止乱敲键盘,导致程序一直运行上述代码 }t2 = clock();if((t2 - t1) / CLOCKS_PER_SEC > speed) //时间间隔超过 SPEED 秒,字串下移 MoveBlock();}
}

7. void JudgeString(int i)  第二个主体函数

        1.总体思想:在上一个函数中找到了第一个首字符匹配的字串,这个函数则将这个字串单独拿出来,继续判断后续输入的正确性。若后续输入完全正确,就消除该字串(空格覆盖),若出现错误,需要从第一个字符重新输入(从头重新输入),或者选择消除其他的字串。

2.算法实现:给出死循环,获取玩家输入并做出判断,全对或者出现错误,将字串颜色恢复为白色并退出死循环返回上一层函数。函数声明中的 int i 表示第 i 个字串。

void JudgeString(int i) //主体函数 (包含主要算法)
{while(1) { t2 = clock();if((t2 - t1) / CLOCKS_PER_SEC > speed) //超时,字串下移 MoveBlock();int t = 25000 * speed; while(--t){if(kbhit() != 0) //敲击了键盘break; } //未敲击键盘时,出循环后,t 为 0 if(t != 0) //以下为敲击键盘的情况,未敲击时直接继续死循环 {char ch = getch();if(ch == 32 || ch == 9 || ch == 27) //暂停,退出,重开 {OtherChoice(ch);continue;}if(0 < block[i].y && block[i].y < 30 && block[i].strings[k] == ch) //下一个字符输入正确 {block[i].Color[k] = 10; color(block[i].Color[k]);gotoxy(block[i].x + k, block[i].y);putchar(block[i].strings[k]);k++;   }else //输入错误,则该字串重置, 即将整个字串的颜色改为白色 {for(int j = 0; j < k; j++){block[i].Color[j] = 7; color(block[i].Color[j]);gotoxy(block[i].x + j, block[i].y);putchar(block[i].strings[j]);} k = 0; //k也重置, 即正确的字符为0个 break; }int size = strlen(block[i].strings);if(k == size) //完全正确,用空格消除字串 {putchar('\a'); //蜂鸣gotoxy(block[i].x, block[i].y);while(k > 0){putchar(' ');k--; //退出循环后,k恢复到 0 }n++; //正确数加一 color(15); gotoxy(65, 2);printf("%d", n);UpdateBlock(i); //更新该字串 break;} }}
}

8.void MoveBlock(void) 字串下移 和 void UpdateBlock(int i)更新字串

1.字串下移采用先删除原字串,再在新位置输出字串。删除使用空格覆盖,新位置则在空格覆盖完后,所有字串y轴 + 2的方式。每次移动完一次字串重新计时。

2.更新字串时先遍历一边所有字串,找到排在最末尾的那个字串,然后更新的字串就放在最末尾的后面,即last_y - 2,其他跟初始化字串一样。

void MoveBlock(void) //字串下移
{   /*先删除原位置,再进行下移*/ for(int i = 0; i < NUM; i++){int size = strlen(block[i].strings);if(block[i].y > 0)   /* 用空格替代原字串,以实现下次循环时字串的下移 */ {gotoxy(block[i].x, block[i].y);while(size > 0){putchar(' ');size--;}}block[i].y += 2; //下移          }for(int i = 0; i < NUM; i++){if(block[i].y > 30) //字串超过底线,游戏结束 Gameover();if(block[i].y > 0) //字串可以输出 {int size = strlen(block[i].strings);for(int j = 0; j < size; j++){gotoxy(block[i].x + j, block[i].y);color(block[i].Color[j]);putchar(block[i].strings[j]);} }}t1 = clock(); //重新计时
}void UpdateBlock(int i)
{int size = 5 + n / 7; //难度系数,每对7个,单个字串的字符数加一 int last_y = 0; //寻找排在最后的字串for(int order = 0; order < NUM; order++)last_y = last_y < block[order].y ? last_y : block[order].y; for(int j = 0; j < size; j++){block[i].Color[j] = 7; //初始颜色为白色 block[i].strings[j] = 97 + rand() % (122 - 97 + 1);    //  a ——z 的ASSCI序列是 97 ——122block[i].x = 2 + rand() % (48 - size); //x表示字串的初始横坐标 block[i].y = last_y - 2; //将更新后的字串排在最后 } block[i].strings[size] = '\0'; //构成字符串,才能使用strlen()函数
}

9. void OtherChoice(int choice) 其他选择(暂停、结束游戏、重新开始)

代码中 sysytem("pause") ;的效果如下:

system("pause>nul") 可以消除 “请按任意键继续.  .  .” 这几个字。

void OtherChoice(int choice)
{switch(choice){case 32: //暂停 system("pause>nul"); //暂停 t1 = clock(); //暂停结束后重新计时 break;case 27: //退出Gameover();break; default: //重新开始StartInterface();}
} 

10. void Gameover(void) 游戏结束

            游戏结束后需要更新一下最高分记录,然后可以整一些花里胡哨的东西来美化一下界面。在下面代码中使用了绘制爱心并使爱心频闪的方式。

右边是爱心的数学函数公式,其算法实现是构造一个矩形,从上至下,从左至右,在该数学函数公式范围内的输出 ‘ * ’, 否则输出空格。

让爱心频闪的算法是,使用 Sleep(100), 每睡眠100 ms,更换一次字体颜色。

void Gameover(void)
{if(kbhit())fflush(stdin);system("cls");gotoxy(28, 14);color(3);printf("游  戏  结  束 !");Sleep(1200);int max = UpdateScore(); //记录最高分,写到文件中,并返回最终的最高分 system("cls");system("color 7C");int i = 0; /* 绘制爱心 */ for (float y = 1.3f; y > -1.3f; y -= 0.1f, i++) {gotoxy(7, i);for (float x = -1.5f; x < 1.5f; x += 0.05f) {float a = x * x + y * y - 1;putchar(a * a * a - x * x * y * y * y <= 0.0f ? '*' : ' ');}putchar('\n');}gotoxy(27, 24);printf("  得   分   : %d  ", n);gotoxy(27, 26);printf("  最   高   分  : %d  ", max);double a1, a2; //计时a1 =  clock();while(1) //使爱心频闪 {a2 = clock();if((a2 - a1) / CLOCKS_PER_SEC > 1.8) //频闪1.8秒后{gotoxy(29, 28);printf("按任意键继续"); } Sleep(100);system("color 7E");Sleep(100);system("color 7B");Sleep(100);system("color 79");Sleep(100);system("color 7D");Sleep(100);system("color 74");Sleep(100);system("color 76");Sleep(100);system("color 71");Sleep(100);system("color 72");Sleep(100);system("color 74");if(kbhit()){char ch = getch();break;}}gotoxy(29, 28);printf("再来一次?  Y / N : ");char ch = getchar();ch = tolower(ch);while(ch != 'y' && ch != 'n'){fflush(stdin);gotoxy(49, 28);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(49,28);printf("输入不合法,请重新输入!");Sleep(1000);gotoxy(49, 28);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(49, 28);ch = getchar();ch = tolower(ch);  }fflush(stdin);if(ch == 'y')StartInterface();if(ch == 'n')exit(1);
}

11. int UpdateScore(void)  记录最高分,写到文件中,并返回最终的最高分 

此函数主要用到了打开文件的一些方法,下面罗列了打开文件的各种方式,就不多加赘述了

int UpdateScore(void) //记录最高分,写到文件中,并返回最终的最高分
{int score[3];FILE *fp = fopen("打字游戏最高分记录.txt", "r"); //只读if(fp == NULL) //创建新文件并初始化成绩 {fp = fopen("打字游戏最高分记录.txt", "w+"); //创建文件,可读写,但会清除原文件 for(int i = 0; i < 3; i++)score[i] = 0;} elsefor(int i = 0; i < 3; i++)fscanf(fp, "%d", &score[i]);fclose(fp);fp = fopen("打字游戏最高分记录.txt", "r+"); //读写 if(speed == Easy) //简单{score[0] = score[0] > n ? score[0] : n;  for(int i = 0; i < 3; i++)fprintf(fp, "%d ", score[i]);fputs("\n上面分别为三种难度系数的最高分,不要乱改!!!,若修改后导致程序运行错误\n\
或者最高分为乱码数字,将此.txt文件永久删除即可。", fp);fclose(fp);return score[0];}if(speed == Medium) //中等 {score[1] = score[1] > n ? score[1] : n;   for(int i = 0; i < 3; i++)fprintf(fp, "%d ", score[i]);fputs("\n上面分别为三个难度系数的最高分,不要乱改!!!", fp);fclose(fp);return score[1];}if(speed == Difficulty) //困难 {score[2] = score[2] > n ? score[2] : n; for(int i = 0; i < 3; i++)fprintf(fp, "%d ", score[i]);fputs("\n上面分别为三个难度系数的最高分,不要乱改!!!", fp);fclose(fp);return score[2];}
}

C语言实现简单打字游戏相关推荐

  1. 用c语言做一个五子棋程序,C语言制作简单五子棋游戏

    原标题:C语言制作简单五子棋游戏 C语言制作简单的五子棋游戏 学习C语言的人很多,但是用C语言很少,而用来为自己所用,来做游戏的人就更少了,很多人都是跟着学校学习,学校讲到哪就坐到哪,但是以后却还是不 ...

  2. 弹球小程序怎么用c语言编写,C语言实现简单弹球游戏

    电视机待机的屏幕上的弹球,怎么实现? 今天文章就跟大家分享下C语言实现简单弹球游戏的具体代码,供大家参考,具体内容如下 #include #include #include #include #inc ...

  3. 基于C语言的简单飞机游戏

    最近在B站上看到一位老师用VC++6.0编写游戏的教程,自己尝试了一下十分有意思,这里是链接:[C语言/EasyX]做游戏,学编程 C语言/EasyX游戏开发[自取课程资料,评论区置顶]_哔哩哔哩_b ...

  4. Pygame 简单打字游戏

    功能描述: 1.点击开始游戏,会出现一段英文文章,并进入60s倒计时 2.如果一分钟内输入完成这段会自动呈现下一段 3.单词正确数实时统计,背景颜色随输入速度而变化 代码: 注意:上面动态图需要放在游 ...

  5. html简单打字游戏,javascript实现简单打字游戏

    本文实例为大家分享了javascript打字游戏的具体代码,供大家参考,具体内容如下 传智打字游戏 .label{ position:absolute;left: 0px; } var CODE = ...

  6. html简单打字游戏,javascript实现的简单打字游戏

    传智打字游戏 .label{ position:absolute;left: 0px; } var CODE = "QWERTYUIOPASDFGHJKLZXCVBNM"; var ...

  7. Java 实现简单打字游戏

    打字游戏 题目: 代码实现: 题目: 编写一个打字游戏,从一个面板顶端随机出现一些字母,字母按一定的时间自顶向下移动,如果字母接触到面板底端则Game Over,用户可以通过敲击键盘消除移动中的字母. ...

  8. 一个简单打字游戏的设计(C语言)

    需求: 生成的20个随机字符串由大小写组成,规则如下: 程序源码: #include<stdio.h> #include<stdlib.h> #include<time. ...

  9. 简单五子棋游戏c语言简单,C语言制作简单五子棋游戏

    #pragma comment(lib,'winmm.lib') //玩游戏 void PlayGame() { //鼠标操作 int chess[N][N] = { 0 };//标志没有棋子的标志 ...

  10. 如何编辑简单打字游戏

    先来简单看一下要求 一,我们可以先编写Player类,先把属性定义好,然后进行封装 public class Player {private String name; //定义姓名private in ...

最新文章

  1. 为什么我喜欢EJB 3.0并且尤其喜欢EJB 3.1
  2. CodeForces - 1327D Infinite Path(图论综合)
  3. 通杀IIS7.0畸形解析0day漏洞
  4. linux系统内存缓冲和磁盘预留
  5. CI框架 -- URL
  6. UnityShader10:CG标准函数库
  7. 5个开发人员不应该错过的最好跨平台PHP编辑器
  8. C#中在窗体间使用消息来处理相关联的事件
  9. ArcGIS相关学习视频链接
  10. js获取浏览器信息及版本(兼容IE)
  11. java zip 中文文件名乱码_java使用zip压缩中文文件名乱码的解决办法
  12. 机器学习算法对比分析(转载)
  13. 看了B站上的这些Java视频,我飘了!
  14. 《大象 Thinking in UML》学习笔记(三)——UML核心元素之参与者、用例
  15. 百度网盘微信小程序文件 同步到百度网盘APP或客户端
  16. 每日LeetCode一道题————有效的数独
  17. 京东股权众筹投后总结和反思2(冲动不要紧,有后悔药)
  18. 精美卡通儿童教育班会课件PPT模板
  19. 进入知识储备期的通知
  20. 怎么查看linux有多少内存插槽,linux 查看内存插槽数、最大容量和频率

热门文章

  1. www.idcnd.net传媒官方客服提供
  2. android手机脱网分析,网络营销-13款手机浏览器分析(Android).pptx
  3. 嵌入式linux下的触屏模拟
  4. [转载]STED和STORM、PALM
  5. EclipseMaven导入Maven项目后在pom.xml出现Missing artifact org.springframework:spring-jdbc:jar:3.2.4.RELEAS
  6. Unity3d常用快捷键
  7. [TJOI2019]唱、跳、rap和篮球_生成函数_容斥原理_ntt
  8. 磁盘基础知识-磁盘的构成
  9. 所谓的飞扬档案管理软件
  10. 面试之防火墙软硬件架构