一、实验目标
1)体验敏捷开发中的两人合作。
2)进一步提高个人编程技巧与实践。
二 、实验内容
1)根据以下问题描述,练习结对编程(pair programming)实践;
2)要求学生两人一组,自由组合。每组使用一台计算机,二人共同编码,完成实验要求。
3)要求在结对编程工作期间,两人的角色至少切换 4 次;
4)编程语言不限,版本不限。建议使用 Python 或 JAVA 进行编程。
三、队员信息
杜家云 | 杜蒙蒙 | |
博客地址 | https://www.cnblogs.com/cloudyyy/ | https://www.cnblogs.com/qingmuling/ |
github地址 | https://github.com/cloudy-y/game_of_life | https://github.com/qingmuling/my-travel-plans-1 |
四、代码规范
1、变量名不可以是关键字,尽量做到见名知意。
2、宏定义里变量名全大写。
3、函数名采用小驼峰式命名法。
4、缩进正确
5、注释简明易懂
五、程序的总体设计
六、结对编程过程
本次结对编程实验我们队主要是在qq上使用qq屏幕分享以及在github上提交代码来完成的。
第一次互换 | 杜蒙蒙同学完成矩阵初始化模块,杜家云同学进行纠错。 |
第二次互换 | 杜家云同学完成下一时刻细胞分布模块与矩阵输出模块,杜蒙蒙同学进行纠错。 |
第三次互换 | 杜蒙蒙同学对功能模块进行整合,调用,完善程序,杜家云同学进行纠错。 |
第四次互换 | 程序完成后出错,杜家云同学进行修改 |
第五次互换 | 杜蒙蒙同学对程序进行进一步完善,修改。程序完成 |
七、功能模块
上一阶段我们队初步讨论是使用老师建议的python代码编写程序,但是这周经过深入讨论觉得我们还是对c/c++代码更加熟悉一些,于是觉得改成C++代码编写程序。
1、初始化矩阵
使用随机函数随机选择细胞的初始生存状态。
1 void InitMap() //初始化细胞矩阵 2 { 3 killAll(); 4 5 srand((unsigned)time(NULL)); //用时间做种,每次产生随机数不一样 6 7 for (int i = 0; i < WIDTH; ++i) 8 { 9 for (int j = 0; j < HEIGHT; ++j) 10 { 11 int Alive = rand() % 2; //产生0或1的随机数 12 setCurCell(i, j, Alive); 13 } 14 15 } 16 }
初始化细胞状态后,设置矩阵
1 void setCurCell(int x, int y, int Alive) //设置当前细胞矩阵细胞存活状态 2 { 3 if (locValid(x, y) == 0) 4 { 5 return; 6 } 7 else 8 { 9 SCell* cell = getCell(current_map, x, y); //getCell函数为获得细胞的生存状态 10 if (cell - current_map >= WIDTH * HEIGHT) 11 { 12 return; 13 } 14 cell->Alive = Alive; 15 } 16 }
2、下一时刻细胞矩阵
根据生命游戏规则来计算当前细胞下一时刻的生存状态。下面程序是用来计算周围八个位置存活的细胞数量
1 int getAroundCellNum(int x, int y) //计算周围存活细胞数量 2 { 3 int count = 0; 4 5 if (locValid(x, y) == 0) //边界判断 6 { 7 return -1; 8 } 9 //测试目标位置周围的八个相邻位置 10 for (int i = x - 1; i <= x + 1; ++i) 11 { 12 for (int j = y - 1; j <= y + 1; ++j) 13 { 14 if (i == x && j == y) 15 { 16 continue; 17 } 18 if (locValid(i, j) == 1) 19 { 20 if (getCellAlive(i, j) == 1) 21 { 22 count++; 23 } 24 } 25 } 26 } 27 28 return count; 29 }
以下程序用来计算下一时刻的细胞矩阵,其规则如下:
每个细胞的生死遵循下面的原则:
①如果一个细胞周围有3个细胞为生(一个细胞周围共有8个细胞),则该细胞为生(即该细胞若原先为死,则转为生,若原先为生,则保持不变) 。
② 如果一个细胞周围有2个细胞为生,则该细胞的生死状态保持不变;
③在其它情况下,该细胞为死
1 void nextStep(void) //根据规则计算下一时刻细胞矩阵 2 { 3 int aroundNum = 0; 4 for (int i = 0; i < WIDTH; ++i) 5 { 6 for (int j = 0; j < HEIGHT; ++j) 7 { 8 aroundNum = getAroundCellNum(i, j); 9 if (aroundNum == 2) 10 { 11 setNewCell(i, j, getCellAlive(i, j)); 12 } 13 else if (aroundNum == 3) 14 { 15 setNewCell(i, j, 1); 16 } 17 else 18 { 19 setNewCell(i, j, 0); 20 } 21 } 22 } 23 swapMap(); 24 }
3、输出矩阵
本阶段实验我们队并未实现GUI界面的编程,只是输出0/1矩阵,用0,1代码表示细胞的生死,在下周的实验中我们会做关于生命游戏的界面,并进一步去完善程序,使其能够实现更多的功能。
1 void print() //输出细胞矩阵 2 { 3 int k; 4 for(int i=0;i) 5 { 6 for(int j=0;j ) 7 { 8 k=getCellAlive(i,j); 9 if(k==0) printf("0 "); 10 else if(k==1) printf("1 "); 11 } 12 printf("\n"); 13 } 14 15 }
以下是输出结果预览图
4、清屏
清屏功能是将细胞全部杀死,即矩阵全部置0。本次实验中虽然编写了该模块,但并未调用,这是为后续界面做的基础功能,在下一阶段界面设计好后会添加清屏功能。
1 void killAll(void) //清屏 2 { 3 if (current_map != NULL && new_map != NULL) 4 { 5 for (int i = 0; i < WIDTH; ++i) 6 { 7 for (int j = 0; j < HEIGHT; ++j) 8 { 9 setCurCell(i, j, 0); 10 setNewCell(i, j, 0); 11 } 12 } 13 } 14 15 }
4月5日更新
5、界面功能
本次界面功能我与杜家云同学多次交换角色,没有明确的区分驾驶员和领航员,我们在网络上查找了很多关于windows编程的资料,最后主要在CSDN找到了一位博主学习windows编程的学习笔记,以下是笔记之一的链接
https://blog.csdn.net/nullccc/article/details/81188355
跟着这位博主的学习笔记,让我们对于windows编程有了很大的了解,并能够根据这个,进行简单的绘制窗口。以下是程序的主要绘制代码,由于windows编程绘制窗口涉及的代码很多,下面代码仅涉及绘制生命游戏世界。
1 void DrawWorld(CWorld * world, int world_w, int world_h, HDC hdc) 2 { 3 CleanWorld(hdc); 4 DrawGrid(hdc, world_w, world_h); 5 DrawCell(world, hdc); 6 } 7 8 //将世界涂成黑色(背景色) 9 void CleanWorld(HDC hdc) 10 { 11 HPEN BlackPen = CreatePen(PS_SOLID, 1, RGB(0,0,0));//建立画笔 12 HBRUSH BlackBrush = CreateSolidBrush(RGB(0,0,0));//建立阴影画刷 13 SelectObject(hdc, BlackPen);//选用GDI对象 14 SelectObject(hdc, BlackBrush); 15 16 //画矩形 17 Rectangle(hdc, 0, 0, WORLD_WIDTH * CELL_SIZE, WORLD_HEIGHT * CELL_SIZE); 18 DeleteObject(BlackPen);//删除GDI对象 19 DeleteObject(BlackBrush); 20 } 21 22 //描画所有细胞 23 void DrawCell(CWorld* world, HDC hdc) 24 { 25 HPEN WhitePen = CreatePen(PS_SOLID, 1, RGB(255,250,240)); 26 HBRUSH WhiteBrush = CreateSolidBrush(RGB(255,250,240)); 27 SelectObject(hdc, WhitePen); 28 SelectObject(hdc, WhiteBrush); 29 for (int i = 0; i < world->getWidth(); ++i) 30 { 31 for (int j = 0; j < world->getHeight(); ++j) 32 { 33 if (world->getCellAlive(i, j) == 1) 34 { 35 Rectangle(hdc, i * CELL_SIZE, j * CELL_SIZE, i * CELL_SIZE + CELL_SIZE, j * CELL_SIZE + CELL_SIZE); 36 } 37 } 38 } 39 40 DeleteObject(WhitePen); 41 DeleteObject(WhiteBrush); 42 } 43 44 //描画网格 45 void DrawGrid(HDC hdc, int w, int h) 46 { 47 HPEN GrayPen = CreatePen(PS_SOLID, 1, RGB(128, 128, 128)); 48 SelectObject(hdc, GrayPen); 49 for (int i = 0; i <= w; ++i) 50 { 51 MoveToEx(hdc, i * CELL_SIZE, 0, NULL);//移动目标 52 LineTo(hdc, i * CELL_SIZE, h * CELL_SIZE);//画线 53 } 54 for (i = 0; i <= h; ++i) 55 { 56 MoveToEx(hdc, 0, i * CELL_SIZE, NULL); 57 LineTo(hdc, w * CELL_SIZE, i * CELL_SIZE); 58 } 59 60 DeleteObject(GrayPen); 61 }
以下是预览图
八、github项目
下图是我们的github项目仓库
下面两张图是我的提交记录
提交图1
提交图2
九、实验总结
结对编程过程中遇到的问题:
1、在第一阶段讨论各模块的功能时,我们觉得边界细胞的计算与内部细胞计算方式应该不同,故而准备在边界设一圈死细胞,方便里面的细胞计算,但后来在编写代码时我们尝试编写讨论发觉完全没必要,只需要多加一个边界判断模块,就可以很轻松的解决边界问题。
2、同样是在第一阶段讨论时,我们准备在初始化的时候,一开始默认为死,初始化活细胞,其余默认为死,而本次经过讨论后,直接使用随机函数,为细胞
赋值0或1
3、在下一时刻细胞矩阵模块中,由于我们在编写代码时的配合不够默契,导致最后整合功能模块代码的时候出现了错误。修改后,运行成功。
4、代码完成后,由于我们的程序只能运行一次,只能观察到一次单步演化,故而增加了循环后,可以多次进行单步演化。
5、在添加了界面功能之后,由于一开始我们是在win32 application下编写的程序,并没有报错,但之后在将代码复制给对方时,却发现直接运行.cpp文件会报错,上网查询后发现,我们是因为windows子系统设置错误,将Console子系统换成windows子系统后就没有报错了。
本次结对编程相对于以前传统的单人编程给我的感觉很不一样,一个人写代码时很容易陷入自己的思维误区,而结对编程则可以很好的避免这一点,结对编程工作效果绝对是1+1>2的。而由于我和队友组队时间不长,故而彼此之间还不够默契,有些地方难免会磨合不到位,有些小失误,但也能基本解决问题。本次实验让我对于c++的windows编程有了很大的认识,并对于c++类的了解有了很大的进步。但由于时间关系,还有很多功能没能实现。