标签:猜单词 c++ 程序员联盟 游戏 programmerleague
1、第一部分第十一课:小游戏,猜单词
2、第一部分第十二课预告:指针一出,谁与争锋
上一课《【C++探索之旅】第一部分第十课:文件读写,海阔凭鱼跃》中我们学习了读写文件的知识。
第一部分的课程也快接近尾声了,下一课我们会学习很重要的指针的知识。之后,我们就进入第二部分:面向对象编程了。哈哈哈,激动不?
目前为止,我们也学了不少内容:编译基本原理,IDE,变量,函数,条件语句,循环语句,引用,数组,文件读写,等等。你应该为自己感到自豪。
俗语说得好:实践是最好的老师。我们学了那么多知识点,却还没有综合地来应用一下,怎么行呢?
这一课我们就用一个小游戏来锻炼吧。
游戏规则很简单:我们输入一个单词,然后把单词里的字母打乱,显示给用户看,让用户猜这个单词是什么。
准备工作和建议
这个小游戏看似简单,但对于初学者的话还是有点难度的,因为需要用到我们前几课的一些知识点,例如:
变量
string(字符串)
函数
控制流程,条件语句
数组
如果有知识点生疏了,可以在编写这个游戏前回去复习。
[猜数字]游戏的主旨
我们要写的小游戏的流程是这样:
玩家1输入一个单词
程序将单词打乱
玩家2尝试从打乱的单词猜出原始的单词是什么
下面是一个玩此游戏的一轮的例子:
输入一个单词 MYSTERY 原先的单词是什么 ? MSYRETY RYTEMSY 猜错了! 原先的单词是什么 ? MSYRETY SERMYTY 猜错了! 原先的单词是什么 ? MSYRETY MYSTERY 猜对了! 好棒!
分析以上流程:
在这一轮中,玩家1选择输入MYSTERY(英语"神秘,秘密"的意思)作为要猜的单词给玩家2来猜。
程序将MYSTERY这个单词的字母顺序打乱,然后请玩家2来猜打乱前的原始单词是什么。
玩家2尝试猜测,他猜了3次才猜中:
RYTEMSY:显示"猜错了!"
SERMYTY:还是没猜对
MYSTERY:终于猜对了,祝贺!
当然了,在上面演示的一轮游戏的例子中,玩家2可以看到玩家1输入的原始单词,暂时先这样。到这课最后的改进部分我们会说怎么避免。当然了,聪慧如你也许早就想到好几个办法了。
开始前的一些建议
当我们让一个初学编程的朋友放手一搏,第一次自己写一个比较完整的程序时,是有难度的。
小编也是过来人,以前还没入门编程时感受颇深:看书,照着书上例子敲代码,好像都懂啊。可是一上手自己写比较复杂的代码,就愣住了,不知该从何下手。
"从哪里开始写呢?","我该做什么呢?","我该用什么呢?",等等,都是初学的症候。不过谁都得过了这一阶段,才能体验到美好的入门感觉。
因此,绝对不要对自己灰心!编程么,就是用一种新的思路来解决问题,只要你经过了编程思想的锤炼,你会发现其他很多事情都会显得简单了。
不过,善良如小编,还是不希望你自己过于迷失。毕竟只要稍加提示,你的思路就会更开阔啦。当然了,也许不必提示,你已经把程序写出来了吧。
找到头绪
这个程序的编写其实只有三步:
输入原始单词;
打乱单词的字母顺序;
做循环:请求玩家2输入猜测的单词。只要玩家2没猜到单词,循环就继续进行。
其实以上的三个步骤是相对独立的,因此,与其一开始就很头大地想要一下子把整个程序写出来,为什么不把程序分成三个部分一点点写出来呢?
列出代码的大纲
一开始,肯定是先写main函数啦,毕竟是程序的入口嘛。我们可以先把程序写成这样:
int main() { //1 : 请求用户输入单词 //2 : 打乱输入的单词 //3 : 请求用户猜单词 return 0; }
我们要做的,只是分别用代码实现注释里的这三步而已。看吧,写代码之前先思考,然后做好规划,对于写出条理分明的代码何等重要。
不过我建议大家先实现第1步,接着实现第3步,最后实现最难的第2步。
如何打乱单词呢?
打乱单词的这一步是最难的。需要学习一些基础知识:
抽取随机数
为了做到单词的打乱是随机的,我们需要随机地抽取单词中的字母,那怎么达成这个目的呢?臣妾做不到啊。
不要紧,我们来学习随机数的生成。
首先,须要引入两个头文件:ctime和cstdlib,因为我们要用到这两个头文件中的函数。
然后,我们需要调用函数srand(time(0))来初始化随机数的"种子"(seed)。
然后,一旦"种子"准备好了,就可以用rand函数来生成随机数了,例如:randomNb = rand() % 5; (5就限定了生成的随机数是介于0到4之间)。
srand其实是seed random的缩写,seed在英语中是“种子”的意思。
补充一些摘自百度百科的概念:
========================
【srand 和rand配合使用产生伪随机数序列。rand函数在产生随机数前,需要系统提供的生成伪随机数序列的种子,rand根据这个种子的值产生一系列随机数。 如果系统提供的种子没有变化,每次调用rand函数生成的伪随机数序列都是一样的。srand(unsigned seed)通过参数seed改变系统提供的种子值,从而可以使得每次调用rand函数生成的伪随机数序列不同,从而实现真正意义上的“随机”。通常可以利 用系统时间来改变系统的种子值,即srand(time(NULL)),可以为rand函数提供不同的种子值,进而产生不同的随机数序列】
【所谓的“伪随机数”指的并不是假的随机数,这里的“伪”是有规律的意思。其实绝对的随机数只是一种理想状态的随机数,计算机只能生成相对的随机数即伪随机 数。计算机生成的伪随机数既是随机的又是有规律的 —— 一部份遵守一定的规律,一部份则不遵守任何规律。比如“世上没有两片形状完全相同的树叶”,这正点到了事物的特性 —— 规律性;但是每种树的叶子都有近似的形状,这正是事物的共性 —— 规律性。从这个角度讲,我们就可以接受这样的事实了:计算机只能产生伪随机数而不是绝对的随机数。】
【通过time()函数来获得计算机系统当前的日历时间(Calendar Time),处理日期时间的函数都是以本函数的返回值为基础进行运算。其原型为:time_t time(time_t * t); 如果你已经声明了参数t,你可以从参数t返回现在的日历时间,同时也可以通过返回值返回现在的日历时间,即从一个时间点(例如:1970年1月1日0时0 分0秒)到现在此时的秒数。如果参数为空(NULL或0),函数将只通过返回值返回现在的日历时间。】
========================
如 果我们在使用rand函数前没有用srand函数制定seed的值,或者虽然用了srand函数,但是给它的参数是一个常量,比如srand(1),那么 每次程序运行rand产生的数字都是一样的。只有用例如time()函数来给一个每次都不一样的seed值,才能使得rand的返回值不一样,才能做到 “随机”。
srand函数只需要在rand函数前面调用一次就够了,也只能调用一次,之后你想要调用rand函数几次都无所谓,但是每个程序中不能用两次srand函数,切记。
下面演示一个程序,它会随机生成一个整数,是介于0到4之间的:
#include <iostream> #include <ctime> // 必须的头文件 #include <cstdlib> // 必须的头文件 using namespace std; int main() { int randomNb(0); srand(time(0)); randomNb = rand() % 5; return 0; }
随机抽取单词中的字母
随机生成一个数字是很不错啊,但是我们的程序是要随机挑选单词里的一个字母呢!
那还不是一样的原理。
我们要随机抽取单词中的字母,只要用下标就可以了。还记得之前的课里提到string很类似数组吗?因此,只要 数组名[下标] 的形式,下标随机就可以了。例如secretWord[2]就是单词的第三个字母。
一旦我们知道如何生成随机数了,那么只要规定生成的随机数的范围就可以啦,范围就是:0~单词中字母数减1
例如:
#include <iostream> #include <string> #include <ctime> #include <cstdlib> using namespace std; int main() { string secretWord("MYSTERY"); srand(time(0)); int position = rand() % secretWord.size(); cout << "随机抽取的字母是: " << secretWord[position]; return 0; }
从字符串中删除一个字符
那么我们如何构建新的打乱的单词呢?
我们可以用一个循环,从原始单词中每次随机抽取一个字符,然后添加到新的打乱的单词中。
为了避免重复处理同一个字母,我们须要每次将抽取的字母从原始单词中删去。这样,到最后,原始单词的所有字母就会都"跑到"打乱的单词中了。为了从字符串中删除字符,我们可以使用erase这个方法:
secretWord.erase(4, 1); // 删除原始单词中的第五个字母
erase函数有两个参数:
第一个参数表明从哪个字母开始删除:上例中是4,就表明是从第五个元素开始删除。毕竟数组是从0开始作为第一个下标的嘛。
第二个参数表明删除几个字符:上例中是1,因此只删除从第五个字符开始的1个字符,也就是只删除第五个字符。
创建函数
这一整个程序其实并没有那么复杂,不一定要创建新的函数。但是适时地使用函数绝对是好习惯。在我们的程序中,第二步的主要工作就可以交给一个自己创建的函数来做。
这样,我们就可以在main函数中调用这个函数,如下:
mixedWord = mixLetters(secretWord);
我们将原始的单词secretWord作为函数的参数传递给它,而函数的返回值则赋给打乱的单词mixedWord。
好了,提示就到这里了。
少年,放手一搏吧!
答案
相信你花了一些时间来思考,并且写了程序。第一次你不一定都知道怎么写,但是没关系,重点是思考和尝试的过程。如果不思考直接来看答案,或尝试了一下就放弃,那编程可是学不好的哦。
上面我们已经提过,此游戏的代码可以分为三个部分来实现。第1步和第3步相对容易,只有第2步有些难度。
放上小编自己写的版本。当然了,你可以写得比我更好。我这个版本还是很基础的。
翠花,上代码:
=========================
#include <iostream> #include <string> #include <ctime> #include <cstdlib> using namespace std; //用于打乱单词的函数 string mixLetters(string word) { string mixedWord; int position(0); //只要还没有抽完单词里的所有字母,就继续while循环 while (word.size() != 0) { //在单词中随机选择一个字母 position = rand() % word.size(); //将此字母添加到新的单词mixedWord中,也就是我们最后得到的打乱的单词 mixedWord += word[position]; //将此字母从字符串中删去 //为了不重复处理同一个字母 word.erase(position, 1); } //返回打乱后的单词 return mixedWord; } int main() { string secretWord, mixedWord, userEnteredWord; //初始化伪随机数的种子 srand(time(0)); //1 : 请求玩家1输入原始单词 cout << "输入一个单词" << endl; cin >> secretWord; //2 : 用函数mixLetters将玩家1输入的原始单词打乱顺序 mixedWord = mixLetters(secretWord); //3 : 请求玩家2猜单词 do { cout << endl << "原先的单词是什么 ? " << mixedWord << endl; cin >> userEnteredWord; if (userEnteredWord == secretWord) { cout << "猜对了! 好棒!" << endl; } else { cout << "猜错了!" << endl; } } while (userEnteredWord != secretWord);//只要没猜对,就再进入while循环 return 0; }
=========================
不要惊讶于此代码的短小。也许只是你没想到,看了代码才发现,啊,原来这么简单,原来可以这么写。
下面对程序做分步解释吧:
第一步:输入单词
这一步是最简单的。cout来输出一行提示消息。cin来获取用户输入,存放到secretWord变量中。
第二步:打乱单词
这一步是用一个函数mixLetters来实现的。
while (word.size() != 0) { position = rand() % word.size(); mixedWord += word[position]; word.erase(position, 1); }
每一次进入while循环体中,我们都随机挑选单词中的一个字母,然后将其放入新的打乱的单词mixedWord中。最后将这个字母从原始的单词中删去。
一旦处理完原始单词中所有的字母(也就是原始的单词为空了),我们就跳出循环,函数会返回打乱后的单词。
第三步:问用户秘密单词是什么
这一步我们将使用一个do... while循环,以保证我们给用户至少一次猜单词的机会。
用cin来获取用户的输入,然后与secretWord这个原始的单词做比较,一旦两者一致,则获胜;否则就继续循环。
}while (userEnteredWord != secretWord); //只要没猜对,就再进入while循环
每次都显示一条消息:假如没猜对,则显示"猜错了";假如猜到了,则显示"猜对了!好棒!"
改进游戏
好了,我们的程序写完了,不过始终可以改进。我们给出一些改进的建议:
在用户1输入单词之后,想办法"隐藏"单词,不让用户2看到:可以使用加多个空行(endl);也可以有其他办法。
使用户可以一直玩:目前我们的游戏一轮就结束了。你可以在每一轮结束后显示一行信息:"您还想继续新一轮游戏吗?(y/n)"。如果用户输入y,回车,则继续新的一轮;如果用户输入n,回车,则退出游戏。
设置猜测次数上限:比如设置5次作为猜测的机会上限,那么如果用户5次没猜中单词的话,游戏也失败。
计算玩家的平均分:每一轮的游戏所获分数,可以设置比如1次就猜中的得10分,2次才猜中的9分,依次类推。然后把玩家所得总分除以所玩轮数,就是平均分啦
用一个文件储存不少单词,好像字典一样,然后从中随机选择 : 这样你就可以一个人玩了,不需要别人给你出题了。你的程序会从文件中随机选中一个单词,将其打乱,然后你自己来猜。下面是一个字典的例子:
FEATURE
MOTHER
ANSWER
CLOSE
GARAGE
DELETE
ENGINEER
ZERO
YESTERDAY
TOGETHER
JUICE
LAUGH
OFTEN
PEOPLE
KNOWLEDGE
当然,你可以自己填充英语单词,或者去网上下载那种英文字典的txt文件,里面有几万,几十万,甚至几百万个单词,从中选择的话就有猜单词的乐趣和难度啦。
要读取文件中的单词,你可以用我们上一课学到的知识点:读写文件。
如果你自己还想到其他改进的点子,也可以去实现。你会发现通过这个游戏,自己进步了很多。
欢迎把改进后的游戏的代码发给小编噢~
今天的课就到这里,一起加油吧!
下一课我们学习:指针一出,谁与争锋
本文出自 “程序员联盟 frogoscar” 博客,请务必保留此出处http://4526621.blog.51cto.com/4516621/1750943
标签:猜单词 c++ 程序员联盟 游戏 programmerleague
原文地址:http://4526621.blog.51cto.com/4516621/1750943