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 );
  • 相关阅读:
    欧拉回路 定理
    UESTC 1087 【二分查找】
    POJ 3159 【朴素的差分约束】
    ZOJ 1232 【灵活运用FLOYD】 【图DP】
    POJ 3013 【需要一点点思维...】【乘法分配率】
    POJ 2502 【思维是朴素的最短路 卡输入和建图】
    POJ 2240 【这题貌似可以直接FLOYD 屌丝用SPFA通过枚举找正权值环 顺便学了下map】
    POJ 1860【求解是否存在权值为正的环 屌丝做的第一道权值需要计算的题 想喊一声SPFA万岁】
    POJ 1797 【一种叫做最大生成树的很有趣的贪心】【也可以用dij的变形思想~】
    js 实现slider封装
  • 原文地址:https://www.cnblogs.com/pmer/p/3183254.html
Copyright © 2011-2022 走看看