劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(六)
. #include <stdio.h>
. #include <time.h>
. #include <ctype.h>
. #include <stdlib.h>
.
. #define BELL '\a'
. #define DEALER 0
. #define PLAYER 1
.
. #define ACELOW 0
. #define ACEHIGH 1
.
. int askedForName = ;
.
.
. void dispTitle(void);
. void initCardsScreen(int cards[],int playerPoints[],
. int dealerPoints[], int total[],
. int *numCards);
. int dealCard(int * numCards,int cards[]);
. void dispCard(int cardDrawn,int points[]);
. void totalIt(int points[],int tatal[],int who);
. void dealerGetsCard(int *numCards,int cards[],
. int dealerPoints[]);
. void playerGetsCard(int *numCards,int cards[],
. int playerPoints[]);
. char getAns(char mesg[]);
. void findWinner(int total[]);
.
. main()
. {
. int numCards;
. int cards[],playerPoints[],dealerPoints[],total[];
. char ans;
.
. do
. {
. initCardsScreen(cards,playerPoints,dealerPoints,total, &numCards);
. dealerGetsCard(&numCards,cards, dealerPoints);
. printf("\n");
. playerGetsCard(&numCards,cards,playerPoints);
. playerGetsCard(&numCards,cards,playerPoints);
. do
. {
. ans = getAns("Hit or stand (H/S)?");
. if ( ans == 'H' )
. {
. playerGetsCard(&numCards,cards,playerPoints);
. }
. }
. while( ans != 'S' );
.
. totalIt(playerPoints,total,PLAYER);
. do
. {
. dealerGetsCard(&numCards,cards,dealerPoints);
. }
. while (dealerPoints[ACEHIGH] < );
.
. totalIt(dealerPoints,total,DEALER);
. findWinner(total);
.
. ans = getAns("\nPlay again(Y/N)?");
. }
. while(ans=='Y');
.
. return ;
. }
.
. void initCardsScreen( int cards[],int playerPoints[],
. int dealerPoints[], int total[],
. int *numCards )
. {
. int sub,val = ;
. char firstName[];
. *numCards=;
.
. for(sub=;sub<=;sub++)
. {
. val = (val == ) ? : val;
. cards[sub] = val;
. val++;
. }
.
. for(sub=;sub<=;sub++)
. {
. playerPoints[sub]=dealerPoints[sub]=total[sub]=;
. }
. dispTitle();
.
. if (askedForName==)
. {
. printf("What is your first name?");
. scanf(" %s",firstName);
. askedForName=;
. printf("Ok, %s,get ready for casino action!\n\n",firstName);
. getchar();
. }
. return;
. }
.
. void playerGetsCard(int *numCards,int cards[],int playerPoints[])
. {
. int newCard;
. newCard = dealCard(numCards, cards);
. printf("You draw:");
. dispCard(newCard,playerPoints);
. }
.
.
. void dealerGetsCard(int *numCards,int cards[],int dealerPoints[])
. {
. int newCard;
. newCard = dealCard(numCards,cards);
. printf("The dealer draws:");
. dispCard(newCard,dealerPoints);
. }
.
. int dealCard(int * numCards,int cards[])
. {
. int cardDrawn,subDraw;
. time_t t;
. srand(time(&t));
. subDraw = (rand()%(*numCards));
. cardDrawn = cards[subDraw];
. cards[subDraw] = cards[*numCards -];
. (*numCards)--;
. return cardDrawn;
. }
.
. void dispCard(int cardDrawn, int points[])
. {
. switch(cardDrawn)
. {
. case(): printf("%s\n","Jack");
. points[ACELOW] += ;
. points[ACEHIGH] += ;
. break;
. case(): printf("%s\n","Queen");
. points[ACELOW] += ;
. points[ACEHIGH] += ;
. break;
. case(): printf("%s\n","King");
. points[ACELOW] += ;
. points[ACEHIGH] += ;
. break;
. default : points[ACELOW] += cardDrawn;
. if(cardDrawn==)
. {
. printf("%s\n","Ace");
. points[ACEHIGH]+= ;
. }
. else
. {
. points[ACEHIGH]+=cardDrawn;
. printf("%d\n",cardDrawn);
. }
. }
. return ;
. }
.
. void totalIt(int points[],int total[],int who)
. {
. if ( (points[ACELOW] == points[ACEHIGH])
. ||(points[ACEHIGH] < ))
. {
. total[who] = points[ACELOW];
. }
. else
. {
. total[who] = points[ACEHIGH];
. }
.
. if (who == PLAYER )
. {
. printf("You have a total of %d\n\n", total[PLAYER]);
. }
. else
. {
. printf("The house stands with a total of %d\n\n",
. total[DEALER]);
. }
. return;
. }
.
. void findWinner(int total[])
. {
. if ( total[DEALER] == )
. {
. printf("The house wins.\n");
. return ;
. }
. if ( (total[DEALER] > ) && (total[PLAYER] > ) )
. {
. printf("%s", "Nobody wins.\n");
. return ;
. }
. if ((total[DEALER] >= total[PLAYER])&& (total[DEALER] < ))
. {
. printf("The house wins.\n");
. return ;
. }
. printf("%s%c","You win!\n",BELL);
. return;
. }
.
. char getAns(char mesg[])
. {
. char ans;
. printf("%s", mesg);
. ans = getchar();
. getchar();
. return toupper(ans);
. }
.
. void dispTitle(void)
. {
. int i = ;
. while(i<)
. {
. printf("\n");
. i++;
. }
. printf("\n\n*Step right up to the Blackjack tables*\n\n");
. return ;
. }
main()函数中player完成抽牌之后,立刻计算了player的点数:
. totalIt(playerPoints,total,PLAYER);
这个计算结果基于ACE的点数被作为1或11两种可能性,取最好一种作为最后的结果。
. void totalIt(int points[],int total[],int who)
. {
. if ( (points[ACELOW] == points[ACEHIGH])
. ||(points[ACEHIGH] > ))
. {
. total[who] = points[ACELOW];
. }
. else
. {
. total[who] = points[ACEHIGH];
. }
.
. if (who == PLAYER )
. {
. printf("You have a total of %d\n\n", total[PLAYER]);
. }
. else
. {
. printf("The house stands with a total of %d\n\n",
. total[DEALER]);
. }
. return;
. }
这个函数让我们得以领略什么叫思路含糊和废话连篇。首先
. if ( (points[ACELOW] == points[ACEHIGH])
. ||(points[ACEHIGH] > ))
. {
. total[who] = points[ACELOW];
. }
. else
. {
. total[who] = points[ACEHIGH];
. }
它的意思是当较高点数超过21点时把较低作为最终的点数,否则把较高点数作为最后的点数。显而易见这可以更简洁地表述为
if ( points[ACEHIGH] > 21 )
{
total[who] = points[ACELOW];
}
else
{
total[who] = points[ACEHIGH];
}
原来的代码把“(points[ACELOW] == points[ACEHIGH])||”写出来是思路不清导致的拖泥带水。
更简洁的写法是:
total[who] = ( points[ACEHIGH] > 21 )? points[ACELOW]: points[ACEHIGH];
“?:”这个三目运算在这里应用得恰到好处。
有些人对三目运算有一种无名的恐惧,鼓吹所谓“尽量不要用三目运算符”。这是毫无道理的,这种无理源自无知。他们自己不会用刀,于是就骗人骗己地宣称使用木棍强于用刀。
. if (who == PLAYER )
. {
. printf("You have a total of %d\n\n", total[PLAYER]);
. }
. else
. {
. printf("The house stands with a total of %d\n\n",
. total[DEALER]);
. }
这一段同样拖泥带水,其实它的效果和下面写法没有本质区别:
printf( "%s a total of %d\n\n",
who == PLAYER ? "You have" : "The house stands with",
total[who] );
所以totalIt()函数应改写为
void totalIt(int [],int [],int ); void totalIt(int points[],int total[],int who)
{ total[who] = ( points[ACEHIGH] > 21 )? points[ACELOW]: points[ACEHIGH]; printf( "%s a total of %d\n\n",
who == PLAYER ? "You have" : "The house stands with",
total[who] ); }
计算了完player的点数之后,按照规则在main()中由dealer继续抽牌(前面已抽过第一张牌)。dealer抽牌的策略是不到17点则继续,据代码作者说现实中的庄家的策略也是如此。
. do
. {
. dealerGetsCard(&numCards,cards,dealerPoints);
. }
. while ( dealerPoints[ACEHIGH] < );
dealerGetsCard ()函数的功能与playerGetsCard()函数重叠,前面已经提到过,甚至可以说这两个函数都是多余的。
此外这里还有一个更严重的问题,那就是“dealerPoints[ACEHIGH] < 17”这个表达式的逻辑问题。这个表达式要求dealer的点数达到17点或以上时停止抽牌,但问题在于点数有两种计算方法,一种是把Ace作为11点(Soft hand),另一种是把Ace作为1点。“dealerPoints[ACEHIGH] ”的意义是Soft hand点数,但是原作者在对程序的说明中压根就没有明确dealer的Soft hand点数达到或超过17点时停牌,只是泛泛地说了一句“the dealer stands on 17”。按软件工程的说法,这叫需求不清,是比代码错误更加严重的错误。
紧接着,矛盾出现了。
. totalIt(dealerPoints,total,DEALER);
totalIt()函数计算dealer的点数却是按照最好成绩计算的,这就发生了矛盾。比如dealer为15点时又取了一张牌Ace,按照Soft hand规则,dealer的点数是25点,但最后的成绩却是按照硬牌规则为16点,而如果dealer的点数为16点,那么前面他根本就不应该停牌。这是“双重标准”的C语言版。
这里较为合理的写法应该是
do
{
dealerGetsCard(&numCards,cards,dealerPoints);
totalIt(dealerPoints,total,DEALER);
}
while ( total [DEALER] < 17 );
劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(六)的更多相关文章
- 劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(八)
[重构](续) 牌的表示: 一副牌有52张,可用一整数数组描述.但是由于在游戏过程中牌数在不断减少,所以用一表示剩余张数的整数和一整数数组共同描述.C99支持一种变量长度数组,但用在这里并没有什么特别 ...
- 如何使用 js 写一个正常人看不懂的无聊代码
如何使用 js 写一个正常人看不懂的无聊代码 代码质量, 代码可读性, 代码可维护性, clean code WAT js WTF https://www.destroyallsoftware.com ...
- 如何写出同事看不懂的Java代码?
原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 哈喽大家好啊,我是没更新就是在家忙着带娃的Hydra. 前几天,正巧赶上组里代码review,一下午下来,感觉整个人都血压拉满了.五花八门的代码 ...
- 《写给大忙人看的java se 8》笔记
现在才来了解java8,是不是后知后觉了点? 新的编程技术,个人不喜欢第一时间跟进. 待社区已有实践积淀再切入似乎更划算些? 一点点精明的考虑. 不多说,上代码. //读<写给大忙人看的java ...
- 转:HIBERNATE一些_方法_@注解_代码示例---写的非常好
HIBERNATE一些_方法_@注解_代码示例操作数据库7步骤 : 1 创建一个SessionFactory对象 2 创建Session对象 3 开启事务Transaction : hibernate ...
- 【Xamarin挖墙脚系列:代码手写UI,xib和StoryBoard间的博弈,以及Interface Builder的一些小技巧(转)】
正愁如何选择构建项目中的视图呢,现在官方推荐画板 Storybord...但是好像 xib貌似更胜一筹.以前的老棒子总喜欢装吊,用代码写....用代码堆一个HTML页面不知道你们尝试过没有.等页面做出 ...
- 【读书笔记】《写给大忙人看的Java SE 8》——Java8新特性总结
虽然看过一些Java 8新特性的资料,但是平时很少用到,时间长了就忘了,正好借着Java 9的发布,来总结下一些Java 8中的新特性. 接口中的默认方法和静态方法 先考虑一个问题,如何向Java中的 ...
- 代码手写UI,xib和StoryBoard间的博弈,以及Interface Builder的一些小技巧
近期接触了几个刚入门的iOS学习者,他们之中存在一个普遍和困惑和疑问.就是应该怎样制作UI界面.iOS应用是非常重视用户体验的,能够说绝大多数的应用成功与否与交互设计以及UI是否美丽易用有着非常大的关 ...
- 匈牙利&&EK算法(写给自己看)
(写给自己看)匈牙利算法(最大匹配)和KM算法(最佳匹配) 匈牙利算法 思想 不断寻找增广路,每次寻得增广路,交换匹配边和非匹配边,则匹配点数+1 这里增广路含义:交错路,即从未匹配点出发经过未匹配边 ...
随机推荐
- [BZOJ 4591] 超能粒子炮-改
Link: 传送门 Solution: 记录一下推$\sum_{i=0}^k C_n^i$的过程: 其实就是将相同的$i/p$合起来算,这样每个里面都是一个可以预处理的子问题 接下来递归下去算即可 T ...
- bzoj 3283 扩展BSGS + 快速阶乘
T2 扩展BSGS T3 快速阶乘 给定整数n,质数p和正整数c,求整数s和b,满足n! / pb = s mod pc 考虑每次取出floor(n/p)个p因子,然后将问题转化为子问题. /*** ...
- poj 1509
求一个字符串在旋转置换群下最小字典表示. 用的是后缀数组(后缀自动机还是再听听jason_yu讲讲吧,关于right集合的部分还有问题) 最小表示法的思想很有好(判断两个对象在某一置换群划分下,是否等 ...
- MySQL: 查看一次SQL的执行时间都花在哪些环节上
select @@profiling -- 看看当前的session的profiling打开没有 set profiling = 1 -- 如果没打开,打开一下 -- 执行一些sql select c ...
- 各种SSD SMART 信息 转
intel SSD Toolbox SMART信息 解释:03 – Spin Up Time (磁头加载时间)The average time it takes the spindle to spin ...
- 【leetcode】sort list(python)
链表的归并排序 超时的代码 class Solution: def merge(self, head1, head2): if head1 == None: return head2 if head2 ...
- win7 系统盘怎样瘦身! 可整理出4-5G。
1.移走虚拟内存文件到非系统盘 大家都知道,为了加快系统的执行,Windows提供了虚拟内存机制,而在Windows7中,默认是开启这项功能的,并且虚拟内存文件在系统盘.比方一台2G内存的机器,虚拟内 ...
- C++STL源代码学习(之slist篇)
///stl_slist.h ///list为双向循环链表,slist为单向链表.某些操作效率更高 ///slist是SGI额外提供的单向链表,不属于C++标准 struct _Slist_node_ ...
- 读书笔记-APUE第三版-(8)进程控制
进程ID 每一个进程都有一个唯一的进程ID.几个特殊进程: 0号进程是内核进程,一般是调度进程swapper. 1号进程init,是用户进程(以root权限执行/sbin/init),负责初始化. 几 ...
- Entity Framework实现事务回滚
在使用Entity Framework为主从表添加数据,当一个表添加数据成功,另一个表添加数据失败,这时候就需要用到事务回滚. 比如有以下关系的2张表. 客户端使用TransactionScope类可 ...