黄金点游戏
-
项目要求
黄金点游戏是一个数字小游戏,其游戏规则是:
N个同学(N通常大于10),每人写一个0~100之间的有理数 (不包括0或100),交给裁判,裁判算出所有数字的平均值,然后乘以0.618(所谓黄金分割常数),得到G值。提交的数字最靠近G(取绝对值)的同学得到N分,离G最远的同学得到-2分,其他同学得0分。玩了几天以后,大家发现了一些很有意思的现象,比如黄金点在逐渐地往下移动。
1、本作业属于结对编程项目,必须由二人共同完成,并分别将本次作业过程发到博客,同时将本次作业源代码提交到codeing系统;
2、如果可能的话尽量以C/S或B/S方式实现,即利用服务器接收和处理所有玩家提交的数字,并将结果反馈给各玩家,玩家可以通过客户端提交的数字;
3、如果采用单机方式实现的话,需要为用户提供便利的输入界面;
4、该游戏每次至少可以运行10轮以上,并能够保留各轮比赛结果。
-
成员介绍
成员:赵伟莨 ,杜娟
我的coding地址:https://coding.net/u/zhaoweiliang
杜娟的博客地址:http://www.cnblogs.com/dujuan105/
coding地址:https://coding.net/u/dujuan105
我的结对编程伙伴杜娟是一个很大方,随性的女孩,和她相处不会有什么拘谨,我们可以各抒己见,充分发挥各自的特长,她虽然稍稍有点女汉纸性格(ps:很容易相处,不拘小节),但是她真的是一个做事非常认真的人。对于程序的测试优化都做了许多努力,而且也提出了一些改进的建议。为了强化自己的编程能力,特地借了相关的书籍学习,我也看到了她的提高,在结对编程的期间我们互相鼓励,共同解决了许多问题。
-
功能实现
为了保存玩家信息,我们采用了结构体数组,结构体中的int name为玩家的编号,int score为比赛得分,int number为玩家输入的数字,int win,fail分别保存了玩家赢和输的次数,float b用于保存number和黄金点的差值的绝对值。
主函数用于绘制便于交互的菜单界面,用switch分支语句来判断用户所需执行的功能,从而调用相应的子函数实现功能。在while循环中以“w+”的方式创建文件a.txt(文件可读可写),这样既可以将玩家数据保存,又可以读取玩家信息。
#include "stdio.h" #include "stdlib.h" #include "windows.h" #include "math.h" typedef struct player { int name; int score; float number; float b; int win; int fail; }PLAYER; //结构体保存了玩家的编号,分数等重要信息 void setup_game(FILE *fp); //游戏的初始化的函数 void print_rule(); //打印游戏规则的函数 void set_score(PLAYER *p,float ave,int num,FILE *fp);//计算比赛得分的函数 void save_file(FILE *fp,PLAYER *p,int n,int num);//将比赛成绩保存到文件中 void lookup(FILE *fp,int num2,int a); //从文件从读取玩家信息 int main() { int select; FILE *fp; system("color 9E"); while(1) { if((fp=fopen("a.txt","w+"))==NULL) { printf("无法创建文件保存记录!\n"); exit(0); } printf("\t\t\t ********黄金点游戏********\n\n"); printf("\t\t\t\t1. 开始游戏PK\n"); printf("\t\t\t\t2. 游戏规则^_^\n"); printf("\t\t\t\t3. 退出游戏\n"); scanf("%d",&select); switch(select) { case 1: setup_game(fp); break; case 2: print_rule(); break; case 3: exit(0); break; default: { printf("\t\t\t 您的输入有误,请重新输入\n"); Sleep(1000); //计时器,一秒后执行system("cls")清屏 system("cls"); break; } } } return 0; }
void setup_game(FILE *fp);此函数用于初始化数据,函数中调用子函数,可以实现用户所需的游戏功能。为了节省内存空间,我们采用了malloc函数动态分配结构体数组空间的方式先分配较小的空间,若玩家人数超过了所分配空间的大小,则调用realloc函数增加结构体数组的空间。在for循环中首先对数据初始化,计算出黄金点的值。然后将相应的参数传入子函数可以实现计算玩家的得分,获取玩家信息等功能。
void setup_game(FILE *fp) { PLAYER *p; int i,num1,num2,j; int flag=1; float sum,ave; char select; p=(PLAYER *)malloc(10*sizeof(PLAYER)); //动态分配结构体数组 printf("请输入玩家人数:"); scanf("%d",&num1); if(num1>10) { p=(PLAYER *)realloc(p,num1*sizeof(PLAYER));//若所分配的空间不足,则增加空间 } printf("请输入比赛轮次:"); scanf("%d",&num2); printf("\n"); for(j=0;j<num2;j++) { printf("第%d轮比赛:\n",j+1); for(i=0,sum=0 ;i<num1;i++) { if(flag==1) { p[i].name=i+1; p[i].score=0; //初始化操作,赋初值为0 p[i].win=0; p[i].fail=0; } printf("\t\t\t\t玩家%d: ",p[i].name); scanf("%f",&p[i].number); sum+=p[i].number; } ave=sum/num1; ave=(float)(ave*0.618); //计算黄金点的值 printf("\n\t\t\t黄金点G的值为:%f\n",ave); set_score(p,ave,num1,fp); save_file(fp,p,j,num1); flag=0; } rewind(fp); printf("比赛已完成,是否查看各轮成绩:Y or N:"); while(1) { flushall(); scanf("%c",&select); if(select=='y'||select=='Y') { int a; printf("请输入想要查询的轮次:"); scanf("%d",&a); system("cls"); lookup(fp,num2,a); Sleep(3000); system("cls"); break; } else if(select=='n'||select=='N') { break; } else { printf("您的输入有误!\n"); } } system("cls"); printf("最终战绩如下:\n"); for(i=0;i<num1;i++) { printf("\t\t\t玩家%d: ",p[i].name); printf("赢局次数:%d 败局次数:%d\n",p[i].win,p[i].fail); } Sleep(3000); system("cls"); }
void set_score(PLAYER *p,float ave,int num,FILE *fp);此函数可以实现计算本轮各个玩家的得分,比较得出本轮中各个玩家输入的数字与黄金点差值的绝对值的最大值max和最小值min,然后通过for循环对结构体数组进行遍历,若p[i].b与max相等,则p[i].score减2分,若p[i].b与min相等则加n分。同理,通过类似的方法,可以实现当前综合得分的最大值,最小值,从而判断玩家的输赢。
void set_score(PLAYER *p,float ave,int num,FILE *fp) { int i,win,fail; float max=(float)fabs(p[0].number-ave); float min=max; char ch; for(i=0;i<num;i++) { p[i].b=(float)fabs(p[i].number-ave); if(max<p[i].b) max=p[i].b; //max保留本轮比赛中与黄金点最大的差值 if(min>p[i].b) min=p[i].b; //min保留本轮比赛中与黄金点最小的差值 } printf("本轮比赛的得分:\n"); for(i=0;i<num;i++) { if(p[i].b==max) { p[i].score-=2; //若差值p[i].b与max相等,则减2分 fail=i; } if(p[i].b==min) { p[i].score+=num; //若差值p[i].b与min相等,则加n分 win=i; } } p[win].win+=1; p[fail].fail+=1; int max_score=p[0].score; int min_score=max_score; for(i=0;i<num;i++) //比较得出目前为止最高分和最低分 { if(p[i].score>max_score) max_score=p[i].score; if(p[i].score<min_score) min_score=p[i].score; } for(i=0;i<num;i++) { printf("\t\t\t\t玩家%d: %d",p[i].name,p[i].score); if(max_score==min_score) printf(" 平手\n"); else if(p[i].score==max_score) printf(" 赢家^_^\n"); else if(p[i].score==min_score) printf(" 输家\n"); else printf(" 继续加油\n"); } while(1) { printf("是否继续Y or N : "); flushall(); scanf("%c",&ch); if(ch=='y'||ch=='Y') { system("cls"); break; } else if(ch=='n'||ch=='N') { exit(0); } else { printf("您的输入有误!\n"); } } }
此函数用于显示游戏规则,可以实现显示5秒后自动清屏的功能。
void print_rule() { printf(" a. N个同学(N通常大于10),每人写一个0~100之间的有理数(不包括0或100)。\n"); printf(" b. 裁判算出所有数字的平均值,然后乘以0.618(所谓黄金分割常数),得到G值。\n"); printf(" c. 提交的数字最靠近G(取绝对值)的同学得到N分,离G最远的同学得到-2分,\n 其他同学得0分。\n\n"); Sleep(5000); system("cls"); }
void save_file(FILE *fp,PLAYER *p,int n,int num);用于实现将个轮次比赛成绩存入文件的功能,每轮比赛成绩的开头输入N是为了在读取文件是更加容易识别。 void lookup(FILE *fp,int num2,int a)函数可以实现对指定轮次比赛成绩的读取。首先识别'N',然后读取'N'之后的数字num,num表示比赛的轮次,若num与用户输入的数字相等,则读取后面的比赛成绩,否则寻找下一个'N'继续之前的操作,直到找出与用户输入的数字相等的num为止。
void save_file(FILE *fp,PLAYER *p,int n,int num) { int i; fprintf(fp,"N%d",n+1); for(i=0;i<num;i++) { fprintf(fp," %d",p[i].score); } fprintf(fp,"\n"); } void lookup(FILE *fp,int num2,int a) { char ch; int num,i,n=1,flag=0; while(1) { ch=fgetc(fp); if(ch=='N') fscanf(fp,"%d",&num); if(num==a) { char c[10]={"\0"}; ch=fgetc(fp); while(ch!='\n') { ch=fgetc(fp); for(i=0;ch!=' ';i++) { c[i]=ch; ch=fgetc(fp); if(ch=='\n') break; } printf("\t\t\t\tÍæ¼Ò%d£º",n); printf("%s·Ö\n",c); n++; for(i=0;isdigit(c[i])||c[i]=='-';i++) c[i]='\0'; } flag=1; } if(num!=a) continue; if(flag==1) break; } }
-
操作展示
游戏开始界面
显示游戏规则,5秒后清屏
第一轮战绩
第二轮完成后的综合战绩
查询第二轮战绩
显示比赛的所有轮次完成时的战绩
文件中存储的各轮比赛战绩
-
结对编程总结
本次结对编程让我收获良多。我们都体会到团队协作在完成一个项目时的重要性。每个人都有自己的特点,编程习惯各不相同,要想快速的读懂队员编写模块的程序就要求我们遵守相编程规范,不能一味的按照自己不规范的方式编程。每人在各自独立设计、实现软件的过程中不免要犯这样那样的错误。在结对编程中,因为有随时的复审和交流,程序中的错误就会少得多,程序的初始质量会高很多,这样会省下很多以后修改、测试的时间。
具体的说,结对编程有如下优点:
(1)在开发层次,结对编程能提供更好的设计质量和代码质量,两人合作能有更强的解决问题的能力。
(2)对开发人员自身来说,结对工作能带来更多的信心,高质量的产出能带来更高的满足感。
(3)在心理上, 当有另一个人在你身边和你紧密配合, 做同样一件事情的时候, 你不好意思开小差, 也不好意思糊弄。
(4)在企业管理层次上,结对能更有效地交流,相互学习和传递经验,能更好地处理人员流动。因为一个人的知识已经被其他人共享。
然而,在结对编程中也有一些缺陷:
(1)个人风格造成的问题,不同的人编写程序时的习惯不同,一时间难以改变。
(2)程序员因为意见不同,导致讨论的时间过长,浪费很多时间。
这次结对编程我们还试图对程序进行优化改进,所以有学习了C#以及各种控件的使用,但是由于初次使用C#语言,还不太熟练,程序中有些BUG没有处理完。还有一些的工作等待后续完成。界面图如下: