0?wx_fmt=jpeg


内容简介

1、第一部分第十一课:小游戏,猜单词

2、第一部分第十二课预告:指针一出,谁与争锋



小练习


上一课《【C++探索之旅】第一部分第十课:文件读写,海阔凭鱼跃》中我们学习了读写文件的知识。


第一部分的课程也快接近尾声了,下一课我们会学习很重要的指针的知识。之后,我们就进入第二部分:面向对象编程了。哈哈哈,激动不?


目前为止,我们也学了不少内容:编译基本原理,IDE,变量,函数,条件语句,循环语句,引用,数组,文件读写,等等。你应该为自己感到自豪。


俗语说得好:实践是最好的老师。我们学了那么多知识点,却还没有综合地来应用一下,怎么行呢?


这一课我们就用一个小游戏来锻炼吧。


游戏规则很简单:我们输入一个单词,然后把单词里的字母打乱,显示给用户看,让用户猜这个单词是什么。



准备工作和建议


这个小游戏看似简单,但对于初学者的话还是有点难度的,因为需要用到我们前几课的一些知识点,例如:


  • 变量

  • string(字符串)

  • 函数

  • 控制流程,条件语句

  • 数组


如果有知识点生疏了,可以在编写这个游戏前回去复习。


[猜数字]游戏的主旨


我们要写的小游戏的流程是这样:


  1. 玩家1输入一个单词

  2. 程序将单词打乱

  3. 玩家2尝试从打乱的单词猜出原始的单词是什么


下面是一个玩此游戏的一轮的例子:


输入一个单词 
MYSTERY  

原先的单词是什么 ? MSYRETY
RYTEMSY
猜错了!

原先的单词是什么 ? MSYRETY
SERMYTY
猜错了!

原先的单词是什么 ? MSYRETY
MYSTERY
猜对了! 好棒!



分析以上流程:


  1. 在这一轮中,玩家1选择输入MYSTERY(英语"神秘,秘密"的意思)作为要猜的单词给玩家2来猜。

  2. 程序将MYSTERY这个单词的字母顺序打乱,然后请玩家2来猜打乱前的原始单词是什么。

  3. 玩家2尝试猜测,他猜了3次才猜中:

    1. RYTEMSY:显示"猜错了!"

    2. SERMYTY:还是没猜对

    3. MYSTERY:终于猜对了,祝贺!


当然了,在上面演示的一轮游戏的例子中,玩家2可以看到玩家1输入的原始单词,暂时先这样。到这课最后的改进部分我们会说怎么避免。当然了,聪慧如你也许早就想到好几个办法了。


开始前的一些建议


当我们让一个初学编程的朋友放手一搏,第一次自己写一个比较完整的程序时,是有难度的。


小编也是过来人,以前还没入门编程时感受颇深:看书,照着书上例子敲代码,好像都懂啊。可是一上手自己写比较复杂的代码,就愣住了,不知该从何下手。


"从哪里开始写呢?","我该做什么呢?","我该用什么呢?",等等,都是初学的症候。不过谁都得过了这一阶段,才能体验到美好的入门感觉。


因此,绝对不要对自己灰心!编程么,就是用一种新的思路来解决问题,只要你经过了编程思想的锤炼,你会发现其他很多事情都会显得简单了。


不过,善良如小编,还是不希望你自己过于迷失。毕竟只要稍加提示,你的思路就会更开阔啦。当然了,也许不必提示,你已经把程序写出来了吧。


找到头绪


这个程序的编写其实只有三步:


  1. 输入原始单词;

  2. 打乱单词的字母顺序;

  3. 做循环:请求玩家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文件,里面有几万,几十万,甚至几百万个单词,从中选择的话就有猜单词的乐趣和难度啦。


要读取文件中的单词,你可以用我们上一课学到的知识点:读写文件。


如果你自己还想到其他改进的点子,也可以去实现。你会发现通过这个游戏,自己进步了很多。


欢迎把改进后的游戏的代码发给小编噢~



第一部分第十二课预告


今天的课就到这里,一起加油吧!

下一课我们学习:指针一出,谁与争锋