直到6月2号晚上零点,为期一个月的华为德州扑克比赛也算告一段落了。我们团队总共有3人,其中,一个同学(吴晓东)负责算法的设计,一个同学(宋振兴)负责消息的解析,而我则负责整个框架的设计以及具体决策算法的实现。从5月份初报名开始,到环境的搭建,框架的设计,任务的分工,算法的设计以及各个模块代码的编写,从我个人的感觉来看,整个过程可以说是一个比较艰辛的历程。
德州扑克是一种棋牌类游戏,属于一种博弈过程,起先我对这个游戏没有任何的了解,最多的也只是在影视题材里面见过,但是具体的游戏规则并不懂。而正是在这样的条件下,我们参加了由华为举办的德州扑克大赛。比赛的形式,由华为官方提供服务端程序,这个服务端程序负责整个游戏的控制,而参赛的队伍则必须按照官方提供的通信协议,设计出自己的玩家程序,实现和服务器的对接,并和其他玩家进行博弈游戏,最大限度的获得存活场次和金币数量。
整个德州扑克的游戏分为四个阶段,包括了翻牌前(preflop)、翻牌(flop)、转牌(turn)、河牌(reiver),玩家在不同的阶段,根据自己的手牌信息和已知的公牌信息以及每一个玩家的动作做出对自己最有利的动作以获得最后的奖池奖金,这里的动作包括了盖牌(fold)、加注(raise)、跟注(call)、让牌(check)、全押(all In),在每一个阶段里面,只有当所有的玩家投入的金额相等,或者全押,才会进入下一个阶段,而如果是弃牌的玩家,则放弃最后的奖池奖金,不参与后面的喊注。最后游戏的输赢是只剩一个玩家或者在场的玩家的两张手牌和5张公牌组合出最大的牌型的玩家平分最后的奖池奖金。
根据官方提供的通信协议手册,客户端可以将整个流程进行如下划分:
客户端先通过initialize和服务器建立连接,并注册,然后调用recevMsg开始接收消息,解析消息,如果接收到了游戏结束game-over的报文,则调用destroy断开连接释放资源,整个流程结束,否则根据接收到的报文,解析里面的信息,然后根据需要调用Update更新游戏和玩家的状态,如果服务器要求客户端做出决策动作,则调用makeDecision根据当前收集的信息进行决策,然后把决策动作交由sendMsg返回给服务器,等待服务器的下一条报文信息,整个客户端游戏如此不断循环,直到游戏结束。
根据流程图,可以获得用例图:
根据样例用图,进一步分析,可以得到整个系统框架所需的类图,当然这个只是初期的一个系统框架,粒度还是比较粗,这个只是为了便于分工合作,提前设计好借口,后期还是根据不同的需要进行了部分的调整,初期设计的类图如下:
在设计中,根据用例图对模块功能进行了划分并封装,最后得到了上图的几个类,由于设计初期,我们还不能确认在决策算法中需要什么数据信息,所以,在Status类中只是保留了接口和引用关系,里面具体的成员变量和函数则需要和具体的实现需求结合然后加以扩充,Action类的情况也是类似的。这里的设计需要注意的是,为了使整个系统的各个模块部分有一个比较好的解耦效果,采用了设计模式中的策略模式,将决策模块提取出来,然后在主类中保留了接口,以便后期采用不同的决策算法进行替代,而不需要去改动其他模块。
在这次的合作中,我们采用的是敏捷开发的团队合作方式,整个合作过程,我们的文档部分只有一些必要的框架文档和算法说明,剩余的部分还是以交流居多。整个过程中,团队交流次数3-4次左右,这样的交流每次都要持续一个晚上的时间,大概3个小时左右,而成员之间的合作交流在4-5次左右,每次时间也都是2-3个小时。也就是说,在这次的合作过程中,我们强调的是沟通和效率,通过文档来记录交流一些基本信息,对于繁杂说明,我们选择了面对面交流方式,然后辅以必要的文档来提高共同效率。
敏捷开发出了注重沟通之外,还需要强调团队的个人能力,在我们的团队中,每个人都明确分工,这个得益于一开始的框架模块划分,其中,具有较好的数学功底的同学负责资料的收集和算法的设计,剩余的两个,则一个负责消息模块的解析,一个负责框架的搭建和算法的编码实现,每个人都各司其职。
此外,敏捷开发还注重阶段性成果的实现,在这个过程中,我们团队先从环境的搭建,然后是框架的设计,再是每个模块的实现,在每一个阶段都一定的成果进度的安排和实现,然后在上一个阶段成果的基础上进入下一个阶段。虽然如此,但仍然存在一些问题与不足:
1、我们在整个团队项目中,没有使用代码管理软件,这是一个潜在的弊端,由于没有使用代码管理软件的经验以及相应的条件,导致我们最后在进行代码整合的时候出现了一些滞后的情况,另一个同学在进行代码调试的时候,在进行交流才发现,他使用的代码并不是我提供的最新版本,这是我们遇到的一个代码统一问题,虽然那同学后来自己建立了SVN服务器,但是由于机器性能不足,以及IP问题,后来没能用起来,这个是不足之处。
2、由于没有团队合作的经验,在编写代码的时候,没有注重代码的排版以及代码注释说明,在帮助对方调试代码的时候,发现几乎没有什么注释,对于类的成员函数或者是结构体的成员都没有必要的说明,这个对于阅读代码的人来说是一个非常痛苦的,而对于函数接口的说明更是缺乏必要的功能说明,以及输入参数以及返回值的说明,这个给阅读和理解程序的人来说造成了很大的障碍,对于团队的合作效率来说是一个不小的弊端。
3、在拿到对方的代码的时候,没有大致浏览对方的代码,对别写的代码没有一个整体的把握,使得没有能够使用别人提供好的一些接口,而是自起炉灶,这对于时间上的耗费以及潜在的bug的产生都是有相当大的代价,这在一定程度也使得项目的进展滞后。
4、在项目开发过程中,除了必要的沟通之外,对于一些重要的任务的分配应该有一些相应的反馈机制,在我们的开发过程中,就出现了这样一个严重的问题,在我的算法设计部分里面,需要一些消息解析过程中提供必要的参数信息,由于我在编写算法模块的过程,我不清楚另一个同学的消息解析的情况,因此我只有预留必要的变量,然后由消息解析的同学来负责补充,对于这个任务,我当时在群里叮嘱过后,对方也给予确认答复。然而,问题就在于,我没有在后期对这个任务的分配进行完成度的确认,使得我们在临近截止日期才发现最后这一块该同学并没有完成,使得我们到了最后一刻还在编写代码,调试bug,以至于没有预留出比较充分的时间来测试程序的情况。
5、在软件工程里面一直强调的是,在开发过程,唯一不变的是需求是不断改变的。是的,在我们开发过程中,算法的更新也是一而再,再而三的发生,直到最后临近截止时间了,负责算法的同学还提出求改算法模块的要求,对于他的这个要求,一个比较稳妥的做法就是先让该同学把修改后的版本完整描述出来,然后再结合修改的难度以及剩余允许时间所能做的修改考虑是否进行最后的更新或者调整。这种情况下的更新调整对于负责编码的同学来说是一个相当大的挑战,一个是要在有限的时间里面快速做出响应,还有保证最后调整的代码不会出现严重的bug问题。网络上有个段子,描述的就是这个现象,说一个产品经理被程序员打是因为到了最后还要修改需求。这次我遇到的情况也差不多是如此,所以如何很好地应对需求变化是一个比较值得思考的问题。
由于以上几个不足,使得我们最后用来测试的时间几乎没有,我们只能提交能运行的代码完成比赛,最后的结果如何应该都是可以接受的。结果也在6月5号出来了,很可惜,这次的成绩不佳。
比赛的大概过程我看了一下,整体来分析的话,第一轮,我们第一局进展非常顺利,拿到了第一名的成绩,但是后面两场都因为超时次数过多被踢出去,但是勉强进了前4,进入了下一轮。但是到了第二轮之后,很幸运的是,没有出现超时(0.5S给出答复消息)掉线的情况,其中,第一局,一开始的情况还不错,但是到了363局的时候,出现了我们手拿9对子,和别人的A对子加注厮杀,随后allIn,就挂了,这个有点可惜了。而第二局的话,一开始的进展就不太顺利,拿了不怎么样的手牌也和对手叫板,而其他牌手都相当保守,弃牌居多,在这一局里面,我们到了第9局就挂了。第三局的话,我们排在第4,应该说是正常发挥,一开始我们是领先的,后来在256局就输了3K多,情况也是差不多拿了KQ和别人的A对和K对叫板,然后接下来就开始处于劣势,到399我们就挂了。
我分析的情况大体上如此,最后的时间确实比较赶,赛前没有时间好好测测我们的程序,毕竟整体框架都搭建好了,剩下的就是算法的局部调整,由于没有多参加几次官方的PK来发现算法的潜在问题,这个还是比较遗憾的,不过大家都挺尽心尽力的。
通过对比赛结果和参赛过程的分析发现,对于超时问题,一个是能力上的不足,没有充分利用多线程,因为能力的不足所以做起来没有把握,因此担心出现多投入零产出的问题,还有一个是团队合作上的进度问题,进度的滞后使得最后没有空余的时间做测试,没有能够最大程度发挥我们的算法模型,这是一个比较遗憾的事。在后面的学习过程,应多注意这次参赛过程中的诸多问题,希望下一次有一个明显的进步和收获。