4.2 打字操练游戏(随机数与特殊输入)
有了上面的绘造简单动画的手艺,如今来看一下简易的打字操练游戏。
【法式4-2】编写一个简易的打字训练游戏:计算机每次随机生成一个小写英文字符,从屏幕一行的左侧起头逐步地向右挪动,走过的位置逐个显示为-字符。在行走过程中,若是用户准确地键入那个字符,则该字符就停行在当前位置,输出*字符并响铃,暗示用户赢了;若是字符从左侧走到几乎接近于右侧鸿沟时(凡是的字符窗口为80列,在此设定为第75列为右界),用户仍然没有准确键入那个字符,则该字符停行在右界处不动。用户键入ESC键时能够选择完毕游戏。
阐发那个游戏中所需的一些操做,有些用前面的常识已经能够写出来了,有些则需要新的常识才气处置。
起首来看动画输出字符。按照上一节中的绘造简单动画的办法,让字符从左向右运动并留下-陈迹是很容易的,在轮回中可写以下语句:
cout << "\b--" << (char)ch;留意那里因为利用 cout办法,所以不克不及间接输出ch(那样会把它做为整数输出),需要把它强迫转换为char类型再输出。
法式中要求“随机生成一个小写英文字符”。那需要进修利用尺度库中与随机数相关的常识。
1、随机数相关函数计算机法式凡是实现的都是确定性的计算:给一个或者一组初始数据,计算出一批确定的成果。然而计算机应用中有时也需要带有随机性的计算,需要用计算机生成随机数。例如以下场所:
(1)在法式调试中需要利用随机性数据做为试验数据;
(2)用计算机模仿某种现实情况或过程,提醒此中的规律性,利用随机数能够较好地反映客不雅过程的现实情况。
(3)在游戏法式中需要利用随机数展开或控造游戏过程。
计算机现实上无法生成实正的随机数,只能生成所谓的伪随机数:那些数字其实不实正的随机,但具有类似于随机数的统计特征,如平均性、独立性等。若何用计算机生成随机性较好的随机数仍是一个研究问题。最简单的生成办法是定义一种递推关系,通过它生成一个数值序列,设法使那个序列中的数看起来比力具有随机性。
C/C++尺度库供给了根本的随机数相关功用。要想利用它们,C++法式头部应包罗系统文件cstdlib(而C语言中要求包罗头文件stdlib.h),即在文件头部写上如下代码行:
#include <cstdlib>那个头文件里描述了与随机数相关的两个函数:
1、随机数种子函数srand,它的类型特征申明是:
void srand(unsigned seed)那个函数用参数 seed 的值从头设置种子值,即为生成下一个随机数而保留的一个整数值(由它动身递推,获得下一个随机数)。函数类型特征申明中的“void”暗示那个函数没有返回值。默认的初始种子值是1。用户能够利用如下语句指定另一个种子值(例如,10):
srand(10);当然,若是每次都利用一个固定的种子数,那么屡次运行法式时所生成的随机数序列老是一模一样的。为此,一个常用的技巧是利用尺度库中的时间函数time()获得从威林威治时间1970年1月1日零时整起头计时所颠末的时间秒数,把它视为一个整数,并用做为种子数。time()函数能够利用比力复杂的实参,在简单利用时,凡是以0做为实参。因而,法式中凡是如许写:
srand(time(0));从而能够让屡次运行法式时所生成的随机数序列差别。
而为了利用时间函数time(),需要包罗尺度库头文件ctime或time.h。为此,C++源法式头部需要写上如下代码行:
#include <ctime>2、随机数生成函数rand。它的类型特征申明是:
int rand(void)那是一个无参函数,每次挪用将得到一个新随机整数,其值在0和系统定义的符号常量RAND_MAX (其值随系统而差别)之间。例如,在定义了整型变量k之后,可用如下语句挪用该函数:
k = rand();用户可能现实需要生成的随机数处于一个自定义的范畴,则需要对由rand()产生的随机数停止转换。若是希望产生的随机数为整数,且处于整数区间[min, max](满足max - min ≤ RAND_MAX),则能够利用如下语句:
k = rand() % (max + 1 - min) + min;若是希望产生的随机数为小数,且处于实数区间[dmin, dmax](满足max - dmin ≤ RAND_MAX),则能够利用如下语句(假设已定义了double类型的变量x):
x = double(rand()) / RAND_MAX * (dmax - dmin) + dmin;以上介绍了用尺度库函数中的随机函数生成整数和小数。但是怎么生成一个小写英文字符呢?那就需要两点常识:
(1)计算机系统中常用的是ASCII字符集,此中的小写英文字符的编码是97~122(即小写字母a的编码是97,其它字母的编码依次增加,曲到小写字母z的编码为122)。
(2)在C/C++系统中,ASCII字符能够视为范畴在[0, 127]内的数字来利用。
因而,假设法式顶用变量ch存储生成的字符,则可用如下体例实现随机生成一个小写英文字符:
int ch; ch = rand() % (z + 1 - a) + a;请留意此中的变量ch被定义为int类型而非char类型。后面章节中将会解释原因。
2、特殊输入函数kbhit和getch此法式的难点是:若何让法式在主动地以动画效果输出字符的同时,又能承受用户键入字符呢?读者一般都已晓得多种输入操做办法:在C++中凡是用“cin >>”停止流式输入;在C和C++中都能够用尺度库中的格局化输入函数scanf或字符输入函数getchar”。但是那些输入操做都是阻塞式的:法式运行到利用那些办法停止输入的语句时,就会停行下来什么都不做,期待用户停止输入;用户键入的所有字符城市回显在屏幕上;只要等用户按下回车键完成输入之后,法式才会继续往下施行。因而那些输入操做办法不适用于那个游戏法式。
为了实现法式的功用,需要用到两个非尺度库中的函数 kbhit 和 getch。
在Windows平台上发行的C/C++编译器和集成开发情况,凡是都包罗了一些非尺度库(扩大库)函数,此中利用较多的头文件就是 conio.h(其名称是“Console Input/Output,控造台输入输出”的简写),在该头文件中声了然通过控造台停止数据输入和数据输出的函数。若是要利用该头文件中所声明的函数,就需要在法式中包罗该头文件,即在源法式起头处写如下的代码行:
#include <conio.h>kbhit 函数(名字来源于英文“keyboard hit”的缩写)用于非阻塞地响应键盘输入事务。它将查抄当前能否有键盘输入,如有就返回一个非0值,不然返回0。也能够通过该函数趁便获得一个字符,但其实不显示在屏幕上。
getch 函数的功用是从控造台读取一个字符,但其实不显示在屏幕上。它的常见用法有两种:
getch();或
int ch; //或 char ch; ch = getch();前一用法其实不关心用户的现实输入,可用于让法式暂停并期待用户按肆意键后继续施行。后一用法招致法式期待用户按键,并将把现实输入字符所对应的编码赋给 ch,然后继续施行。无论是哪种用法,getch() 施行时城市不断期待曲到用户按下某个键。用户不按键时该函数不会返回,法式也就不会向下施行,所以那个函数也是相当于一个阻塞函数。
在编写字符界面的小游戏时常常需要组合利用 kbhit 和 getch 那两个函数。一种简单的写法是:
if (kbhit()) ch = getch();即在用 kbhit 检测到键盘输入时,就用 getch 读入一个字符。
趁便看一个小例子:若是需要对编号为 0 ~ n-1 的 n 小我停止一次抽奖,就需要在屏幕上持续随机显示编号,按下肆意键时停行。可用如下代码片段实现那一功用:
int n = 1000; srand(time(0)); do { cout << "\b\b\b" << rand() % n; //"\b"是控造输出时向左退回一个字符 } while (!kbhit()); 3、打字操练游戏法式有了上面的阐发,大致地能够写出法式的主体构造如下:
定义所需的变量 while (用户键入字符不是ESC键) { 产生一个新的小写英文字符; for (从右边界起头; 尚未抵达右鸿沟; 变量自增) { 以动画形式向右绘造字符 若是玩家按键 读入字符并停止处置(字符准确则此局赢) } 若是用户键入ESC键则再次确认能否退出 若是已抵达右鸿沟则此局输 } 游戏完毕查阅 ASCII 码表可知,此中编码为 7 的特殊字符代表“响铃”,利用时能够用转换描述的形式写为 \b;输出那个字符就实现响铃操做。需要留意的是,响铃操做耗时较长,法式中只能少量地利用它(例如本法式中是每次打字胜利时响铃一次),不外太频繁地利用它。
而编码为 27 的特殊字符代表 ESC 键,在法式中能够间接用字符值与数值 27 停止比力。。
据此写出完好的源法式如下:
//https://gitee.com/devcpp/cgames anbangli@foxmail.com GNU GPL v3 //cgame4-2(type).cpp 字符横向挪动的打字游戏 #include <iostream> #include <cstdlib> #include <ctime> #include <windows.h> //非尺度库函数 Sleep 所需的头文件 #include <conio.h> //非尺度库函数 kbhit 和 getch 所需的头文件 using namespace std; int main() { const int ESCKEY = 27; //定义整型常量ESCKEY,赋值为ESC键的ASCII编码 int pos, posmax = 72; //当前位置,右鸿沟更大位置 int win = 0, loss = 0; //已赢局数和已输局数 cout << "打字游戏(按回车键起头,游戏过程中按 ESC 完毕)" << endl; getchar(); int ch = 0, key = 0; //随机字符,玩家键入字符 srand(time(0)); //设置随机数种子 while (key != ESCKEY) { //主轮回。key 不是 ESC键时玩游戏 ch = rand() % (z + 1 - a) + a; //产生随机字符 for (pos = 0; pos < posmax && key != 27; pos++) { cout << "\b--" << (char)ch; pos++; Sleep(150); if (kbhit() && (key = getch()) == ch) { win++; cout << "*\a"; //输出 * 并响铃 break; } } if (key == ESCKEY) { //用户键入ESC键,需要确认 cout << "\n完毕游戏吗?(y/n)"; while ((key = tolower(getchar())) != y && ch != n) ; //空轮回体 key = (key == y ? ESCKEY : 0); //若是为y则改为 ESCKEY,用于控造主轮回 } if (pos >= posmax) { //抵达右鸿沟 loss ++; } cout << endl; } cout << "字符操练个数:" << win + loss << " 准确键入个数:" << win << endl; cout << "游戏完毕,谢谢利用" << endl; return 0; }若是用户屡次利用那个打字游戏法式,会觉得那个游戏的根本功用仍是能够的,但是比市场上的其它打字法式比拟,还有所欠缺,还有良多能够改良之处:
(1)如今的字符是从左向右挪动,能不克不及让字符从上往下运动?
(2)每次只能显示一个字符停止训练,能不克不及同时显示多个字符?
(3)每次只是在打字胜利时响铃一次,能不克不及播放其它声音?或者在整个打字游戏过程中都播放音乐?
那些功用都是能够考虑增加的功用。但是需要进修和利用更多的编程技巧。例如要想让字符从上往下运动就需要进修更多的字符输出控造(见下一章中的 gotoxy 函数),要同时显示多个字符则需要在法式中进修利用数组。至于播放布景音乐,我觉得超出了本书涵盖的内容,所以在此不做介绍。
还有,读者在运行那些法式时,可能会觉得法式运行时的控造台窗口中的字符比力小,看起来不敷恬逸。那是因为Windows 系统默认的控造台窗口中的字体是点阵字体,字体大小为16磅。更好是调整一下控造台窗口的字体和字号,使之更合适阅读。
若是读者在利用小龙Dev-C++(可在其主页“https://devcpp.gitee.io”中下载安拆)做为法式开发情况,能够点击菜单“东西”下面的“控造台窗口蓝底白色大字”,控造台窗口的默认参数就马上被修改为蓝底白字,字号为28 磅,以后不断有效,便利阅读。
若是不是利用小龙Dev-C++,则调整控造台窗口默认参数的操做如下:
1、在控造台窗口的题目栏上点击鼠标右键,在弹出菜单中点击“默认值”(如下面左图所示);
2、在弹出的“控造台窗口 属性”对话框中(如下面右边所示),在“字体”选项卡下,在字体列表中选择“新宋体”,并在“大小”列表中选择24或28磅。点击“确定”按钮封闭此对话框。
3、封闭当前控造台窗口。在未来所有的控造台窗口中将会利用调整后的字体和字号。
继续阅读:Anbang24:用C/C++编写小游戏:4.3 字符串动画