
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("\n"); 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("\nPlay 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!\n\n",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\n","Jack"); 135. points[ACELOW] += 10; 136. points[ACEHIGH] += 10; 137. break; 138. case(12): printf("%s\n","Queen"); 139. points[ACELOW] += 10; 140. points[ACEHIGH] += 10; 141. break; 142. case(13): printf("%s\n","King"); 143. points[ACELOW] += 10; 144. points[ACEHIGH] += 10; 145. break; 146. default : points[ACELOW] += cardDrawn; 147. if(cardDrawn==1) 148. { 149. printf("%s\n","Ace"); 150. points[ACEHIGH]+= 11; 151. } 152. else 153. { 154. points[ACEHIGH]+=cardDrawn; 155. printf("%d\n",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\n\n", total[PLAYER]); 176. } 177. else 178. { 179. printf("The house stands with a total of %d\n\n", 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.\n"); 190. return ; 191. } 192. if ( (total[DEALER] > 21) && (total[PLAYER] > 21) ) 193. { 194. printf("%s", "Nobody wins.\n"); 195. return ; 196. } 197. if ((total[DEALER] >= total[PLAYER])&& (total[DEALER] < 21)) 198. { 199. printf("The house wins.\n"); 200. return ; 201. } 202. printf("%s%c","You win!\n",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("\n"); 221. i++; 222. } 223. printf("\n\n*Step right up to the Blackjack tables*\n\n"); 224. return ; 225. }
繼續走查dispCard()函數:
20. void dispCard(int cardDrawn,int points[2]); 130. void dispCard(int cardDrawn, int points[2]) 131. { 132. switch(cardDrawn) 133. { 134. case(11): printf("%s\n","Jack"); 135. points[ACELOW] += 10; 136. points[ACEHIGH] += 10; 137. break; 138. case(12): printf("%s\n","Queen"); 139. points[ACELOW] += 10; 140. points[ACEHIGH] += 10; 141. break; 142. case(13): printf("%s\n","King"); 143. points[ACELOW] += 10; 144. points[ACEHIGH] += 10; 145. break; 146. default : points[ACELOW] += cardDrawn; 147. if(cardDrawn==1) 148. { 149. printf("%s\n","Ace"); 150. points[ACEHIGH]+= 11; 151. } 152. else 153. { 154. points[ACEHIGH]+=cardDrawn; 155. printf("%d\n",cardDrawn); 156. } 157. } 158. return ; 159. }
dispCard()函數的功能是顯示抽到的牌的點數並計算抽牌者目前的總點數。這個函數最主要的毛病是可讀性差,原因主要有兩點,第一,在switch語句中蹩腳地嵌套了一句if語句,實際上這個switch語句可以這樣寫:
void dispCard(int cardDrawn,int points[]); void dispCard(int cardDrawn, int points[]) { switch(cardDrawn) { case 11: puts("Jack"); points[ACELOW] += 10; points[ACEHIGH] += 10; break; case 12: puts("Queen"); points[ACELOW] += 10; points[ACEHIGH] += 10; break; case 13: puts("King"); points[ACELOW] += 10; points[ACEHIGH] += 10; break; case 1 :puts("Ace"); points[ACELOW] += 1 ; points[ACEHIGH] += 11; break; default :printf("%d\n",cardDrawn); points[ACELOW] += cardDrawn; points[ACEHIGH] += cardDrawn; break; } }
顯然要好看得多。
第二,就是不應把輸出牌面點數與計算點數放在這一個函數中同時完成,應該分為兩個函數。
void dispCard(int cardDrawn); void dispCard(int cardDrawn) { switch(cardDrawn) { case 11: puts("Jack"); break; case 12: puts("Queen"); break; case 13: puts("King"); break; case 1 :puts("Ace"); break; default :printf("%d\n",cardDrawn); break; } } void update(int cardDrawn,int points[]); void update(int cardDrawn, int points[]) { switch(cardDrawn) { case 11: case 12: case 13: points[ACELOW] += 10; points[ACEHIGH] += 10; break; case 1 :points[ACELOW] += 1 ; points[ACEHIGH] += 11; break; default :points[ACELOW] += cardDrawn; points[ACEHIGH] += cardDrawn; break; } }
這樣代碼更簡單。
現在回到main()函數,考察 dealerGetsCard(&numCards,cards, dealerPoints); 之后的代碼。
40. playerGetsCard(&numCards,cards,playerPoints); 41. playerGetsCard(&numCards,cards,playerPoints);
這兩行的意思是在dealer(計算機)取得一張牌后,player開始抽牌。由於player至少要抽兩張牌,所以連續兩處調用playerGetsCard()函數。然而查看一下這個函數的定義就會發現:
24. void playerGetsCard(int *numCards,int cards[52], 25. int playerPoints[2]); 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. }
這個函數其實與dealerGetsCard()函數是一樣的函數。同樣的函數寫了兩次,無論如何都是很垃圾的寫法。
寫代碼有一條基本原則——要“拽”DRY(Don’t repeat yourself)(參見http://www.cnblogs.com/pmer/archive/2011/07/16/2108436.html)。相同的函數定義了兩次就是不夠“拽”。
由於這兩個函數基本相同,所以很容易合並為一個。它們的差別只在
105. printf("You draw:");
和
114. printf("The dealer draws:");
這兩句,可以通過為函數增加一個參數的辦法統而為一,也可以把這兩句從函數中直接剝離,這時playerGetsCard()和dealerGetsCard()這兩個函數本身都沒有存在的必要性了。即在main()中直接可調用dispCard()函數:
int main(void) { /*……*/ do{ /*……*/ printf("The dealer draws:"); dispCard ( dealCard( numCards , cards ) , dealerPoints ) ; printf("You draw:"); dispCard ( dealCard( numCards , cards ) , playerPoints) ; /*……*/ } while( getAns("\nPlay again(Y/N)?") == 'Y' ); /*詢問是否繼續*/ return 0; }
main()中的
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' );
的作用是讓player輸入。如果輸入為H,則繼續抽取一張牌;如果輸入為S,則player取牌過程結束;如果輸入其他字符,則繼續提問。
這段代碼的邏輯有些復雜化。如果我寫,大概會按下面方式寫:
do { char ans; ans = getAns("Hit or stand (H/S)?"); if ( ans == 'H' ) playerGetsCard(&numCards,cards,playerPoints); if ( ans == ' S ' ) break; } while( 1 );
並且把它抽象為一個函數。