. #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点程序(六)的更多相关文章

  1. 劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(八)

    [重构](续) 牌的表示: 一副牌有52张,可用一整数数组描述.但是由于在游戏过程中牌数在不断减少,所以用一表示剩余张数的整数和一整数数组共同描述.C99支持一种变量长度数组,但用在这里并没有什么特别 ...

  2. 如何使用 js 写一个正常人看不懂的无聊代码

    如何使用 js 写一个正常人看不懂的无聊代码 代码质量, 代码可读性, 代码可维护性, clean code WAT js WTF https://www.destroyallsoftware.com ...

  3. 如何写出同事看不懂的Java代码?

    原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 哈喽大家好啊,我是没更新就是在家忙着带娃的Hydra. 前几天,正巧赶上组里代码review,一下午下来,感觉整个人都血压拉满了.五花八门的代码 ...

  4. 《写给大忙人看的java se 8》笔记

    现在才来了解java8,是不是后知后觉了点? 新的编程技术,个人不喜欢第一时间跟进. 待社区已有实践积淀再切入似乎更划算些? 一点点精明的考虑. 不多说,上代码. //读<写给大忙人看的java ...

  5. 转:HIBERNATE一些_方法_@注解_代码示例---写的非常好

    HIBERNATE一些_方法_@注解_代码示例操作数据库7步骤 : 1 创建一个SessionFactory对象 2 创建Session对象 3 开启事务Transaction : hibernate ...

  6. 【Xamarin挖墙脚系列:代码手写UI,xib和StoryBoard间的博弈,以及Interface Builder的一些小技巧(转)】

    正愁如何选择构建项目中的视图呢,现在官方推荐画板 Storybord...但是好像 xib貌似更胜一筹.以前的老棒子总喜欢装吊,用代码写....用代码堆一个HTML页面不知道你们尝试过没有.等页面做出 ...

  7. 【读书笔记】《写给大忙人看的Java SE 8》——Java8新特性总结

    虽然看过一些Java 8新特性的资料,但是平时很少用到,时间长了就忘了,正好借着Java 9的发布,来总结下一些Java 8中的新特性. 接口中的默认方法和静态方法 先考虑一个问题,如何向Java中的 ...

  8. 代码手写UI,xib和StoryBoard间的博弈,以及Interface Builder的一些小技巧

    近期接触了几个刚入门的iOS学习者,他们之中存在一个普遍和困惑和疑问.就是应该怎样制作UI界面.iOS应用是非常重视用户体验的,能够说绝大多数的应用成功与否与交互设计以及UI是否美丽易用有着非常大的关 ...

  9. 匈牙利&&EK算法(写给自己看)

    (写给自己看)匈牙利算法(最大匹配)和KM算法(最佳匹配) 匈牙利算法 思想 不断寻找增广路,每次寻得增广路,交换匹配边和非匹配边,则匹配点数+1 这里增广路含义:交错路,即从未匹配点出发经过未匹配边 ...

随机推荐

  1. 【转载】实现UTF8与GB2312编码格式相互转换(VC)已经验证!

    UTF-8编码:[1,1,1,0,A5,A6,A7,A8],[1,0,B3,B4,B5,B6,B7,B8],[1,0,C3,C4,C5,C6,C7,C8];对应的UNICODE编码:[A5,A6,A7 ...

  2. 2016 UESTC DP专题题解

    题解下载地址:http://pan.baidu.com/s/1eSx27Jk 题解下载地址:http://pan.baidu.com/s/1qYDxlhi

  3. 重温PHP之快速排序

    基本原理:选出当前数组中任一元素(通常为第一个)作为标准,新建两个空数组分别置于当前数组前后,然后遍历当前数组,如果数组中元素值小于等于第一个元素值就放到前边空数组,否则放到后边空数组. //快速排序 ...

  4. OpenCV/CUDA/Qt 环境配置小结

    OpenCV Qt CUDA windows环境下 配置 反复装过几次,每次都网搜攻略:自个做个记录 方便以后使用. 碰到OpenCV各种奇怪的错误 先看看 图片imread() 有没有读空 再找其他 ...

  5. How To Backup Your Android Phone’s Boot, Recovery And System Partition Images -- RomDump

    One can’t stress enough on the importance of backups and when it comes to tinkering with your Androi ...

  6. UITextView in iOS7 doesn&#39;t scroll

    UITextView in iOS7 has been really weird. As you type and are entering the last line of your UITextV ...

  7. java int类型转换成String , 不足10位 在前面补0

    String.format("%010d", 25); 0代表前面要补的字符 10代表字符串长度 d表示参数为整数类型 String s = "Hello World!& ...

  8. Oracle用imp和exp实现数据的导入和导出

    使用方法如下: Imp username/password@connect_string param=value - exp username/password@connect_string para ...

  9. VirtualBox 快捷键

    VirtualBox 的 Host 键是哪一个? 默认的 Host = Right Ctrl 组合键,意思是键盘上两个 “Ctrl”中右边的那个.键盘上是没有 “Right”这个键的,刚开始不明白,后 ...

  10. Struts2 S标签 数目字格式化成金额输出(保留两位小数)

    JSP: <s:property value="%{formatDouble(price)}" /> Action:添加 //格式化数字显示 public String ...