劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(五)
来源:互联网 发布:手机麻将 源码 编辑:程序博客网 时间:2024/05/17 02:02
0. #include <stdio.h>1. #include <time.h>2. #include <ctype.h>3. #include <stdlib.h>4. 5. #define BELL '\a'6. #define DEALER 07. #define PLAYER 18. 9. #define ACELOW 010. #define ACEHIGH 111. 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. do43. {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. do54. {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. else153. { 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. else169. { 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. else178. {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. else153. { 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. do43. {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 );
并且把它抽象为一个函数。
<script type="text/javascript"><!--google_ad_client = "ca-pub-1944176156128447";/* cnblogs 首页横幅 */google_ad_slot = "5419468456";google_ad_width = 728;google_ad_height = 90;//--></script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
- 劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(五)
- 劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(六)
- 劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(六)
- 劣质代码评析——《写给大家看的C语言书》附录B之21点程序(八)
- 劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(三)
- 写给大家看的C语言书(第2版)的评论
- 《写给大家看的设计书(第3版)》
- 写给大家看的项目管理书(第3版)——互动出版网
- 写给大家看的设计书(第4版)(精装版)——互动出版网
- 前端编程提高之旅(五)----写给大家看的css书
- 劣质代码评析——猜数字问题
- 久负盛名的C语言神奇入门书写:给大家看的C语言书(第2版)
- 《写给大家看的c语言书》重点记录
- 《写给大家看的Web设计书(第3版)》即将上市
- 好书推荐-《写给大家看的设计书(第3版)》
- 写给大家看的设计书 (第3版)
- 《写给大家看的c语言书》中所用部分函数解析
- 读书笔记——写给大家看的设计书
- Timus 1203. Scientific Conference
- AJAX 全选、反选删除
- 设计模式--工厂方法模式
- 初入手势识别(2)
- 转:【分享】.Net有哪些大型项目、大型网站的案例?
- 劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(五)
- hunter学算法-从一个数组中找到和最大的子数组,数组中可能有负数。
- 2.3 常见内核数据结构 : 存储系统数据结构
- 努力吧,现在也不晚(目前唯一一个转载文章,用于激励自己)
- openssl engine
- c程序调用shell命令方法
- 跟着BOY学习开发cocos2d-x 游戏 实战篇(1)之 loading 界面编写
- Androidpn的使用[找不到org.androidpn.client.PersistentConnectionListener
- ADB用法