劣质代码评析——《写给大家看的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 这里增广路含义:交错路,即从未匹配点出发经过未匹配边 ...
随机推荐
- [BZOJ4561][JLOI2016]圆的异或并(扫描线)
考虑任何一条垂直于x轴的直线,由于圆不交,所以这条直线上的圆弧构成形似括号序列的样子,且直线移动时圆之间的相对位置不变. 将每个圆拆成两边,左端加右端删.每次加圆时考虑它外面最内层的括号属于谁.用se ...
- blog搬家啦
本blog大概不会更新了 新blog地址:https://zykykyk.github.io/
- GNU C __attribute__ 机制简介
摘要: 在学习linux内核代码及一些开源软件的源码(如:DirectFB),经常可以看到有关__attribute__的相关使用.本文结合自己的学习经历,较为详细的介绍了__attribute__相 ...
- hdu 4118 dfs
题意:给n个点,每个点有一个人,有n-1条有权值的边,求所有人不在原来位置所移动的距离的和最大值.不能重复 这题的方法很有看点啊,标记为巩固题 Sample Input 1 4 1 2 3 2 3 2 ...
- hdu 3061 最大权闭合子图
属于模板题吧... #include <cstdio> #include <cstring> #include <vector> #define min(a,b) ...
- bzoj 3252: 攻略 -- 长链剖分+贪心
3252: 攻略 Time Limit: 10 Sec Memory Limit: 128 MB Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神 ...
- URAL 1876 Centipede's Morning
1876. Centipede's Morning Time limit: 0.5 secondMemory limit: 64 MB A centipede has 40 left feet and ...
- sql中如何统计一字段中字符串的个数
declare @s varchar(100)set @s='156434A27kAsdABCiosd-01&**('--找出现的次数select len(@s)-len(replace(@s ...
- 关于ANDROID模拟器的一些事
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 继上一篇Android Studio VS Eclipse的文章后接着来分享AnDevCo ...
- 报错:无法将类型"System.Data.EntityState"隐式转换为"System.Data.Entity.EntityState"
报错:无法将类型"System.Data.EntityState"隐式转换为"System.Data.Entity.EntityState". 出错语句停留 ...