#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<conio.h> //knhit()的头文件
#include<graphics.h>
#define _CRT_SECURE_NO_WARNINGS
//设计数据;设计窗口属性
const int WIDTH = 640; //640
const int HEIGHT = 500;//500
//正确率和错误率
int right=0;
int error=0;
//写一个结构体,保存下落的文字信息(坐标+保存文字的指针)
struct TARGET
{
int x, y;
char* str;
};
//写一个结构体,保存用户键盘输入的信息
struct USRKEY
{
int x, y;
char str[20];
}userkey = { 320,500 - 30,"" }; //这是给结构体里面的元素初始化值,是一种格式
//在指定位置输出整数
void outtextxy_int(int x,int y,char* format,int num) //char* format固定的表示格式%xxxx
{
char str[20] = "";
sprintf(str, format, num);
outtextxy(x, y, str);
}
//在指定位置输出浮点数
void outtextxy_double(double x, double y, char* format, double num)
{
char str[20] = "";
sprintf(str, format, num);
outtextxy(x, y, str);
}
void divwindow() //写个函数,来切割窗口成几块
{
setlinecolor(RED);
line(WIDTH-100, 0, WIDTH-100, HEIGHT-40);
line(0,HEIGHT-40, WIDTH + 50, HEIGHT-40);
line(WIDTH - 100, 130, WIDTH + 50, 130);
}
//通过结构体随机产生下落的字符,通过随机产生数组下标方式产生,把字符串存到结构体的二维数组里
void initTarget(struct TARGET words[],int n) // word[],即结构体数组
{
static char str[43][42] = { "main","int","extern","static","volatile","initgraph","line","int","char","long","double",
"float","struct","union","outtextxy","sprintf","graphics","NOR","XOR","NAND","AND","OR","NOT","return","include","while","for","switch",
"case","break","goto","if","enum","private","public","class","default","else" ,"%p","%s","%c","%d","typedef"};
//随机生成字符下标(取余算法)
words[n].str = str[rand() % 43];
//不断检测重复,如果重复,则重新生成
while (words[n].str == words[(n + 1) % 3].str || words[n].str == words[(n + 2) % 3].str)
{
words[n].str = str[rand() % 37]; // rand()函数对一个数字取余,是算法:产生XX以内的数
}
words[n].x = rand() % (WIDTH - 200);
words[n].y = -20;
}
void drawScore()
{
settextcolor(YELLOW);//设置字体颜色
settextstyle(25, 0, "STXINGKA");//设置字体
outtextxy(WIDTH - 90, 25, "鄂学谦");
outtextxy(WIDTH - 90, 25+25, "打字练习器");
outtextxy(WIDTH - 90, 225, "正确");
outtextxy_int(WIDTH - 90, 225 + 25, "%d",right); //调用之前封装的打印整形的函数outtextxy_int,安装%d的格式
outtextxy(WIDTH - 90, 285, "错误");
outtextxy_int(WIDTH - 90, 285 + 25, "%d", error);
outtextxy(WIDTH - 90, 285+285-225, "正确率");
if (right + error == 0)
{
outtextxy_double(WIDTH - 90, 285 + 285 - 225, "%.2lf%%",0.00);
}
else
{ //c语言 除法会取整
double sum = right + error;
outtextxy_double(WIDTH - 90, 285 + 285 - 225, "%.2lf%%", right / sum * 100);
}
}
int main()
{
srand((unsigned int)time(NULL));//初始化随机函数,即清空里面的历史值
initgraph(WIDTH+50, HEIGHT); //1.创建一个可视化窗口,并设置窗口的属性和坐标
struct TARGET words[3]; //一次生成3个下落的字符
for (int n = 0; n < 3; n++)
{
initTarget(words,n);
words[n].y = -15 - n * 30;//形成不等高
}
BeginBatchDraw();//开始双缓存绘图,先把图画好了再显示出来,不会一闪一闪的
int i = 0;//定义一个计数的数,需要初始化从0开始计
while (1)
{
cleardevice();//清空图形界面的函数
divwindow();
for (int n = 0; n < 3; n++) //碰线处理
{
words[n].y += 2;//往下落,关于下落速度
if (words[n].y > (HEIGHT - 40 - textheight(words[n].str)))//如果碰线,其中 textheight是当前文字高度
{
initTarget(words, n);//消失就是再初始化一下
}
}
for (int n = 0; n < 3; n++)//文字往下落,打印文字
{
settextcolor(RED);
outtextxy(words[n].x, words[n].y, words[n].str);
}
if (kbhit())//kbhit()函数,如果有按键输入则返回非0,否则返回0
{
char target;//如果有按键输入,定义一个target接受用户的值,将字符串变成字符处理
if ((target = getch()) != '\r') //如果用户输入不为换行符
{
userkey.str[i++] = target;//把用户输入保存到用户输入的字符串数组userkey里面去,target在此表示当前字符
// userkey.x += 5; 如果加上这个,用户输入会一直往右跑
}
else //if和else之后的语句,每次只会执行一个;这里else里的语句,就是当输入是为换行符\r的时候,就会执行以下,即输入换行的时候,开始第二次输入
{
int flagError = 0;//"加标记"的的思想,起开关的作用,如果输入错误,error加1
for (i = 0; i < 3; i++)//输入换行,即确认后,判断输入是否正确,正确则消除
{
if (strcmp(userkey.str, words[i].str) == 0)
{
initTarget(words, i);
right++;
flagError = 1;//输入正确的时候,错误标记置为1
}
}
if (flagError == 0) //标记对应的动作实现,如果对不上,error++
{
error++;
}
i = 0; //第二次输入的时候,把i置0,否则会一直堆积
userkey.x = 320; //把用户输入的显示坐标还原为320,而不是一直往后堆积跑
memset(userkey.str, 0, 20); //防止用户输入堆积,重置输入的字符串
}
}
outtextxy(userkey.x, userkey.y, userkey.str);//没有这个,用户输入显示不出
drawScore();
FlushBatchDraw();//easyx里面的,双缓存绘图,批量绘图;从BeginBatchDraw()开始,直到这里才会把之前所有的绘图内容显示出来,期间不会显示绘图语句,用于防止程序闪屏
Sleep(50);//文字下落的速度,并且防止闪退
}
getchar();
closegraph();
return 0;
}