布同:基于JQuery的五子棋人机对战游戏设计与制作
【一、吐槽我的编程入门之路】
不想看这部分的请直接跳到第二部分。
学习编程不是一个容易的事情,有一个入门的坎儿在那里,必须要逾越。我从大一到大二都没有入门,大学前完全没用过电脑,大一下学期才买了电脑开始了系统的使用体验。一开始学习c语言,但是学了也不会用,不知道编程是个什么事儿,不知道如何去学习。当时很多同学开始学习算法,在大二期间就有了一些比赛的成绩,我也是后来才知道这么一回事儿,不过已经晚了。对于编程的困惑萦绕了我很久,在这段时间了我把谭浩强的c语言书看了4遍,不过还是不会编程。
大三上学期的甲流爆发了,9月份我们整个学校都封闭了,在寝室的一个月时间里,大家都在玩魔兽世界,金山的剑三当时刚出来,很多人都在其华丽的画面中享受着快乐的封闭时光,而我却在担心着自己的未来。我不知道自己毕业后能干什么,我也怕一毕业就失业,所以利用这段时间我进行了很好的动手实践。我一边看书,一边开发一个俄罗斯方块的游戏。当时学习MFC的基本原理,配合一点点数据结构的知识,我就开始编写。最后用了一个周的时间做了一个可以完美运行的俄罗斯方块,有预览,有计分,功能和普通的俄罗斯方块一样。很开心。
后来利用这个架子,稍加改动,我又做了贪吃蛇游戏,对对碰游戏,连连看游戏,一切看上去都很顺利。有了这些小小的实践,我变得自信起来,感觉自己这次算是入门了。入门就是能够选择合适的资料,进行独立解决相关问题的能力阶段。
上次用MFC做这个游戏的时候是两年前了,大三下学期3月份,遇到的最大的问题就是指针问题,经常出现指针跑偏,而且调试过程中,断点在2000行的程序间来回的跳动,对自己的压力挺大的。不会编程的人200行的代码都调试不通,会编程的人,总是能够把很长的代码分裂成一段一段的代码来进行单元测试,最后融合到一起能够很好的工作。这就是区别。
【二、五子棋对战游戏的设计与制作】
我大学期间做了一个五子棋人机对战游戏,当时是为了参加葡萄城grape cup的程序设计比赛。千辛万苦做出来了,但是随着结束时间的步步逼近,由于用的是MFC,当时无法用c#改写,所以没有能够最后参加比赛,挺可惜的,都做出来了,却没有拿到一点安慰的成绩。好在这次编程很大程度上锻炼了我的动手能力和解决问题的实际能力,之后靠这次比较好的锻炼,在毕业面试时很好的表达了自己的优势,很好的分析能力,很好的解决实际问题的能力。下面我来分享一下我的这次开发经历和其中比较好的点。
我的程序连同逻辑+界面展示部分才不足700行,这是我这两天(2012年10月20-21日)用JQuery实现的,上次2010年3月份版用MFC共2000行。短了很多,可以认为是我能力提高了,也可以认为是脚本语言不受指针问题的影响,接口丰富方便的原因。我想说的是棋力目前不会太强,因为我设计的算法还有一部分没有实现,如果实现了,那么棋力是毋庸置疑的。现在就先凑合感受一下吧。
使用方法:下载之后使用浏览器打开,现在测试通过的浏览器有IE8,Firefox,chrome,理论支持js和jquery的浏览器都可以使用。打开之后,点击开始按钮进行,请你先用鼠标左键选择一个落点,程序会应战的。如果要重新开始,请再次点击开始按钮即可。运行效果图如:
【三、开发步骤】
要开发一个五子棋(或者其他棋类游戏):
第一步:必须要有一个核心的算法来确定一件事情:什么样的棋是好棋,什么是坏棋,这个必须量化,让程序可以直接像比较字符串和数字那样,可以有个长短大小的区别,好棋应该是能够判断的这样一个原理。第一步必须定义出来,然后找到获取这个好坏的契机。能不能实现还可以先不考虑。有了这个方法,就能够知道,下棋进行到某一步骤时,哪一个位置是最好的,然后在这个位置落子即可。
第二步:有了获取位置好坏的方法,就可以获取到棋盘上所有落点的强弱,就可以排序之后选取最好的一个落点。如果是人机对战,就需要机器不断记录最新的落点的强弱。所以这就是第二步的工作,每落一颗子就需要更新全盘的状态,重新计算最佳位置,这样就能够持续演进,将棋进行到最后。
第三步:下棋的过程中还需要获取什么时候是赢,什么时候是输,五子棋就是五子连珠,而不是六子连珠的时候就赢(我做的游戏判断六子连珠也是赢,算是个缺憾)。只要能每落一子就检查输赢,那么就能够及时正确的停止游戏。
第四步:游戏可以进行,也可以结束,那么接下来我们就可以开始思考如何玩好这盘棋。有一个重要的规律,那么就是如果能要了对方的命,就该直接下手;如果对方能够要了我的命,我就该马上堵住这个缺口。所以从第一步量化的好坏值中,我们必须定义出一个要命的好坏值,这个时候可以保命或者及时出杀招。
第五步:如果棋双方都进行的很和谐,没有喊打喊杀,那么就需要选出一个威胁值,也就是如果这一步不防,接下来不管多少步都无力回天,那么这个时候虽然不是要命值,但是也是临近要命值之前的威胁值。如果我威胁到了对方,对方没有威胁到我,那么我就可以继续深入到要命值,如果对方威胁到了我,我没有威胁到对方,那么我就需要保命,防止对方进入要命值阶段。这个似乎就是策略问题了。什么程度开始需要防止对方进入要命值,这是一个现实中都很实在的问题,根据个人棋力水平来决定。
第六步:有了第五步,我相信程序已经可以预测到未来第三步子的程度了,如果看到第四步、第N步的结果呢?人无远虑必有近忧嘛,我的程序里尚无这部分实现,但是我的算法基础已经具备了实现这个的能力,也许一段时间后我会尝试实现这部分,先把方法介绍下。这里方法有:
第一种:摆阵。从其他部分选择比较好的能够通过一开始一步子的落差,逐步套住对方的方法是合理的。这个时候需要比较现在全盘的落子情况,选择合适的阵形,在对方对我威胁不大的情况下埋好伏笔,等待对方上钩。这个需要程序员吃透阵,明白其变化过程,结合到程序里来。
第二种:递归演进。这种很简单,只是多消耗cpu就可以算出很好的落子位置。怎么做呢?那就是逐步联想的方法,现在第二步不是能够计算出某个时候最好的一步子吗,那就假设落在这里,然后对方死了,你就毫不犹豫的落子在这里,如果没死,你就为对方找一颗最好的落子位置,这时候你死了,就赶快落子在这里,如果你没有死,那么你就继续为自己想一步,就这样一直下去,直到找到能够杀死对方,而自己不死的一颗落子位置,最后把这一串联想全部还原就行了。还原方法是把假象的落子清除,全盘重新计算好坏。
第三种:递归漫游。还是采用联想的方法,我先为自己想出最好的n0个落子位置,如果哪一个位置落之后能杀死对方,就落子于此,如果哪个位置落子之后能够为对方造成2个要命值,也就是一下子堵不了,那么也是好棋,如果只能够找到一个只能形成一个要命值的落点,那么肯定会被对方及时发现,所以还需要从这n0个位置继续往前联想n1个(n1建议比n0小,因为越想太多也许都是空的所以逐步缩小范围才是正理,这里的意思是在n0个的基础上每一个都再联想n1个)。如果n1个位置中哪一个能够为对方造成两个或者更多的要命值,那么也是好的。按此进行x步,直到在nx个位置中找到好棋。x不宜过大,nx也不宜过大,cpu还是很耗的,想3步或者4步就行了。
谈到这第六步,其实已经将常见的人学习五子棋的过程都转化到棋的算法中去了。如果照此计算,实现了这些之后,本程序的棋力应该是非常强的。只有第六步才是决定最终程序展现给大家的是一个高手还是小孩智力的五子棋对战算法。你联想的越深入,找到好棋的可能性就越大。所谓的好棋就是形成五子棋常说的禁手形态的落子位置,诸如三三禁手,三四禁手,三三四禁手,四四禁手,三四四禁手之类的。如果不懂这个,建议查阅相关资料。
【总结】
讲到这里也差不多了,本来打算每个月写一篇文章的。但是发现收获没有那么多,总是写不了。这周六我从早上11点到凌晨3点,连续忙了16个小时,没出去吃饭、上厕所,甚至说没下床,盘着腿在被窝里连续编程,搞出了这么个JQuery版本的五子棋对战游戏,也算是给园子里出一点力了。我比较喜欢博客园,应该果断顶起。