zoukankan      html  css  js  c++  java
  • 劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(六)

    0. #include <stdio.h>
    1. #include <time.h>
    2. #include <ctype.h>
    3. #include <stdlib.h>
    4. 
    5. #define BELL 'a'
    6. #define DEALER 0
    7. #define PLAYER 1
    8. 
    9. #define ACELOW 0
    10. #define ACEHIGH 1
    11. 
    12. int askedForName = 0;
    13. 
    14. 
    15. void dispTitle(void);
    16. void initCardsScreen(int cards[52],int playerPoints[2],
    17. int dealerPoints[2], int total[2], 
    18. int *numCards);
    19. int dealCard(int * numCards,int cards[52]);
    20. void dispCard(int cardDrawn,int points[2]);
    21. void totalIt(int points[2],int tatal[2],int who);
    22. void dealerGetsCard(int *numCards,int cards[52],
    23. int dealerPoints[2]);
    24. void playerGetsCard(int *numCards,int cards[52],
    25. int playerPoints[2]);
    26. char getAns(char mesg[]);
    27. void findWinner(int total[2]);
    28. 
    29. main()
    30. {
    31.    int numCards;
    32.    int cards[52],playerPoints[2],dealerPoints[2],total[2];
    33.    char ans;
    34.    
    35.    do 
    36.    { 
    37.       initCardsScreen(cards,playerPoints,dealerPoints,total, &numCards);
    38.       dealerGetsCard(&numCards,cards, dealerPoints);
    39.       printf("
    ");
    40.       playerGetsCard(&numCards,cards,playerPoints); 
    41.       playerGetsCard(&numCards,cards,playerPoints);
    42.       do
    43.       {
    44.          ans = getAns("Hit or stand (H/S)?");
    45.          if ( ans == 'H' )
    46.          { 
    47.             playerGetsCard(&numCards,cards,playerPoints);
    48.          }  
    49.       }
    50.       while( ans != 'S' );
    51.       
    52.       totalIt(playerPoints,total,PLAYER);
    53.       do
    54.       {
    55.          dealerGetsCard(&numCards,cards,dealerPoints);
    56.       }
    57.       while (dealerPoints[ACEHIGH] < 17 );
    58.       
    59.       totalIt(dealerPoints,total,DEALER);
    60.       findWinner(total); 
    61.       
    62.       ans = getAns("
    Play again(Y/N)?");  
    63.    }
    64.    while(ans=='Y');
    65.    
    66.    return 0;
    67. }
    68. 
    69. void initCardsScreen( int cards[52],int playerPoints[2],
    70.                       int dealerPoints[2], int total[2], 
    71.                       int *numCards )
    72. {
    73.    int sub,val = 1 ;
    74.    char firstName[15];
    75.    *numCards=52;
    76.    
    77.    for(sub=0;sub<=51;sub++)
    78.    {
    79.       val = (val == 14) ? 1 : val;
    80.       cards[sub] = val;
    81.       val++;  
    82.    }
    83.    
    84.    for(sub=0;sub<=1;sub++)
    85.    { 
    86.       playerPoints[sub]=dealerPoints[sub]=total[sub]=0;
    87.    }
    88.    dispTitle();
    89.    
    90.    if (askedForName==0)
    91.    { 
    92.       printf("What is your first name?");
    93.       scanf(" %s",firstName);
    94.       askedForName=1;
    95.       printf("Ok, %s,get ready for casino action!
    
    ",firstName);
    96.       getchar();
    97.    }
    98.    return;        
    99. }
    100. 
    101. void playerGetsCard(int *numCards,int cards[52],int playerPoints[2])
    102. {
    103.    int newCard;
    104.    newCard = dealCard(numCards, cards);
    105.    printf("You draw:");
    106.    dispCard(newCard,playerPoints);
    107. }
    108. 
    109. 
    110. void dealerGetsCard(int *numCards,int cards[52],int dealerPoints[2])
    111. {
    112.    int newCard;
    113.    newCard = dealCard(numCards,cards);
    114.    printf("The dealer draws:");
    115.    dispCard(newCard,dealerPoints);
    116. }
    117. 
    118. int dealCard(int * numCards,int cards[52])
    119. {
    120.    int cardDrawn,subDraw;
    121.    time_t t;
    122.    srand(time(&t));
    123.    subDraw = (rand()%(*numCards));
    124.    cardDrawn = cards[subDraw];
    125.    cards[subDraw] = cards[*numCards -1];
    126.    (*numCards)--;
    127.    return cardDrawn;
    128. }
    129. 
    130. void dispCard(int cardDrawn, int points[2])
    131. {
    132.    switch(cardDrawn)
    133.    {
    134.       case(11): printf("%s
    ","Jack");
    135.                 points[ACELOW] += 10;
    136.                 points[ACEHIGH] += 10;
    137.                 break;
    138.       case(12): printf("%s
    ","Queen");
    139.                 points[ACELOW] += 10;
    140.                 points[ACEHIGH] += 10;
    141.                 break;
    142.       case(13): printf("%s
    ","King");
    143.                 points[ACELOW] += 10;
    144.                 points[ACEHIGH] += 10;
    145.                 break;
    146.       default : points[ACELOW] += cardDrawn;
    147.                 if(cardDrawn==1)
    148.                 { 
    149.                    printf("%s
    ","Ace");
    150.                    points[ACEHIGH]+= 11;
    151.                 }
    152.                 else
    153.                 {  
    154.                   points[ACEHIGH]+=cardDrawn;
    155.                   printf("%d
    ",cardDrawn); 
    156.                 }
    157.    }
    158.    return ;
    159. }
    160. 
    161. void totalIt(int points[2],int total[2],int who)
    162. {
    163.    if ( (points[ACELOW] == points[ACEHIGH])
    164.       ||(points[ACEHIGH] < 21 ))
    165.    { 
    166.      total[who] = points[ACELOW];
    167.    }
    168.    else
    169.    { 
    170.        total[who] = points[ACEHIGH];
    171.    }
    172.    
    173.    if (who == PLAYER )
    174.    {
    175.       printf("You have a total of %d
    
    ", total[PLAYER]);
    176.    }
    177.    else
    178.    {
    179.        printf("The house stands with a total of %d
    
    ", 
    180.        total[DEALER]);
    181.    }
    182.    return;
    183. }
    184. 
    185. void findWinner(int total[2])
    186. {
    187.    if ( total[DEALER] ==  21 )
    188.    {
    189.        printf("The house wins.
    ");
    190.        return ;
    191.    }
    192.    if ( (total[DEALER] > 21) && (total[PLAYER] > 21) )
    193.    { 
    194.       printf("%s", "Nobody wins.
    ");
    195.       return ; 
    196.    }
    197.    if ((total[DEALER] >= total[PLAYER])&& (total[DEALER] < 21))
    198.    { 
    199.       printf("The house wins.
    ");
    200.       return ; 
    201.    }
    202.    printf("%s%c","You win!
    ",BELL);
    203.    return;
    204. }
    205. 
    206. char getAns(char mesg[])
    207. {
    208.    char ans;
    209.    printf("%s", mesg);
    210.    ans = getchar();
    211.    getchar();
    212.    return toupper(ans);
    213. }
    214. 
    215. void dispTitle(void)
    216. {
    217.    int i = 0 ;
    218.    while(i<25)
    219.    { 
    220.         printf("
    ");
    221.         i++; 
    222.    }
    223.    printf("
    
    *Step right up to the Blackjack tables*
    
    ");
    224.    return ;
    225. }
    View Code

       main()函数中player完成抽牌之后,立刻计算了player的点数:  

    52.       totalIt(playerPoints,total,PLAYER);

       这个计算结果基于ACE的点数被作为1或11两种可能性,取最好一种作为最后的结果。 

    161. void totalIt(int points[2],int total[2],int who)
    162. {
    163.    if ( (points[ACELOW] == points[ACEHIGH])
    164.       ||(points[ACEHIGH]  >  21 ))
    165.    { 
    166.      total[who] = points[ACELOW];
    167.    }
    168.    else
    169.    { 
    170.        total[who] = points[ACEHIGH];
    171.    }
    172.    
    173.    if (who == PLAYER )
    174.    {
    175.       printf("You have a total of %d
    
    ", total[PLAYER]);
    176.    }
    177.    else
    178.    {
    179.        printf("The house stands with a total of %d
    
    ", 
    180.        total[DEALER]);
    181.    }
    182.    return;
    183. }

       这个函数让我们得以领略什么叫思路含糊和废话连篇。首先 

    163.    if ( (points[ACELOW] == points[ACEHIGH])
    164.       ||(points[ACEHIGH]  >  21 ))
    165.    { 
    166.      total[who] = points[ACELOW];
    167.    }
    168.    else
    169.    { 
    170.        total[who] = points[ACEHIGH];
    171.    }

       它的意思是当较高点数超过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];
    

        “?:”这个三目运算在这里应用得恰到好处。

      有些人对三目运算有一种无名的恐惧,鼓吹所谓“尽量不要用三目运算符”。这是毫无道理的,这种无理源自无知。他们自己不会用刀,于是就骗人骗己地宣称使用木棍强于用刀。 

    173.    if (who == PLAYER )
    174.    {
    175.       printf("You have a total of %d
    
    ", total[PLAYER]);
    176.    }
    177.    else
    178.    {
    179.        printf("The house stands with a total of %d
    
    ", 
    180.        total[DEALER]);
    181.    }

       这一段同样拖泥带水,其实它的效果和下面写法没有本质区别: 

       printf( "%s a total of %d
    
    ", 
               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
    
    ", 
                     who == PLAYER ? "You have" : "The house stands with",
                     total[who] );
          
          }
    

       计算了完player的点数之后,按照规则在main()中由dealer继续抽牌(前面已抽过第一张牌)。dealer抽牌的策略是不到17点则继续,据代码作者说现实中的庄家的策略也是如此。

    53.       do
    54.       {
    55.          dealerGetsCard(&numCards,cards,dealerPoints);
    56.       }
    57.       while ( dealerPoints[ACEHIGH] < 17 );

       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”。按软件工程的说法,这叫需求不清,是比代码错误更加严重的错误。

      紧接着,矛盾出现了。 

    59.       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 );
  • 相关阅读:
    音频播放
    控制器的创建和控制器View的创建
    UIWebView之获取所点位置的图片(放大webView中的图片)
    SQLite的应用
    数据库操作(SQLite)
    计算文件(文件夹)大小
    本人专访微信魔法表情“米悠”作者流星剑
    Swift给每个开发者赢取500万的机会!不看一生后悔。
    疯狂猜图产品和盈利模式分析
    开发者改变世界的初心!
  • 原文地址:https://www.cnblogs.com/pmer/p/3183254.html
Copyright © 2011-2022 走看看