一、实验目标
1)体验敏捷开发中的两人合作。
2)进一步提高个人编程技巧与实践。
二 、实验内容
1)根据以下问题描述,练习结对编程(pair programming)实践;
2)要求学生两人一组,自由组合。每组使用一台计算机,二人共同编码,完成实验要求。
3)要求在结对编程工作期间,两人的角色至少切换 4 次;
4)编程语言不限,版本不限。建议使用 Python 或 JAVA 进行编程。
博客内容应该包括:
1、代码规范
2、程序的总体设计(附图说明模块之间的关系)
3、程序结对编程过程(附图)及功能实现情况(附代码和图)
4、项目github地址(附图)
5、实验总结
三、实验过程及截图记录
1、代码规范
虽说结对编程利大于弊,但对于我们这些代码掌握不牢靠的人来说,弊端还是很明显的。在来回交换角色的过程中,我跟李同学几次都不能与对方心有灵犀,几乎就是各写各的。因为疫情原因,我跟李同学分隔两地,通过语音电话实验结对编程,各有各的想法,各有各的追求,我们通过语音电话很混乱的表达自己的想法以及实现后的效果。在简单的代码的地方,每个人都想多写点,在复杂代码的地方,每个人又都只写两段。在来回三次艰难却愉快的写代码阶段完成之后,我们又来了一轮对代码的润滑和规范美化,其中包括了:
(1)因为英语单词的存储量有限,有的函数可以用英文单词命名,有的不知道英语单词的就用了拼音。所以在最后一次交换修改代码中,我们把所有的函数名都统一用拼音表示了,简单易明。
(2)在一个工程中,难免有大量的代码,尤其是不止一个人写代码的时候,这个时候为了让编程能力弱的人立马明白你写的代码,注释是一个很重要的条件。在我们合作的代码中,几乎行行都有代码的注释,首先是为了让对方能很快的明白自己想表达的意思,其次也是为了以后复盘的时候可以让自己更清晰。
(3)在编程中,有许多程序语言可以选择,根据我们自己的能力,我们在java、python、c/c++中选择了c/c++。在一个语言中,一种命令可以有多种执行方式,在c++代码中,我们规定了,注释统一使用“//”,函数名统一使用拼音。
2、程序的总体设计
李同学和我我们起初看过几篇生命游戏的小程序,有的画面非常精美,我们本来想花点时间做出那种精美的带有色彩的小程序。后来被现实打败,多少次我们都想不出来那个代码究竟应该怎么写。最后我们一拖再拖拖到了我们放弃精美画面的时候。在这个不精美的小程序中,黑点点代表细胞,黑点点消失了就代表细胞死亡。
(1)流程图
(2)函数
void start(); //选择开始,初始化
void zuotu(MOUSEMSG *_m); //作图函数
void shengsi(); //生死判断
void shuru(); //输入函数
void shuchu(); //输出函数
void dy_world(); //打印函数
void shubiao(); //鼠标控制
void huitushebei(); //建立绘图
void jianpanjiankong(); //键盘控制
3、程序结对编程过程(附图)及功能实现情况(附代码和图)
我们通过QQ里的屏幕分享进行互动,李同学的麦有点问题,我们同时又在微信上进行语音通话。
(1)第一次交换角色。李同学创建了远程仓库,并且已经指定了部分所需头文件,定义了小程序里的一些参数。我fork了她的仓库,然后也初步声明了一些需要用到的函数。目前还没有写出函数。这是我们第一次做结对编程,这一步更是结对编程里的第一不,所以我们前进的异常困难。fork部分,我又重新学习了上一次实验报告里的知识。在pull request的时候,我没有成功提交pull request,导致李同学没有办法merge我的pull request,在这里我们耽搁了很久。
(2)第二次交换角色。李同学完成了 start() 和 jianpanjiankong()函数的编写,我完成了完成了 huitushebei() ,shubiao(),zuotu(MOUSEMSG *_m)函数的编写。有上一步的经验,我们这次的操作熟练了并且有自信了许多。就是写代码的时候略微困难,复杂的、困难的函数几乎都在这一步。
(3)第三次交换角色。完成kongjian(int x1, int x2, char *s),shengsi()函数的编写,我完成 打印,输入,输出函数。在这一步里,我的函数没有什么难度,我们很快就完成了。李同学的函数在这里出现了卡壳,不过后来经过百度询问,得到了解决。我们已经感觉到胜利就在眼前,并且不像之前那么愁眉苦脸了。
(4)第四次交换角色。代码部分已经写完了,在这一part里,我们对代码进行了美化规范。李同学提出我的代码格式没有缩进,我说c对缩进不敏感所以我就没在意,她说这个样子的代码很难看,然后一点一点把我的代码进行了缩进。我在已经有的注释上修改了一些模糊的注释。至此,我们的结对编程算是完美落幕了。
(5)代码和运行结果
1 //李一,编写了所需的头文件 2 #include<graphics.h>//安装了EASY X才能打开这个头文件。 3 #include<stdio.h> 4 #include<stdlib.h> 5 #include<windows.h> 6 #include <conio.h> 7 #include <time.h> 8 __int8 world[102][202] = {0}; 9 IMAGE imgLive, imgEmpty; // 定义活细胞和无细胞区域的图案 //创建两个图像对象。 10 11 MOUSEMSG m; //定义鼠标消息结构体的变量,并定义指向它的指针 12 MOUSEMSG *_m = &m; 13 14 char s1[20] = "速度:1", s2[20] = " "; //速度值字符串s1,s2 15 int Speed = 125; // 游戏速度(毫秒) 16 17 //王一,定义要用到的若干个函数,以及 main 函数的编写 18 void start(); //世界初始化 19 void kongjian(int x1, int x2, char *s); //控件函数 20 void zuotu(MOUSEMSG *_m); //作图 21 void shengsi(); //生死判断 22 void shuru(); //输入函数 23 void shuchu(); //输出函数 24 void dy_world(); //打印世界 25 void shubiao(); //鼠标监控与操作 26 void RandWorld(); //随机设置世界 27 void huitushebei(); //建立绘图设备 28 void jianpanjiankong(); //键盘监控 29 30 int main() 31 { 32 33 34 initgraph(1800, 900); //创建一个尺寸为 1800x900 的绘图环境,原点坐标(0,0),位于左上角 35 start(); //世界初始化 36 huitushebei(); //建立绘图设备 37 while (true) 38 { 39 jianpanjiankong(); //键盘监控 40 shubiao(); //鼠标监控与操作 41 dy_world(); // 绘制世界 42 shengsi(); // 生死判断 43 Sleep(Speed); 44 } 45 return 0; 46 } 47 48 //李二,完成了 start() 和 jianpanjiankong()函数的编写 49 void start() 50 { 51 memset(world, 0, 102 * 202 * sizeof(bool)); //初始化world数组 52 setbkcolor(WHITE);// 设置背景色为白色 53 cleardevice();// 用背景色清空屏幕 54 setfillcolor(BLACK); //设置填充颜色为黑色 55 kongjian(0, 0, " "); 56 setcolor(BROWN); // 设置字体颜色为棕色 57 outtextxy(900, 5, s1); //打印速度值 58 setcolor(BLACK); // 设置字体颜色为黑色 59 outtextxy(400, 5, "Q/清空 W/保存 E/加载 S/随机"); //打印文字 60 } 61 void jianpanjiankong() //键盘监控 62 { 63 if (_kbhit()) 64 { 65 char c = _getch(); //读取键盘 //Speed等于450时,此处暂停 66 67 if (c >='0' && c <= '9') 68 { 69 sprintf(s2, "%d", c - '0'); //将速度值写入字符数组s2中 70 strcpy(s1, "速度:"); //初始化s1 71 strcat(s1, s2); //将s2连接到s1后 72 setcolor(BROWN); // 设置字体颜色为棕色 73 outtextxy(900, 5, s1); //打印速度值 74 if (c == '0') 75 Speed = 900; 76 else 77 Speed = ('9' - c) * 25; //根据输入数字调整速度 78 } 79 switch (c) 80 { 81 case 'q': 82 start(); //世界初始化 83 break; 84 case 'w': 85 shuchu(); //输出操作 86 break; 87 case 'e': 88 shuru(); //输入操作 89 break; 90 91 case 's': 92 RandWorld(); // 创建一个细胞随机分布的世界 93 dy_world(); // 绘制世界 94 break; 95 96 case ' ': 97 c = '/'; 98 setcolor(LIGHTRED); // 设置字体颜色为亮红色 99 outtextxy(1400, 5, "暂停"); //打印文字 100 while (c != ' ') 101 { 102 shubiao(); //鼠标监控与操作 103 Sleep(500); 104 if (_kbhit()) 105 { 106 c = _getch(); 107 dy_world(); // 绘制世界 108 shengsi(); // 生死判断 109 } 110 } 111 setfillcolor(WHITE); //设置当前填充颜色为白色 112 solidrectangle(1400, 5, 1450, 20); //打印白色方格,覆盖“暂停”文字 113 setfillcolor(BLACK); //设置当前填充颜色为黑色 114 break; 115 } 116 } 117 } 118 //王二,完成了 huitushebei() ,shubiao(),zuotu(MOUSEMSG *_m)函数的编写 119 void huitushebei() //建立绘图设备 120 { 121 // 调整世界图案的大小 122 Resize(&imgLive, 9, 9); // 123 Resize(&imgEmpty, 9, 9); //用于调整指定绘图设备的尺寸。 124 125 // 绘制有生命世界的图案 126 SetWorkingImage(&imgLive); //用于设定当前的绘图设备。 127 setfillcolor(BLACK); //设置当前填充颜色为黑色 128 setcolor(LIGHTGRAY); //设置边框颜色为白色 129 fillrectangle(0, 0, 8, 8); //打印黑色方格,有边框 130 131 // 绘制无生命世界的图案 132 SetWorkingImage(&imgEmpty); 133 setfillcolor(WHITE); //设置当前填充颜色为白色 134 solidrectangle(0, 0, 8, 8); //打印白色方格 135 136 // 恢复对默认窗口的绘图 137 SetWorkingImage(NULL); 138 } 139 void shubiao() //鼠标监控与操作 140 { 141 142 143 int x1 = 0, x2 = 0; //x1,x2存储单个控件的左右边的X坐标位置 144 while (MouseHit()) //检测是否有鼠标消息 145 { 146 m = GetMouseMsg(); //获取鼠标消息 147 if (m.mkLButton || m.mkRButton) //鼠标左右键是否有按下 148 { 149 150 if (m.y > 26) //m.y>26说明鼠标位于作图区,否则位于控件区 151 { 152 do 153 { 154 if (m.y > 26) 155 zuotu(_m); //将鼠标信息传递给做图函数 156 m = GetMouseMsg(); //获取鼠标消息 157 } while (m.mkLButton || m.mkRButton); //直到松开按键才结束作图 158 } 159 else //判断鼠标位于哪个控件 160 { 161 if (2 < m.x&&m.x < 63) 162 { 163 x1 = 2; x2 = 63; //第一个控件的位置 164 char s[] = "清空"; 165 kongjian(x1, x2, s); //实现控件动画 166 x1 = 0; x2 = 0; 167 168 start(); 169 170 } 171 if (72 < m.x&&m.x < 133) 172 { 173 x1 = 72; x2 = 133; //第二个控件的位置 174 char s[] = "保存"; 175 kongjian(x1, x2, s); //实现控件动画 176 x1 = 0; x2 = 0; 177 178 shuchu(); //输出操作 179 180 } 181 if (142 < m.x&&m.x < 203) 182 { 183 x1 = 142; x2 = 203; //第三个控件的位置 184 char s[] = "加载"; 185 kongjian(x1, x2, s); //实现控件动画 186 x1 = 0; x2 = 0; 187 188 shuru(); //输入操作 189 } 190 if (212 < m.x&&m.x < 273) 191 { 192 x1 = 212; x2 = 273; //第三个控件的位置 193 char s[] = "随机"; 194 kongjian(x1, x2, s); //实现控件动画 195 x1 = 0; x2 = 0; 196 197 RandWorld(); // 创建一个细胞随机分布的世界 198 dy_world(); // 绘制世界 199 200 } 201 if (282 < m.x&&m.x < 343) 202 { 203 x1 = 142; x2 = 203; //第三个控件的位置 204 char s[] = "结束"; 205 kongjian(x1, x2, s); //实现控件动画 206 x1 = 0; x2 = 0; 207 208 exit(0); //结束程序 209 } 210 211 }; 212 } 213 } 214 FlushMouseMsgBuffer(); //清空鼠标消息缓冲区 215 } 216 void zuotu(MOUSEMSG *_m) //作图 217 { 218 int x, y, xb, yb; 219 220 xb = _m->x / 9+1; 221 yb = (_m->y - 26) / 9+1; 222 x = (xb - 1) * 9; 223 y = (yb - 1) * 9 + 26; 224 if (_m->mkLButton) 225 { 226 putimage(x, y, &imgLive); //按左键时,打印方格,有边框 227 world[yb][xb] = 1; 228 } 229 if (_m->mkRButton) 230 { 231 putimage(x, y, &imgEmpty); //按右键时,删除方格 232 world[yb][xb] = 0; 233 } 234 235 } 236 //李三 完成kongjian(int x1, int x2, char *s),shengsi()函数的编写 237 void kongjian(int x1, int x2, char *s) //控件函数 238 { 239 if (x1 == 0 && x2 == 0) //若x1,x2==0,则初始化控件 240 { 241 setcolor(BLACK);// 设置字体颜色为黑色 242 setfillcolor(WHITE); //设置填充颜色为白色 243 244 fillrectangle(2, 2, 63, 23); //画白色填充矩形 245 outtextxy(15, 5, "清空"); //打印文字 246 247 fillrectangle(72, 2, 133, 23); //画白色填充矩形 248 outtextxy(85, 5, "保存"); //打印文字 249 250 fillrectangle(142, 2, 203, 23); //画白色填充矩形 251 outtextxy(155, 5, "加载"); //打印文字 252 253 fillrectangle(212, 2, 273, 23); //画白色填充矩形 254 outtextxy(225, 5, "随机"); //打印文字 255 256 fillrectangle(282, 2, 343, 23); //画白色填充矩形 257 outtextxy(295, 5, "结束"); //打印文字 258 259 setfillcolor(BLACK); //设置填充颜色为黑色 260 } 261 if (x1 != 0 && x2 != 0) //控件反馈效果 262 { 263 MOUSEMSG m; //定义鼠标消息结构体的变量 264 265 setcolor(BLACK);// 设置字体颜色为黑色 266 267 setfillcolor(LIGHTGRAY); //设置填充颜色为浅灰色 268 fillrectangle(x1, 2, x2, 23); //画浅灰色填充矩形 269 270 setbkcolor(LIGHTGRAY);// 设置背景色为浅灰色 271 outtextxy(x1 + 15, 5, s); //打印文字,与初始化相比向右移动2像素,实现动画效果 272 setbkcolor(WHITE);// 设置背景色为白色 273 274 do { m = GetMouseMsg(); } while (m.mkLButton || m.mkRButton); 275 276 setfillcolor(WHITE); //设置填充颜色为白色 277 fillrectangle(x1, 2, x2, 23); //画白色填充矩形 278 outtextxy(x1 + 15 - 2, 5, s); //打印文字,返回初始化位置 279 280 setfillcolor(BLACK); //设置填充颜色为黑色 281 } 282 } 283 void shengsi() //生死判断 284 { 285 __int8 tmp[102][202] = { 0 }; // 临时数组 286 int i, j; 287 int sum=0; 288 for (i = 1; i < 101;i++) 289 for (j = 1; j < 201; j++) 290 { 291 // 计算周围活着的生命数量 292 sum = world[i + 1][j] + world[i + 1][j - 1] + world[i][j - 1] + world[i- 1][j - 1] 293 + world[i - 1][j] + world[i - 1][j + 1] + world[i][j + 1] + world[i + 1][j + 1]; 294 295 // 计算当前位置的生命状态 296 switch (sum) 297 { 298 case 3: tmp[i][j] = 1; break; 299 case 2: tmp[i][j] = world[i][j]; break; 300 default: tmp[i][j] = 0; break; 301 } 302 } 303 memcpy(world, tmp, 102 * 202 * sizeof(bool)); //从tmp中复制102 * 102 * sizeof(__int8)长度的数据给world 304 305 306 } 307 //王三,完成 打印,输入,输出函数 308 void dy_world() //打印世界 309 { 310 int x, y; 311 int i, j; 312 for (i = 1; i <101; i++) 313 { 314 for (j = 1; j <201; j++) //两层循环根据方格状态存储器处理方格 315 { 316 x = (j - 1) * 9; 317 y = (i - 1) * 9+26; 318 putimage(x, y, world[i][j] ? &imgLive : &imgEmpty); //用于在指定位置,当前设备上绘制指定图像 319 } 320 } 321 } 322 323 void RandWorld() // 创建一个细胞随机分布的世界 324 { 325 326 srand((unsigned)time(NULL)); // 设置随机种子 327 328 for (int i = 1; i < 101; i++) 329 for (int j = 1; j <= 201; j++) 330 world[i][j] = rand() % 2; 331 } 332 void shuru() //输入函数 333 { 334 FILE *fp; 335 if ((fp = fopen("data", "r")) == NULL) //打开文件 336 { 337 setcolor(LIGHTRED); // 设置字体颜色为亮红色 338 outtextxy(1600, 5, "错误!"); //打印文字 339 exit(0); 340 } 341 342 int i, j; 343 char ch; 344 for (i = 1; i <101; i++) //两层循环赋值给tu[][] 345 { 346 for (j =1; j<201; j++) 347 { 348 ch=fgetc(fp); 349 switch (ch) 350 { 351 case 48: world[i][j]=0; break; //设置方格状态 352 case 49: world[i][j] = 1; break; //设置方格状态 353 } 354 355 } 356 } 357 dy_world(); //绘制世界 358 setcolor(LIGHTRED); // 设置字体颜色为亮红色 359 outtextxy(1600, 5, "加载成功!"); //打印文字 360 361 362 } 363 void shuchu() //输出函数 364 { 365 FILE *fp; //定义文件指针 366 if ((fp = fopen("data", "w")) == NULL) //打开文件并使fp指向文件 367 { 368 setcolor(LIGHTRED); // 设置字体颜色为亮红色 369 outtextxy(1600, 5, "错误!"); //打印文字 370 exit(0); 371 } 372 373 char ch; 374 int i, j; 375 for (i = 1; i<101; i++) //用两层循环向文件中输入数据 376 { 377 for (j = 1; j <201; j++) 378 { 379 switch (world[i][j]) 380 { 381 case 0: ch = '0'; break; 382 case 1: ch = '1'; break; 383 } 384 fputc(ch, fp); 385 } 386 } 387 fclose(fp); //关闭 388 389 setcolor(LIGHTRED); // 设置字体颜色为亮红色 390 outtextxy(1600, 5, "保存成功!"); //打印文字 391 }
开始界面,按S键随机开始
黑点代表细胞,黑点消失代表细胞死亡
4、项目github地址(附图)
github地址:https://github.com/lili-li1/practice
5、实验总结
我们两个实验合作的次数不少,但是这是第一次以这种形式合作。我有严重的拖延症,只要不到火烧眉毛我都不急,但是这次实验,李同学早早地就催促我快点做实验,快点想思路,快点写代码。跟她一起合作很开心。
这门课程让我们学到了很多新东西,git和github都是曾听未曾听闻的,这次可以使用git在github上实现我和李同学的异地合作,学习了新知识,李同学跟我都很兴奋,虽然刚开始困难重重,但是最后完成任务时的兴奋和喜悦是很真实的。