结对编程——黄金点小游戏的设计与实现
个人感想
最近总是很纠结,关于如何平衡考研时间和学校课程时间,还有关于考研资料的选择。就是在这样一种状态下,我迎来了结对编程项目,两个人共同编程完成一个小游戏,说实话,刚开始时我对这个项目不怎么感兴趣,我总觉得应该把时间都放在考研上。还有一个原因就是,我觉得自己什么都不会,肯定会拖累队友,但是经过我们多次沟通后,我发现这个小游戏没有我想象中的那么难,而且我逐渐有点兴趣了,再加上和队友的多次沟通,我觉得结对编程是一件很有意思的事,两个人提出不同的想法然后讨论,再接着看着自己的想法都能慢慢的实现出来,而且它的优势就在于比个人编程完善很多。
我的队友
我的队友是明仁江,他呢,在我看来人挺好的,对待这次项目很认真,准确来说,在我印象中,他做什么都很认真。他真的教了我很多东西,刚开始时,主动找我讨论对这个小游戏的想法,我们讨论了很多次,每次他都特别耐心的给我解释我不懂的地方,我和他刚开始都不会做界面,他就一点一点的学着做,每次有了新的进展都会一起看,他是一个很有能力的人,在软件工程课上表现的很优秀,开课没多久就把构建之法全书读完了,然后每次的博客写的都很细致,步骤写的很详细,逻辑思维很清晰,这点我必须要学习,我的博客写的一直都不怎么好。以后会多学习别人的优点,不断地改进。总之,我给我的队友大大的赞。
程序设计
本次结对编程目标是:N个同学(N通常大于10),每人写一个0~100之间的有理数 (不包括0或100),交给裁判,裁判算出所有数字的平均值,然后乘以0.618(所谓黄金分割常数),得到G值。提交的数字最靠近G(取绝对值)的同学得到N分,离G最远的同学得到-2分,其他同学得0分。每次游戏至少可以运行10轮以上,并能够保留每轮比赛结果。
本次结对编程使用结构体来保存玩家信息和游戏成绩信息,分别是结构体数组player和achieve。还包含了五个基本函数分别是信息输入函数Input、玩家数字和黄金点比较函数Campare、裁判函数Referee、分数从高到低排序函数Sort、分数输出函数Output。
我的队友完成了程序的整个框架,和信息输入函数Input、裁判函数Referee,同时,队友还帮助实现了玩家数字和黄金点比较函数Campare、按分数从高到低排序函数Sort等。
程序实现(C语言)
玩家信息保存结构体Game:
struct game { int number; float digit; int score; };
int类型变量number为玩家编号,浮点类型变量digit为玩家输入的数字,int类型变量score为玩家成绩。
该程序的核心函数:
void Input(int peoplecount); //信息输入 void Campare(int peoplecount, float goldpoint); //比较函数 void Referee(int gamecount, int peoplecount); //输赢判断 void Sort(int peoplecount); //分数排序 void Output(int peoplecount); //分数输出
玩家信息输入函数:
//玩家信息输入 void Input(int peoplecount) { int i; for (i = 0;i < peoplecount;i++) { player[i] = new game; printf("\t第%d号玩家,请输入数字:", i + 1); player[i]->number = i; scanf("%f", &player[i]->digit); } printf("\n"); }
依次输入玩家的信息,包括玩家的编号、玩家的数字。
裁判函数:
//输赢判断 void Referee(int gamecount, int peoplecount) { int i; float sum = 0, goldpoint = 0; for (i = 0;i < peoplecount;i++) { sum += player[i]->digit; } printf("\t本轮游戏的平均数为:%f。\n", sum / peoplecount); goldpoint = (sum / peoplecount)*(0.618); printf("\t本轮游戏的黄金点为:%f。\n", goldpoint); printf("\n"); Campare(peoplecount, goldpoint); }
在该函数中,用float类型sum变量来保存本轮游戏的平均数,float类型goldpoint变量保存本轮游戏的黄金点,还调用了玩家输赢比较函数Compare,以判断输赢玩家,并对所有玩家的分数输出。
玩家输赢比较函数:
//比较函数 void Campare(int peoplecount, float goldpoint) { int i; float max, min, winpeople = 0, lospeople = 0; max = min = fabs(player[0]->digit - goldpoint); for (i = 0;i < peoplecount;i++) { if (fabs(player[i]->digit - goldpoint) <= min) //找到最接近黄金点的数 { winpeople = player[i]->digit; min = fabs(player[i]->digit - goldpoint); } if (fabs(player[i]->digit - goldpoint) >= max) //找到离黄金点最远的数 { lospeople = player[i]->digit; max = fabs(player[i]->digit - goldpoint); } } for (i = 0;i < peoplecount;i++) { if (player[i]->digit == winpeople) { achieve[i]->score += peoplecount; printf("\t第%d号玩家赢。\n", i + 1); } } for (i = 0;i < peoplecount;i++) { if (player[i]->digit == lospeople) { achieve[i]->score -= 2; printf("\t第%d号玩家输。\n", i + 1); } } printf("\n"); }
在本函数中,先用float类型变量winpeople、lospeople分别保存离黄金点最近的数字和离黄金点最远的数字。给赢家和输家加分数或者减分数,再依次输出和winpeople、lospeople相等的玩家的编号。最后,对每个玩家的分数输出。
按分数从高到底排序函数Sort:
void Sort(int peoplecount) { int i, j; for (i = 0;i < peoplecount;i++) { int tempsignal = i, tempnumber = achieve[i]->number, tempscore = achieve[i]->score; tempnumber = achieve[i]->number; tempscore = achieve[i]->score; for (j = i + 1;j < peoplecount;j++) { if (tempscore< achieve[j]->score) { tempsignal = j; tempnumber = achieve[j]->number; tempscore = achieve[j]->score; } } achieve[tempsignal]->number = achieve[i]->number; achieve[tempsignal]->score = achieve[i]->score; achieve[i]->number = tempnumber; achieve[i]->score = tempscore; } }
该函数实现了玩家分数从大到小的排序。采用的排序方法是冒泡排序法。
玩家分数输出函数:
void Output(int peoplecount) { int i; for (i = 0;i < peoplecount;i++) { printf("\t第%d号玩家:%d。\n", achieve[i]->number + 1, achieve[i]->score); } }
该函数实现了玩家分数的依次输出。
程序运行(C语言)
程序开始界面:
程序开始界面,首先显示游戏规则,再提示输入游戏轮次和玩家个数,按回车,开始玩游戏。
第一轮游戏:
提示输入各个玩家的数字,然后计算出平均值和黄金点,判断哪些玩家赢,哪些玩家输。最后提示是否打印成绩。
打印第一轮游戏成绩:
第二轮游戏:
第二轮成绩:
游戏继续...
最后一轮游戏:
最后一轮游戏结果:
按分数从高到底显示各个玩家分数:
选择是否继续游戏。
程序实现(C#语言)
程序入口:
using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace GoldPoint_Game { static class Program { /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new frmWelcome()); } } }
在program类中,运行程序欢迎窗体frmWelcome。
欢迎窗体:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace GoldPoint_Game { public partial class frmWelcome : Form { public frmWelcome() { InitializeComponent(); } private void textBox1_TextChanged(object sender, EventArgs e) { } private void frmWelcome_Load(object sender, EventArgs e) { } private void button1_Click(object sender, EventArgs e) { GoldGame f1 = new GoldGame(textBox2.Text,textBox3.Text); f1.ShowDialog(); this.Close(); } private void textBox2_TextChanged(object sender, EventArgs e) { } private void button2_Click(object sender, EventArgs e) { this.Close(); } } }
在该窗体中中,实现了对欢迎窗体的初始化,包括游戏规则的提示,游戏轮次、玩家个数的输入,退出游戏、进入游戏提示等。
游戏窗体:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace GoldPoint_Game { partial class GoldGame : Form { ...... } }
游戏窗体是该程序的主体部分。
一个Goldgame类和两个player,achieve列表:
class Goldgame { public int number; public float digit; public int score; }; List<Goldgame> player = new List<Goldgame> { }; List<Goldgame> achieve = new List<Goldgame> { };
两个列表用于保存玩家的信息和玩家的成就。
两个整型变量gameCount、peopleCount:
private int gameCount; private int peopleCount; public int GameCount { get { return gameCount; } set { gameCount = value; } } public int PeopleCount { get { return peopleCount; } set { peopleCount = value; } } int rgameCount; int rpeopleCount; public GoldGame(string str1,string str2) { InitializeComponent(); this.gameCount = Convert.ToInt32(str1); this.peopleCount = Convert.ToInt32(str2); } public GoldGame() { InitializeComponent(); }
通过函数重载的方法,实现两个整型变量gameCount和peopleCount用于接收欢迎窗体传过来的游戏轮次和游戏玩家个数的值。
游戏窗体Load事件:
private void Form1_Load(object sender, EventArgs e) { int i; rgameCount = 1; rpeopleCount = 1; for (i = 0; i <= peopleCount; i++) { Goldgame players = new Goldgame(); players.number = rpeopleCount-1; players.digit = 0; players.score = 0; player.Add(players); Goldgame achieves = new Goldgame(); achieves.number = rpeopleCount-1; achieves.digit = 0; achieves.score = 0; achieve.Add(achieves); rpeopleCount++; } rpeopleCount = 1; label3.Text = "第" + rgameCount + "轮游戏:"; textBox1.Text = Convert.ToString(rpeopleCount); textBox2.Text = "0"; }
该事件中,实现了对玩家信息列表和游戏成就列表的赋初值,和对各个控件的信息设置。
游戏输赢比较函数Campare:
void Campare(float goldpoint) { int i; float max, min, winpeople = 0, lospeople = 0; max = min = System.Math.Abs(player[1].digit - goldpoint); for (i = 1; i <= peopleCount; i++) { if (System.Math.Abs(player[i].digit - goldpoint) <= min) //找到最接近黄金点的数 { winpeople = player[i].digit; min = System.Math.Abs(player[i].digit - goldpoint); } if (System.Math.Abs(player[i].digit - goldpoint) >= max) //找到离黄金点最远的数 { lospeople = player[i].digit; max = System.Math.Abs(player[i].digit - goldpoint); } } for (i=1; i <= peopleCount; i++) { achieve[i].digit = player[i].digit; } listBox1.Items.Add('\n'); if (winpeople == lospeople) { listBox1.Items.Add("玩家数字一样,故不判断输赢!"); } else { for (i = 1; i <= peopleCount; i++) { if (player[i].digit == winpeople) { achieve[i].score += peopleCount; listBox1.Items.Add("第" + i + "号玩家赢。"); } } for (i = 1; i <= peopleCount; i++) { if (player[i].digit == lospeople) { achieve[i].score -= 2; listBox1.Items.Add("第" + i + "号玩家输。"); } } } listBox1.Items.Add('\n'); for (i = 1; i <= peopleCount; i++) { listBox1.Items.Add("第" + i + "号玩家," +"数字为:"+achieve[i].digit+ ",分数为:" + achieve[i].score); } }
首先找到离黄金点最近和最远的输,然后对离黄金点最近和最远的玩家进行分数的加和减操作,接着输出赢家或者输家。最后输出所有人的积累分数。
裁判函数Referee:
private void Referee() { int i; float sum = 0, goldpoint = 0; for (i = 1; i <= peopleCount; i++) { sum += player[i].digit; } label3.Text = "第"+rgameCount+"轮游戏结果如下:"; listBox1.Items.Clear(); listBox1.Items.Add("第"+rgameCount+"轮游戏平均数为:"+sum/peopleCount); goldpoint = Convert.ToSingle(sum / peopleCount * 0.618); listBox1.Items.Add("第" + rgameCount + "轮游戏黄金点为:" + sum / peopleCount*0.618); Campare(goldpoint); }
首先计算出所有玩家的分数之和,再算出平均数,输出平均数。再算出黄金点,输出黄金点,信息均输出到listBox1中。其中调用了输赢比较函数Campare。
输出分数按钮的Click事件:
private void button1_Click(object sender, EventArgs e) { if (rpeopleCount < peopleCount) { listBox1.Items.Add(Convert.ToString("玩家编号:"+ rpeopleCount+","+"玩家数字:" + textBox2.Text)); player[rpeopleCount].number = rpeopleCount; player[rpeopleCount].digit = Convert.ToSingle(textBox2.Text); rpeopleCount++; } else if((rpeopleCount == peopleCount)) { listBox1.Items.Add(Convert.ToString("玩家编号:" + rpeopleCount + "," + "玩家数字:" + textBox2.Text)); player[rpeopleCount].number = rpeopleCount; player[rpeopleCount].digit = Convert.ToSingle(textBox2.Text); textBox1.Text = Convert.ToString(rpeopleCount); listBox1.Text = "第"+rgameCount+"游戏成绩如下:"; Referee(); MessageBox.Show("全部玩家已输入数字,请点击下一轮游戏!", "输入提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } textBox1.Text = Convert.ToString(rpeopleCount); }
对玩家信息的输入,同时显示到listBox1中,输入完毕,宣布本轮游戏的游戏成绩并提示进入下一轮。
下轮游戏按钮的Click事件:
private void button4_Click(object sender, EventArgs e) { if (rgameCount < gameCount) { rgameCount++; label3.Text = "第" + rgameCount + "轮游戏:"; rpeopleCount = 1; textBox1.Text = Convert.ToString(rpeopleCount); listBox1.Items.Clear(); } else { label3.Text = "第" + rgameCount + "轮游戏:"; textBox1.Text = Convert.ToString(rpeopleCount); MessageBox.Show("游戏结束,请点击公布成绩!", "输入提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } }
实现了对每轮游戏的玩家编号的初始化及各个控件的设置。若游戏结束,则会提示游戏结束并提示点击公布成绩。
按分数从高到底排序函数Sort:
private void Sort() { int i, j; for (i = 1; i <= peopleCount; i++) { int tempsignal = i, tempnumber = achieve[i].number, tempscore = achieve[i].score; tempnumber = achieve[i].number; tempscore = achieve[i].score; for (j = i + 1; j <= peopleCount; j++) { if (tempscore < achieve[j].score) { tempsignal = j; tempnumber = achieve[j].number; tempscore = achieve[j].score; } } achieve[tempsignal].number = achieve[i].number; achieve[tempsignal].score = achieve[i].score; achieve[i].number = tempnumber; achieve[i].score = tempscore; } }
该函数实现了对分数从高到底的排序,使用的排序方法是冒泡排序法。
公布成绩按钮的Click事件:
private void button2_Click(object sender, EventArgs e) { Sort(); int i; label3.Text = "游戏分数排名:"; listBox1.Items.Clear(); for (i = 1; i <= peopleCount; i++) { listBox1.Items.Add("玩家编号:" + achieve[i].number + "," + "玩家分数:" + achieve[i].score); } }
该事件对游戏窗体的lLoad事件,实现了对玩家信息列表和游戏成就列表的清空,游戏轮次、游戏玩家的初始化以及各个控件的设置。
private void button5_Click(object sender, EventArgs e) { this.Close(); }
实现对窗口的关闭。
程序运行(C#语言)
游戏欢迎界面:
游戏开始界面:
第一轮游戏:
所有玩家输入数字,提示进入下一轮游戏提示:
所有玩家输入数字,提示进入下一轮游戏提示:
游戏继续...
所有游戏轮次结束,提示公布成绩:
游戏分数排名:
点击重新开始:
若需继续游戏,则各玩家输入数字,继续游戏。若需退出游戏,请点击退出游戏。
下面附上几张讨论时的照片:
编程体会
在这次编程过程中,我体会到了基础知识的重要性,从刚开始的什么都不太懂,到最后和我的队友共同完成这次结对编程,我收获了很多,碰到不会的地方就一起讨论,或者查资料。我和队友起初都不会做界面,然后一点一点的学,看c#的书,终于做出来了,可能不是很完美,但是我们尽了最大的努力。
我觉得结对编程比一个人编程能达到的效果会好很多,两个人的想法会多一点,可以让程序的功能等各方面都改进很多,我相信付出的汗水总会有回报的。我体会到了从来没有过的成就感。以后会更加努力,相信可以做的更好。
我的队友博客链接:http://www.cnblogs.com/visionming
队友coding地址:https://coding.net/u/mingrenjiang
我的coding地址:https://coding.net/u/sunxingzi