. #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. 计算机二级软件VC++6.0下载地址

    计算机二级软件VC++6.0介绍: 适合所有参加全国计算机等级考试的童鞋们……见图如下: 下载地址:(以下两者任选其一即可) (1).计算机二级软件VC++6.0(16.35MB) (2).计算机二级 ...

  2. codevs 1073 家族 并查集

    家族 Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://www.codevs.cn/problem/1073/ Description 若某个家族 ...

  3. Java_如何等待子线程执行结束

    工作中往往会遇到异步去执行某段逻辑, 然后先处理其他事情, 处理完后再把那段逻辑的处理结果进行汇总的产景, 这时候就需要使用线程了. 一个线程启动之后, 是异步的去执行需要执行的内容的, 不会影响主线 ...

  4. IT程序猿们,我该做什么选择呢

    这个时刻,我想我遇到人生小拐点了,程序猿到了30岁,到达了一个分界线了,现在的我该何去何从呢? 先谈下简单的情况吧: 来这个公司2年了,之前因为身体的原因,不想那么累,于是选择了一份维护的工作,就来了 ...

  5. QQclient团队博客

    Windows 8 视频採集  http://impd.tencent.com/?p=25 句柄泄漏检測工具的实现原理 http://impd.tencent.com/?p=29

  6. Java基础加强总结(二)——泛型

    一.体验泛型 JDK1.5之前的集合类中存在的问题——可以往集合中加入任意类型的对象,例如下面代码: package cn.gacl.generic.summary; import java.util ...

  7. warning,C4996,sprintf,deprecated,C4996,strcpy,C4996,strcat

    在vs2003, vs2005中用sprintf 会出现warning C4996: 'sprintf' was declared deprecated或warning C4996: 'strcpy' ...

  8. 动态创建html元素的几种方法

    可以通过以下几种方式动态创建html元素: 1.使用jQuery创建元素的语法 2.把动态内容存放到数组中,再遍历数组动态创建html元素 3.使用模版   □ 使用jQuery动态创建元素追加到jQ ...

  9. eclipse 启动不起来(不能创建VM)

    Specifying the Java virtual machine Here is a typical Eclipse command line: eclipse -vm c:\jdk1.4.2\ ...

  10. ExtJS BorderLayout

    <HTML> <HEAD> <TITLE>布局</TITLE> <link rel="stylesheet" type=&qu ...