第一次结对编程的项目是黄金点游戏的比赛,我们从讨论到实现只用了大约不到一天的时间,而且全程都是两人一起编程,全部代码都在get_numbers.py
一个文件中,因此没有使用 github 来管理代码。
PSP 表格
我觉得由于这次项目的结构简单,代码量也不大, PSP 表格并不是很适用,但是,我还是给一个简单的 PSP 表格,因为我们事先也预估过项目所需的时间。
PSP 各个阶段 | 预估的时间 |
---|---|
计划: 讨论我们的大致策略 | 2h |
开发:实现我们的策略,给出最初版本 V1.0 | 2h |
测试:测试V1.0, 并根据测试结果提出改进想法 | 1h |
开发:实现V2.0 | 1h |
测试:测试V2.0 | 1h |
接口设计
我们的代码结构非常简单,基本没有涉及需要设计接口的部分,我们所有的数据都以 numpy
进行传递。
算法实现和UML图
首先, UML 图并不适用于我们的项目,我们没有使用对象,只把一些很简单的基础功能放到函数中,并在主函数中调用这些基础的功能,下面介绍一下算法的实现:
首先,算法开始我们会从标准输入流读取比赛的历史数据 history
, 并且判断这一轮是否需要捣乱操作:判断标准是最后两轮的黄金点的差值是否小于 GATE * 前 N 轮黄金点历史差值的平均。如果是,那么采取捣乱策略,反之就不采取捣乱策略。这里的 GATE 是一个固定的参数,我们调整为 0.3。
接下来,根据选择的策略返回我们输出的两个值。如果不采取捣乱策略,我们会以随机的方式输出两个值,但是,随机的范围并不是随便设定的,而是根据上一轮黄金点的值来判断的。如果采取捣乱策略,我们会提交一个较大的值 (>=80) 和另一个拟合值,较大值是所有超过80的历史数据中的最小值(防止自己扣分),拟合值是上一轮黄金点的值加上 Lambda * (较大值 - 上一轮黄金点),这里, Lambda 是一个动态调整的参数。
最后,算法会根据历史的反馈,来动态调整参数。一个需要调整的参数是捣乱策略时的 Lambda,如果发现在历史数据中,捣乱人数较多,导致我们算法的拟合值小于那一轮的黄金点,那么算法就会增加 Lambda ,反之,当拟合值比黄金点大太多时,算法就会减小 Lambda。另一个参数是不捣乱时,随机点的范围,如果检测到历史数据中捣乱的队伍较多,就会使得一个随机点的范围上调,反之就不做调整。
Design by contract
这个做法的思想是精确的限定接口和数据,保证程序执行和软件的质量。我们的代码中,各个函数的功能都非常明确,数据接口也很简单,因此函数的接口本身就已经十分精确。
程序代码和设计规范
程序的逻辑我们是事先讨论好的,代码的一些规范(比如变量命名,注释一类)会在结对编程的时候实时指出并改进。由于程序的比较简单,函数功能也很明确,除了一些很明显的异常处理(文件读取异常),没有发生其它异常的情况。
界面模块
我们的程序没有界面模块。
结对的过程和照片
我们在开始实现的前一天,讨论出了我们算法的大体思路。在实现的那一天,先按照原先的计划,很快实现出了第一个版本,然后经过测试,讨论,继续改进,实现了第二个版本。全程都是结对编程。(但是没有人给我们拍照)
合作方式,结对编程的优点和缺点
方式就是很传统的领航员+驾驶员的方式。
优点:效率很高,代码思路很清晰,在有一个人不知道怎么写的时候,另一个人能补充;有错误的地方也能指出;有代码写法可以优化的地方也可以及时优化。
缺点: 这个项目有点小,只能是结对编程的一次小体验,暂时没有发现什么缺点。
PSP 表格
PSP 各个阶段 | 实现的时间 |
---|---|
计划: 讨论我们的大致策略 | 2h |
开发:实现我们的策略,给出最初版本 V1.0 | 1.5h |
测试:测试V1.0, 并根据测试结果提出改进想法 | 1h |
开发:实现V2.0 | 1.5h |
测试:测试V2.0 | 0.5h |
基本上和预估的一致,第一个版本实现的较快,第二个版本实现时间稍长,总时间和预估差不多,一天不到就完成了。
其它收获
这是一个比赛,我们总分取得了第三名。我们的算法的确很简单,但是实际上策略还是十分有效的。算法的缺点也比较明显,一些参数的自动学习和调整非常依赖经验和数据,我们没有能够很好的掌握。
不过,我也切身体会到了结对编程的好处,效率非常高,对两个人的锻炼也很明显,结对编程是从自我监督到互相监督的一种转换,某种程度上依赖队友,同时又需要监督队友,需要更集中的精神,另一方面,结对也是与人沟通的过程,对表达自己的想法也是一种锻炼。