劣质代码评析——《写给大家看的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
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. }

  现在回到main()继续代码走查。

   38.       dealerGetsCard(&numCards,cards, dealerPoints);

   这句话的意图是实现计算机(作为dealer)抽一张牌,并且这张牌的点数两个游戏者能看得见。其函数类型声明为: 

    22. void dealerGetsCard(int *numCards,int cards[52],
    23. int dealerPoints[2]);

   这个函数类型声明写得同样很不地道,原因前面提到过,这里不再复述。应该把它改为: 

    void dealerGetsCard ( int *numCards , int cards[] , int dealerPoints[] );

  该函数的函数定义为: 

    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. }

   有不少初学者不定义几个根本没必要的变量就不会写代码,这个函数犯的也是这种幼稚病。

  newCard这个变量明显多余。此外函数的头部也需要修改。形参numCards名字的毛病这里暂时就不改了: 

    void dealerGetsCard(int *numCards,int cards[],int dealerPoints[])
    {
       printf("The dealer draws:");
       dispCard ( dealCard( numCards , cards ) , dealerPoints ) ;
    }

   这里又涉及到了dealCard()函数和dispCard(),先来看dealCard()函数。

    19. int dealCard(int * numCards,int cards[52]);
    
    
    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));    /* From 0 to numcards */
    124.    cardDrawn = cards[subDraw];
    125.    cards[subDraw] = cards[*numCards -1];
    126.    (*numCards)--;
    127.    return cardDrawn;
    128. }

   函数原型方面的问题这里就不再提了。

  这个函数中有这样两层意思,首先在剩下的牌(共* numcards 张)中随机确定一张牌(第subDraw张),然后再模拟把这张牌从整副牌中抽走(cards[subDraw] = cards[*numCards -1]; (*numCards)--;)。

  这个函数有这样一些错误和毛病: 

    121.    time_t t;

   这个变量定义是毫无意义的,后面进一步说明。 

    122.    srand(time(&t));

   此函数调用的目的是设置伪随机数的种子(Seed),其中有两个错误:

  第一,&t作为实参是毫无必要的。因为这里只需要time()函数的返回值,而不需要通过变量t保存并带回时间的值。这时的代码应该这样写

    srand( time ( NULL ) );

  第二个错误是,需要得到伪随机数列时,只需要在最初设置一次种子,而不是反复设置种子。也就是说这个srand()只需要在每次游戏的初期调用一次,以后则毋庸再调用。那么,很显然,这个函数调用绝对不应该出现在dealCard()这个函数中,而应该出现在main()函数的do-while语句之中,如下面所示。 

    int main(void)
    {
       /*……*/
    
       do{
            srand( time ( NULL ) );
            /*21点游戏*/
        }
       while( getAns("
Play again(Y/N)?") == 'Y' );  /*询问是否继续*/
    
        return 0;
    }

  dealCard()函数中的 

   123.    subDraw = ( rand() % (*numCards) );    /* From 0 to numcards */

   代码本身没有问题,但注释是错误的。作者忘记了numcards在这个函数中是一个指针,而且rand() % (*numCards) 所能得到的值只可能在 0 ~ (*numCards)-1 这个范围。

  这个函数的返回值即为抽到的牌的点数,这个点数作为实参调用dispCard ()函数。即

    dispCard ( dealCard( numCards , cards ) , dealerPoints ) ;
    //原115.    dispCard(newCard,dealerPoints);

(未完待续)

原文地址:https://www.cnblogs.com/pmer/p/3181078.html