接下来就是详细设计
然后细化PlayBout
在给定的猜测次数内
接受游戏参与者输入的价格
然后判断和实际价格是不是一样的
如果不一样就得提示他
高了就是说高了 低了就是说低了
如果是一样的 你赢了
真实的程序开发的时候
要跟客户讨论
你想让我给你写什么程序
你的程序需要完成什么功能
客户不一定一开始就能把这个讲全了
他能够想清楚就不容易了
再清楚地表达出来是非常非常难的
所以很多时候
他其实不知道应该怎么跟你说
他希望程序能解决什么东西
具有什么样的功能、
概要设计、详细设计
都给他看了
反而能够给他提示
想起来了 有一个需求G 刚才忘了说
一旦游戏参与者给出了猜测的价格
如果价格高了或者低了
提示用户的时候
猜测的价格区间就应该反映这个变化
原始价格在100到200之间
然后玩家来猜
150 高了
接下来你给用户提示的时候
价格区间在100到149之间
需要响应区间的变化
就意味着PlayBout函数
需要接受最重要的几个参数
第一 实际价格是多少
第二个 较低的价格是多少
第三个 较高的价格是多少
第四个 还有几次猜测机会
区间是不断变化的
函数接口就做了修正
接下来就是PrintWelcomeInfo
提示欢迎信息的
告诉玩家这个程序该怎么玩
所有细节信息就在这里面输出
再接下来就是游戏的初始化模块
InitializeGame
就把函数框架写上就行了
函数头部花括号里面什么也没填
然后继续
游戏结束模块要输出胜率
然后客户一拍脑袋：“我刚才又忘了说！”
忘了你就得补
需求H：当游戏参与者的胜率
超过75%的时候
输出“You luckyyyyyyyyyyyyy!”
如果当胜率低于75%但超过50%的时候
“So gooooooood!”.
其它的情况
就是胜率不到一半
输出“You can do it better. Wish you luck.”
你千万不要说“You stupid!”
这个不成 给点鼓励语
我们就根据不同的胜率
输出不同的提示信息
这就是我们新的需求H
编程其实很简单 一个if-else if-else
其实就搞定了
继续游戏模块 这才是重头戏
PlayGame
While(true)无限循环
调用InitializeBout
初始化这个游戏回合
生成物品的实际价格
然后PlayBout开始玩这个游戏
游戏结束了
如果返回值是true 说明玩家赢了
递增他获胜的回合数
否则的话就不递增了
不管怎么样 要递增总回合数
然后调用Again 问你是不是继续玩
如果是true就继续
如果是false 就break终止我们的游戏
返回它最终的胜率
这个就是PlayGame函数的伪代码
逻辑上是相当清晰的
现在回到InitializeBout这个函数
就是游戏回合的初始化
在100到200之间
生成物品的实际价值
调用GenerateRandomNumber
来生成这样随机数
用它来作为物品的实际价格
你一下就想起来了
既然我要调用GenerateRandomNumber
来生成一个随机的整数
那么一定需要Randomize一次函数调用
这个随机化动作在哪里做呢
显然不能够在InitializeBout里面做
干脆放在InitializeGame这个函数里
这个是非常重要的设计
就为InitializeGame找了一个事情做
继续详细设计
游戏模块还要细化呢
你的客户又一拍脑袋
又来俩新需求 需求I
如果游戏参与者给出了猜测价格
不在当前区间里
你比如说原来实际价格就100到200之间
然后他一下子敲错键盘了 给你250
因为已经很明确地说了
这个物品价格在100到200之间
给我250这个数字没有意义
如果我算这个数据是有效的
机会就浪费了
正常情况下
游戏应该把这个机会给扣掉
不算他浪费了一次机会
就是说你这个数据是非法的
有效的数字才认为
你用了一次猜测机会
然后是需求J
当最后一次猜测机会在101和102之间
最后没猜对
那用户其实立即就能够知道
它实际价格是什么
可是如果他很不幸
数据在140到150之间
实际上还有11个数让他猜
最后就一次机会了
他也没猜对
这个概率就低了
他没猜对 程序就说你失败了 结束了
玩家抓耳挠腮的
实际数是多少啊 你得跟我说呀
你怎么最后不跟我说实际数是多少
当他猜测机会用完了也没猜对
别跟我说高了、低了
直接告诉他实际价格是多少
别再瞒着他了
PlayBout伪代码
看上去很复杂 代码很多的样子
其实就是对我们原来那个伪代码
做了一次更细化的动作
整体代码本身并没有特别复杂
就是while循环 里面一个switch
几个函数调用
进一步细化有序回合的话
你会发现这里边
其实还需要使用到三个辅助的函数
第一个需要从终端
接受用户输入猜测的价格
这个函数叫GetPrice
第二个我得判断用户猜测的价格
是不是在给定的合法有效的区间里
叫CheckPrice
第三个还要判断用户的价格
和实际价格 高了、低了大小关系
这个我们有一个JudgePrice函数
就是为PlayBout所实现的附加函数
具体的函数的伪代码都在这里
还有一个Again
Again这个函数就要询问玩家
游戏还要不要玩
接受用户的输入
要继续玩 就继续玩
不玩了 Again就返回false
这个是again函数的伪代码
现在呢 就画一个图
把这些实现的函数
和它们之间调用的关系画出来
别说同学们想象不到
这里面定义了那么多函数
就我为了这门课程举这个例子的时候
我其实都没有想到这个程序
最终会实现这么多个函数
从足球的角度来讲
这叫433阵形
连同main正好11个
main函数需要使用到4个函数
PrintWelcomeInfo、InitializeGame、
PlayGame、PrintgameOverInfo
然后PlayGame要使用到
InitializeBout、PlayBout、Again这三个函数
PlayBout要使用GetPrice、
CheckPrice、JudgePrice这三个函数
这个就是整个程序
实现出来以后的框架
现在你看到整个程序逻辑
是非常非常清晰的
实现了11个函数 对于这道题
你想象一下
一开始的时候没觉得这个题
有多么复杂
但是最终写出的代码
居然包括了11个函数
这样的话 程序组织当然就会很清晰
接下来就是guess这个库的具体实现
“guess.cpp”
具体的代码就不解释了
感兴趣的同学
回去可以照着我的片子
把这个代码敲进去 运行一下
体会一下这个程序是怎么实现的
最后要对整个程序进行系统测试
这个系统测试其实非常复杂
比如说这里面数据对象
是不是都弄对了
随机生成的价格是不是合理
猜测的过程、取用户输入数据的过程
有没有问题
所有这些信息其实都需要进行测试
自己就先玩两遍吧
玩两遍的时候要生成价格
然后你真地就自己在那个地方瞎蒙吗
你也不知道它生成的实际价格是多少吗
那个测试的话 效率就太低了
所以真实测试的时候
可能说干脆就直接输出实际的价格
那么在真实程序运行的时候
这一段输出肯定是不能要的
你还能把实际价格输出以后让他猜
那还猜什么呀
所以这个程序真正发布的时候
这行代码一定是要删掉的
可是如果总是一开始测试的时候贴上去
后来又删除
那当代码量很大的时候很难删的
一不小心就删错了
在这种情况下就有一个小技巧
凡是用于测试的这些输出
在未来都不应该出现
用一个宏给包出来
#ifndef NDEBUG、#endif
#ifndef是什么呢
就是如果没有定义
后面跟着那个宏的名字 NDEBUG
那么编译器 就编译cout这条语句
结束这个宏的测试
如果你定义那个宏
那么它里面封包的
cout这条语句就不编译了
所以就不需要你删除
想去掉这样cout的语句 很简单
在程序的最开头写上一行
#define NDEBUG
所有的在#ifndef NDEBUG、#endif中间的这些代码
全部会跳过 不再编译
这是系统测试的小技巧
还有呢 系统测试还没完
游戏的回合还要检查它是不是对
包括参数传递对不对
获取猜测的价格
最低价、最高价的价格判断
猜测次数、提示信息的显示
你都需要测试的
要有一个完整的系统测试的计划
一条一条地去对照
看你的程序运行得对不对
真把这个东西测试完
它的工作量其实是巨大的
这个程序到现在算写完了
对它做一个经验的总结
这个问题一开始大家觉得它难不难
刚开始看到这个题目的时候
你不会觉得它很难
整个程序做设计难不难
如果让你从需求分析、方案设计、
编码实现、系统测试
从头到尾全部走一遍的话
这个程序其实就是蛮难的
一天、两天你都搞不定
那你真要说编码难不难呢
还真的一点都不难
如果我有了一个详细的方案
算法都写在那个地方
剩下无非是用C/C++的代码
把它填充进去就完了
就用了if、switch、for、while
几个函数调用
就实现了这些东西
你别看代码量不短
但它实际上非常非常简单
编码本身并不难
最后你会发现
你和用户交流烦不烦 烦
大部分用户让你给他写程序
他说不清楚他需要啥
所以你要不断跟他交流
不断跟他进行沟通
写方案、做方案给他看
讨论 然后再反馈 让你修改
这个过程是个不断反复的过程
你要没有很高的情商
能够和他做顺畅的交流
那么系统需求你就分析不出来