结对编程
黄金点游戏
黄金点游戏是一个数字小游戏,其游戏规则是:
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/xiaoyongwu
上述完了题目与设计,下面就该我们的吴小勇登场了,也是本次我结对编程的队友——吴小勇。
这是吴小勇同学的博客地址:http://www.cnblogs.com/wuxiaoyong/
以下是我们准备开始编程的照片、编程过程中的照片、以及最后进行调试和测试的照片:
吴小勇同学是我们班的班长,当初知道和他在一个组时我感到很荣幸,作为我们的班长,他对工作负责,对同学们也关怀有加,在以往的学习生活当中给予我们很大的帮助,经常在我们生活学习出现苦恼的时候来安慰我们,为我们解答疑惑,对于这次编程,我感觉到很困惑,怕拖他后腿,但是他还是很细心的和我来商量,告诉我慢慢来,不要太着急的下结论,在吴小勇耐心的指导下,我们开始着手编程。
在编程的过程当中我们遇到了很多问题,由于以前我们用结构数组用的比较少,而这次我们俩是用的结构数组的存储玩家信息,先动态的分配了M个玩家的地址,然后再往其中填上数据,所以在内存的分配与释放上我们俩遇到了小小的困难。事实证明两个人编程比一个好,不到一会儿,我们俩就弄清事情的原委。还有一次我们俩在结构数组指针的传递上出现了分歧,因为用的结构数组,需要传递给子函数数组指针,在用一个*还是两个*问题上我们俩出现了初学者的困惑,但是后来一看C语言书就明白过来了。当然还有其他的一些小问题,这儿就不一一列举了。
在代码规范上面,我觉得我们俩人还是比较和性格的,是吴小勇先写的代码,轮到我的时候,由于我并不是很会编程,所以是基本上是照着吴小勇之前的代码风格写下来的,尽管中间出现了一些小的问题,但是都不大。吴小勇就像一个大哥哥一样,慢慢的带领我们程序一步一步走向正轨,有时候我被代码弄得都几乎想要放弃,但是班长还是耐心的为我解答,后来,我开始有了自信,大胆去写。再之后就没有出现什么大的问题。
总的来说,此次的编程我觉得是成功的,在班长的一步一步带领下,我觉得对程序这个世界有了一定的兴趣,虽然我还是个新手,但是只要有班长在我身边,我就感觉有了依靠,一点一点的去写,在一步一步的坚持下,终于和班长共同完成了这个课题,寻求进步,我很感谢老师给我这次和班长一起合作的机会,让我感受到了在班长的带领下的进步,差距产生追赶,追赶推动进步,进步迈向成功,成功促使就业,就业便有机会当上CEO,迎娶白富美,走向人生巅峰。为了美好的未来,加油!
下面讲讲我们的程序吧:
首先我们用了一个people结构体,保存了玩家的序号、提交的数字、成绩等等信息,并且起了一个PLAYER的别名。紧跟着的是我们的子函数,包括:传值函数、录取信息函数、排序函数、释放内存函数、计算函数、显示函数等等。
struct People { int n_num;//游戏人员序号 float digit;//所选数字 float gap;//所选数字与黄金数字的差距 int S_score;//本轮成绩 int score;//总成绩 }; typedef struct People PLAYER; PLAYER *give(PLAYER **ppla,int num);//把ppla赋给ppla2 PLAYER *Getinf(PLAYER **ppla,int i,int m);//获取玩家信息 void sort_S_score(PLAYER **ppla2,int num);//每轮游戏之后进行选择排序 void sort_score(PLAYER **ppla,int num);//游戏结束之后按照总分进行选择排序 void freememory(PLAYER **ppla,int num);//释放结构体数组的内存 float count_gold(PLAYER **ppla,int num);//计算黄金点 void count_score(PLAYER **ppla,int num,float g_num);//计算分数 void show(PLAYER **ppla2,int num);//显示每轮游戏结果
接下来是该程序最重要的主函数部分:
主函数是这样设计的:首先定义了count变量,用做判断是否循环玩游戏,输入玩家人数Num以及游戏的轮次数id,然后动态分配结构体数组的空间给ppla、ppla2以及创建一个保存游戏每轮得分信息的文件gold.txt。再依次建立每个玩家的信息,然后根据所输入的信息计算出黄金点G值,然后在根据G值计算出每个玩家提交的数字与G值的距离,并且给与相应的分数。再把得分信息存入文件当中,然后根据得分把每一轮进行排序。等到最后一轮结束,再根据总成绩进行排序,并且从文件中直接读取信息输出每位玩家在每一轮当中的得分情况等。最后询问是继续游戏还是退出游戏。
void main(int argc, char *argv[]) { int count=1; while(count) { int id=0,num=0; view(id,num); //动态建立结构体数组 PLAYER **ppla; PLAYER **ppla2; ppla=(PLAYER **)malloc (num * sizeof(PLAYER *)); if(ppla==NULL) { printf("not set the ppla\n"); return ; } ppla2=(PLAYER **)malloc (num * sizeof(PLAYER *)); if(ppla2==NULL) { printf("not set the ppla2\n"); return ; } //打开文件 FILE *fp; fp=fopen("gold.txt","w"); if(fp==NULL) { printf("cano't open gold.txt\n"); exit(0); } //开始每一轮的玩家信息输入、最终结果的输出 int m=1; while(id) { printf("-----第%d轮-----\n",m); //建立每个玩家的信息 int i=0; for(i=0;i<num;++i) { ppla[i]=Getinf(ppla,i,m); if(ppla[i]==NULL) { printf("error\n"); freememory(ppla,i); return; } } //计算黄金点 float g_num=0; g_num=count_gold(ppla,num); //计算得分 count_score(ppla,num,g_num); //把本轮信息存入文件中 int g=0; for(g=0;g<num;++g) { fprintf(fp,"\t%d\t%f\t%f\t%d\t\t%d\n",ppla[g]->n_num,ppla[g]->digit,ppla[g]->gap,ppla[g]->S_score,ppla[g]->score); } //把ppla的值给ppla2并对本轮排序 int s=0; for(s=0;s<num;++s) { ppla2[s]=give(ppla,s); } sort_S_score(ppla2,num); //输出每一轮的排名得分情况 printf("\t\t黄金点数字为:%f\n\n",g_num); show(ppla2,num); int cls=0; printf("是(1)否(0)清屏?\n"); scanf("%d",&cls); if(cls==1) system("CLS"); m++; id--; } fclose(fp); //按照总分排序 int yy=0; printf("=====游戏已结束,是(1)否(0)按照总分成绩进行排序?\n"); scanf("%d",&yy); if(yy==1) { sort_score(ppla,num); show(ppla,num); } //查看每一轮成绩 int xx=0,xy=0,g=0; m=m-1; xy=num; printf("\n=====游戏已结束,是(1)否(0)查看各轮比赛成绩?\n"); scanf("%d",&xx); if(xx==1) { char ch[100]; fp=fopen("gold.txt","r"); num=m*num; int j=1; for(g=0;g<num;++g) { fgets(ch,50,fp); if((g%xy)==0) { printf("\t\t\t第%d轮情况\n",j); j=j+1; printf("\t序号\t提供数字\t黄金间距\t本轮成绩\t总成绩\n"); } printf("%s",ch); } } freememory(ppla,xy); freememory(ppla2,xy); printf("====继续游戏请输入(1),退出游戏请输入(0)======\n>>>"); scanf("%d",&count); if(count==1) system("CLS"); } }
接下来介绍一下显示函数:显示函数包括两个,一个是输出每轮游戏的结果,还有一个是输出游戏规则等,直接用输出语句就行了。
//显示每轮游戏结果 void show(PLAYER **ppla2,int num) { int i=0; printf("\t\t\t排名情况\n"); printf("\t序号\t提供数字\t黄金间距\t本轮成绩\t总成绩\n"); for(i=0;i<num;++i) { printf("\t%d\t%f\t%f\t%d\t\t%d\n",ppla2[i]->n_num,ppla2[i]->digit,ppla2[i]->gap,ppla2[i]->S_score,ppla2[i]->score); } } void view(int &id,int &num) { printf("=====================================================================\n"); printf(" Welcome To The Gold Game \n"); printf("----------------------------------------------------------------------\n"); printf("游戏规则:N个同学(N通常大于10),每人写一个0~100之间的有理数\n"); printf("\t(不包括0或100),交给裁判,裁判算出所有数字的平均值,然后乘以\n"); printf("\t0.618(所谓黄金分割常数)得到G值。提交的数字最靠近G(取绝对值)\n"); printf("\t的同学得到N分,离G最远的同学得到-2分,其他同学得0分。\n"); printf("----------------------------------------------------------------------\n"); printf("\t==========请输入玩家人数==========\n\t>>"); scanf("%d",&num); printf("\t==========请输入游戏轮数==========\n\t>>"); scanf("%d",&id); }
然后是获取玩家信息函数,给子函数传入结构体数组指针,玩家人数,游戏轮次等,第一轮的时候需要给结构体数组中添加每个玩家的结构体,需要动态分配新结构体,然后把结构体按照序号放入ppla当中,并且给玩家的提交数字、总分、每轮得分、间距等赋初值。
//获取玩家信息 PLAYER *Getinf(PLAYER **ppla,int i,int m) { if(m==1) { PLAYER *p; p=(PLAYER *) malloc (sizeof(PLAYER)); if(p==NULL) { return NULL; } p->n_num =i+1; printf("\t\t请输入第%d个玩家选择的有理数字(0-100)\n\t\t>>>>",i+1); scanf("%f",&p->digit ); printf("\n"); p->gap =0; p->S_score =0; p->score =0; return (p); } else { printf("\t\t请输入第%d个玩家选择的有理数字(0-100)\n\t\t>>>>",i+1); scanf("%f",&ppla[i]->digit ); printf("\n"); ppla[i]->gap =0; ppla[i]->S_score =0; return (ppla[i]); } }
下面介绍的是计算函数,计算函数也是两个,一个是计算黄金点,一个是计算得分情况。首先跟据玩家提供的数字进行求平均值,然后*0.618再返回黄金点值。再根据黄金点值计算出每位玩家提供的数字与gold_num的距离,找到最小的min和最大的max(其中包含了多个人提交相同数字的情况),然后在给相应的玩家分数。
//计算黄金点 float count_gold(PLAYER **ppla,int num) { float sum=0,gold=0; int j=0; for(j=0;j<num;++j) { sum += ppla[j]->digit; } sum=sum/num; gold = float(sum * g_gold); return gold; } //计算分数 void count_score(PLAYER **ppla,int num,float g_num) { //把间距计算出来存入结构体中 int k=0; float min=100,max=0; for(k=0;k<num;++k) { ppla[k]->gap = float(fabs(ppla[k]->digit - g_num)); //找到最近的数字 if(ppla[k]->gap < min) { min=ppla[k]->gap ; } //找到最远的数字 if(ppla[k]->gap >max) { max=ppla[k]->gap ; } } for(k=0;k<num;++k) { //避免有很多人选中同一个数字的情况 //本轮成绩为+2、0、-2,总成绩加(减)2 if(ppla[k]->gap ==min) { ppla[k]->S_score =num; ppla[k]->score =ppla[k]->score +ppla[k]->S_score; } else if(ppla[k]->gap ==max) { ppla[k]->S_score =-2; ppla[k]->score =ppla[k]->score +ppla[k]->S_score; } else { ppla[k]->score =ppla[k]->score +ppla[k]->S_score ; } } }
下面是排序函数,排序我们选用的是简单的选择排序,直接对每一轮的得分、总分进行排序。
//每轮游戏之后按照本轮得分情况进行选择排序 void sort_S_score(PLAYER **ppla2,int num) { PLAYER *p; int i,j,k; for(i=0;i<num-1;++i) { k=i; for(j=i+1;j<num;++j) { if(ppla2[j]->S_score > ppla2[k]->S_score ) { k=j; } } if(k!=i) { p=ppla2[i]; ppla2[i]=ppla2[k]; ppla2[k]=p; } } } //游戏结束之后按照总分进行选择排序 void sort_score(PLAYER **ppla,int num) { PLAYER *p; int i,j,k; for(i=0;i<num-1;++i) { k=i; for(j=i+1;j<num;++j) { if(ppla[j]->score > ppla[k]->score ) { k=j; } } if(k!=i) { p=ppla[i]; ppla[i]=ppla[k]; ppla[k]=p; } } }
最后就是内存的释放了:当玩家退出游戏后,直接把动态数组销毁,释放内存。
//释放内存 void freememory(PLAYER **ppla,int num) { int i; for(i=0;i<num;++i) { free(ppla[i]); } free(ppla); }
下面是运行截图:
测试一:11个玩家,玩4轮游戏,前两轮游戏每个玩家输入数据相同,得出结果是:一个玩家满分,一个玩家负满分,其他人0分。后两轮随意输入,得出并不是提交的数越小越容易得分,取决于大家总体的提交数字,相对来说在提交的数当中处于中等偏下的数字越有优势。
测试二:5个玩家,玩4轮,第一轮所有玩家输入相同的数字,得出所有玩家都得分的结论;第二,三,四轮随意输入,计算得分。最后一张为gold.txt中保存的每轮游戏信息。