劣质代码评析——《写给大家看的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.2460.[BeiJing2011]元素(线性基 贪心)
题目链接 线性基:https://blog.csdn.net/qq_36056315/article/details/79819714. \(Description\) 求一组矿石,满足其下标异或和不 ...
- Codeforces Round #281 (Div. 2) C. Vasya and Basketball 暴力水题
C. Vasya and Basketball time limit per test 2 seconds memory limit per test 256 megabytes input stan ...
- IO流-递归遍历目录下指定后缀名结尾的文件名称
/* *自定义遍历目录下指定后缀名结尾文件的名称的方法: * * param file:指定目录 name:指定后缀名 */ 1 public static void FileName(File fi ...
- 重温PHP之选择排序
思路:一组数中,选出最小者与第一个位置数交换,然后在剩余数中再找最小者与第二个位置数交换,依次类推,循环到倒数第二个数和最后一个数比较为止. 测试代码: 结果:
- HDU 4497 GCD and LCM (合数分解)
GCD and LCM Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)Total ...
- hdu 4647 Another Graph Game,想到了就是水题了。。
题目是给一个无向图,其中每个节点都有点权,边也有边权,然后就有2个小朋友开始做游戏了ALICE &BOB 游戏规定ALICE 先行动然后是BOB,然后依次轮流行动,行动时可以任意选取一个节点并 ...
- 不用软件快速拥有几百个QQ群并都是管理员
不用软件快速拥有几百个QQ群并都是管理员!快速拥有有几十万精准数据库的方法 !和快速收集上亿邮箱的思维方法(附上5种赚钱方法).pdf_免费高速下载|百度云 网盘-分享无限制 http://pan.b ...
- Android的基本常用的短信操作
1.调用系统发送短信界面(传入手机号码+短信内容) 2.隐藏发送短信(指定号码指定内容)(这里隐藏只是没有反写入数据库) 3.获得收件箱接收到的短信 4.Android屏蔽新短信通知提示信息:(Con ...
- Appium+python自动化7-输入中文
前言 在做app自动化过程中会踩很多坑,咱们都是用的中文的app,所以首先要解决中文输入的问题! 本篇通过屏蔽软键盘,绕过手机的软键盘方法,解决中文输入问题. 一.定位搜索 1.打开淘宝点搜索按钮,进 ...
- 《Java并发编程实战》第四章 对象的组合 读书笔记
一.设计线程安全的类 在设计线程安全类的过程中,须要包括下面三个基本要素: . 找出构成对象状态的全部变量. . 找出约束状态变量的不变性条件. . 建立对象状态的并发訪问管理策略. 分析对象的 ...