一、实验目标:
1)体验敏捷开发中的两人合作。
2)进一步提高个人编程技巧与实践。
二 、实验内容:
1)根据以下问题描述,练习结对编程(pair programming)实践。
2)要求学生两人一组,自由组合。每组使用一台计算机,二人共同编码,完成实验要求。
3)要求在结对编程工作期间,两人的角色至少切换 4 次。
4)编程语言不限,版本不限。建议使用 Python 或 JAVA 进行编程。
三、选题:
生命游戏:
生命游戏是英国数学家约翰.何顿.康威在1970年发明的细胞自动机,它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死亡的细胞。一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量。如果相邻方格活着的细胞数量过多,这个细胞会因为资源匮乏而在下一个时刻死去;相反,如果周围活细胞过少,这个细胞会因太孤单而死去。游戏在一个类似于围棋棋盘一样的,可以无限延伸的二维方格网中进行。例如,设想每个方格中都可放置一个生命细胞,生命细胞只有两种状态:"生"或"死"。图中,用黑色的方格表示该细胞为"死", 其它颜色表示该细胞为"生" 。游戏开始时, 每个细胞可以随机地(或给定)设定为"生"或"死"之一的某个状态, 然后,再根据如下生存定律计算下一代每个细胞的状态:
每个细胞的状态由该细胞及周围 8 个细胞上一次的状态所决定;
如果一个细胞周围有 3 个细胞为生,则该细胞为生,即该细胞若原先为死则转为生,若原先为生则保持不变;
如果一个细胞周围有 2 个细胞为生,则该细胞的生死状态保持不变;
在其它情况下,该细胞为死,即该细胞若原先为生则转为死,若原先为死则保持不变。
第一阶段开发:
1. 队伍基本资料:
队名:做的都队
潘武振博客地址(https://www.cnblogs.com/zuiaixinxin/)
饶童心博客地址(https://www.cnblogs.com/999xxx/)
仓库地址:https://github.com/pp9527/LifeGame/blob/master/LifeGame.py
2. 前期的交流讨论:
(1)结对编程的实现:由于疫情原因,正常的结对编程无法实现,我们选择比较普遍的QQ远程工具实现两个人控制一台电脑,然后通过QQ语音来进行代码开发时的交流,每半个小时切换一次电脑的控制权实现结对编程。
(2)开发工具的选择:我们都觉得对python更熟悉一些,而且python的库比较强大,可能会让我们选的生命游戏课题变得更加有意思,我们都很期待最后的成果,开发语言选定之后,因为之前有过一些pycharm的使用经验,我们进一步选择了pycharm作为我们的开发工具,也是一个比较好用的工具,使我们的开发更加方便。
(3)代码托管:代码托管在github上,使用git对其进行管理。
(4)代码的设计:对于生命游戏首先想到就是一个二维数组打印的结果,而活细胞和死细胞正好可以用0和1表示,这样就是一个由0和1组成的二位数组,这样生命游戏可以分为几个模块,首先要有初始化地图函数,然后根据生命法则演化出下一张地图的函数,然后打印地图,主函数循环打印实现细胞的动态演变,然后实现地图大小的可输入,也要有对输入进行合法性检测的函数。
首先看初始化函数,在python里面我们选择用列表来实现二位数组,用随机数函数对给定大小的列表赋初值,值为0或1,实现初始地图上细胞的状态和位置随机分布,入口参数为输入的地图长宽,出口参数为赋初值的二位列表。
生命法则函数,入口参数为上一个地图即二维列表,根据生命法则检查他周围八个位置的细胞状态,来决定他的下一个状态,全部检测完后返回下一张地图即新的二维列表。
打印函数,刚开始是打算用一些特殊符号来代表细胞的状态,但是后来觉得界面应该不太美观,后来经过查询资料,经过两个人的商讨觉得可以用python的pygame库来对界面做一下美化,效果应该更好。
第二阶段开发:
1. 代码规范:
本次实验我们用Python来编写程序,经过讨论我们总结了应该主要从以下几点特别注意:
(1) Python语句的缩进:
Pyhton语言与Java、C#等编程语言最大的不同点是,Python代码块使用缩进对齐表示代码逻辑,而不是使用大括号。这对习惯用大括号表示代码块的程序员来说,确实是学习Python的一个障碍。Python每段代码块缩进的空白数量可以任意,但要确保同段代码块语句必须包含相同的缩进空白数量。
(2) Python标识符
标识符用于Python语言的变量、关键字、函数、对象等数据的命名。标识符的命名需要遵循下面的规则。
① 可以由字母(大写A—Z或小写a—z)、数字(0—9)和_(下划线)组合而成,但不能由数字开头。
② 不能包含除_以外的任何特殊字符,如:%、#、&、逗号、空格等。
③ 不能包含空白字符(换行符、空格和制表符称为空白字符)。
④ 标识符不能是Python语言的关键字和保留字。
⑤ 标识符区分大小写,num1和Num2是两个不同的标识符。
⑥ 标识符的命名要有意义,做到见名知意。
(3) Python关键字
Python预先定义了一部分有特别意义的标识符,用于语言自身使用。这部分标识符称为关键字或保留字,不能用于其它用途,否则会引起语法错误,随着Python语言的发展,其预留的关键字也会有所变化。
(4) Python变量
用标识符命名的存储单元的地址称为变量,变量是用来存储数据的,通过标识符可以获取变量的值,也可以对变量进行赋值。对变量赋值的意思是将值赋给变量,赋值完成后,变量所指向的存储单元存储了被赋的值,在Pyhton语言中赋值操作符为“=、+=、-=、*=、/=、%=、**=、//=”。
我们使用的开发工具也会对我们的代码规范进行检查,以上主要参考:https://baijiahao.baidu.com/s?id=1610480755579501812&wfr=spider&for=pc
2. 详细设计
(1)模块组成
(2)代码设计
初始化地图:init_map(rows, cols)
创建二维列表,用随机数函数赋初值为0或1,代表初始细胞状态,入口参数为地图的长宽,由用户输入获得,返回值类型为二维数组。
代码如下:
def init_map(rows, cols):
# 初始化指定长宽的地图, rows(int), cols(int), 返回第一张随机细胞状态图
initial_map = [[0 for i in range(cols)]for i in range(rows)]
for i in range(0, rows):
for j in range(0, cols):
initial_map[i][j] = random.randint(0, 1)
return initial_map
合法性检测:check_int(date)
检测输入的数据是否为正整数,是返回整型的数据,不是返回0。
代码如下:
def check_int(date): # 输入的地图长宽的合法性检测, int try: int(date) except ValueError: print("你输入的不是数字!") sys.exit(0) date = int(date) if date < 1: print("请输入大于一的数:") sys.exit(0) else: return date
演化出下一个地图:cell_laws(mov_map)
功能是根据生命法则,对已有的地图进行检测,推断出每个位置细胞的下一状态,并存到一个新的二维列表里返回,用一个整型变量记录该位置周围活细胞数量,在判断细胞周围细胞状态时,采用对二维列表的遍历操作,python对数组的下标往下溢出时会自动处理,就是在对最左边细胞的左侧细胞判断时,默认判断的是最右边的细胞状态,那么我们只需要处理上限溢出,在判断时加一个对列表长度的取模运算即可,这样就解决了边缘细胞的问题,再将细胞状态一一赋给新的二维列表并返回。
代码如下:
def cell_laws(mov_map):
# 入口参数为一个二维列表,根据生命法则推演出下一张地图并返回
live_cell_num = 0
new_map = [[0 for i in range(len(mov_map[0]))]for i in range(len(mov_map))]
for i in range(0, len(mov_map)):
for j in range(0, len(mov_map[0])):
if mov_map[i][j - 1] == 1:
# 左
live_cell_num += 1
if mov_map[i][(j + 1) % len(mov_map[0])] == 1:
# 右
live_cell_num += 1
if mov_map[i - 1][j] == 1:
# 上
live_cell_num += 1
if mov_map[(i + 1) % len(mov_map)][j] == 1:
# 下
live_cell_num += 1
if mov_map[i - 1][j - 1] == 1:
# 上左
live_cell_num += 1
if mov_map[i - 1][(j + 1) % len(mov_map[0])] == 1:
# 上右
live_cell_num += 1
if mov_map[(i + 1) % len(mov_map)][j - 1] == 1:
# 下左
live_cell_num += 1
if mov_map[(i + 1) % len(mov_map)][(j + 1) % len(mov_map[0])] == 1:
# 下右
live_cell_num += 1
if live_cell_num == 3:
# 周围有三个活细胞,下一状态为活
new_map[i][j] = 1
elif live_cell_num == 2:
# 周围有两个活细胞,保持原状态
new_map[i][j] = mov_map[i][j]
else:
# 其他情况下一状态为死
new_map[i][j] = 0
live_cell_num = 0
return new_map
打印地图:out_map(every_map)
为了使结果更美观的展示,我们去查阅了python的一些动画库,最后选择了pygame,将列表的值通过不同颜色打印出来,加上延时控制,打印出一幅幅画面,产生动画的效果。
代码如下:
def out_map(every_map):
# 入口参数:arr[list][list], 返回演变出的下一张细胞状态图
pygame.init()
pygame.display.set_caption('LifeGame')
screen = pygame.display.set_mode([len(every_map[0]) * 6, len(every_map) * 6])
screen.fill([255, 255, 255])
for i in range(0, len(every_map)):
for j in range(0, len(every_map[0])):
if every_map[i][j] == 1:
pygame.draw.rect(screen, [255, 0, 0], [j * 6, 6 * i, 5, 5], 0)
else:
pygame.draw.rect(screen, [255, 255, 255], [j * 6, i * 6, 5, 5], 0)
pygame.display.flip()
pygame.time.delay(2)
# 程序延迟一段时间
return 1
主函数如下:
map_rows = input("输入地图高度:")
map_rows = check_int(map_rows)
# 行数 == 一维列表长度 == 地图高度
map_cols = input("输入地图宽度:")
map_cols = check_int(map_cols)
# 列数 == 二维数组长度 == 地图宽度
game_map = init_map(map_rows, map_cols)
while 1:
game_map = cell_laws(game_map)
out_map(game_map)
for event in pygame.event.get():
# 监视鼠标
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
3. 运行结果截图
4. 实验过程
以上的代码和设计方法都是我们经过漫长的摸索和查阅资料得出的最终结果,在代码的编写过程中我们远程桌面的方式交替编写,另一个人就负责观察命名规范等有没有不合理的地方,以及遇到问题时的查阅资料的工作,以下是实验过程中的截图:
5. 实验小结
通过本次实验,我感受到了结对编程或者说多人编程的魅力,因为每个人的想法都是不同的,当自己独自写代码时可能想问题不全面,可能只看眼前的问题,想到解决的方法,但是可能这个方法会让你不得不用更多的精力来思考下面的工作,也就是考虑不全面,可能适合当前的问题但是却使得整体工作变得复杂,而多一个人思考解决的方法就更多,更能方便的选择更适合全局的方法; 本次实验也让我复习了前不久刚学的git的知识,真正的是它变成了我自己能力的一部分,也锻炼了自己的编码能力和逻辑思维能力。