软件实践课的一个大作业。
算法4月15号左右就写完了,那时感觉UI两天就能写完,再加上实验室的一些项目,于是就放那了。下周一就结课了,于是趁时赶紧把UI及网络对战实现了。
一、算法部分
这部分分两个模块,一是搜索,另一是估值。
1、搜索
这部分需要用到博弈树理论以及alpha-beta极大极小搜索算法。
博弈树,简化成简单的模型就是:己方每走一步对方便有N种走法,对于对方的N种中的每一步走法自己又有相对应的M种走法,而对于自己的M种走法,对方又有H种走法......如此下去便在逻辑上形成了一颗树,因为每一步我们都想赢,在对方走的基础之上走对自己最有利的一步,这样就形成了博弈,我们称其为博弈树。其有N*M*H*........种走法,呈指数级增长。可见如果我们单纯的BFS或DFS,那将是一件多么“消磨时光”的事,说不定等他跑出来,我们的孩子会在我们的墓碑前含着泪告诉我们结果......因此我们得用一种优化的搜索。极大极小算法解决此类博弈问题的比较流行的一种搜索算法。它的基本思想用一句话概括就是:走 对方走对我们最不利的一步后 局面对我们最有利的一步。如果还是不太理解,在这个网站
的“口袋的例子”中你可以找到更详尽的描述。光这样,效率还是很低的,于是基于极大极小算法的alpha-beta优化算法诞生了。“口袋的例子”中同样也有对alpha-beta算法思想的精彩的描述。
于是乎,这样成熟的算法就有了一个框架(ACM竞赛的时候记不住的话可以当模板使用吧):
基于此算法,我又在寻找“每个合理着法”时使用了贪心算法。其思想是:先搜索一下对方的下一步,如果对方走一步即赢(包括成五连,活四,双活三等),那么我们就要将其堵住。否则寻找对自己最有利的N步(按估值函数判断是否对自己有利),且将其按估值从大到小的顺序排列,搜索时先从估值最大的开始,N是自己定义的有限步(如果是所有可能的都搜索,复杂度仍然很高)。
这样加上一个好的估值函数,一个AI还可以的算法便出来了。
2、估值函数
这个没有太多的技巧,只能老老实实的对棋局进行搜索。大致思想:
从(x , y)开始向左,向右,左上,右下分别搜索,搜索同色且相连的棋子个数,将其标记为已搜索,然后根据活三活四等的特征进行匹配,记录活三活四等的个数,对每一个棋型进行评价,比如一个活三300,两个活三就赢了,给100000。然后将两方的棋型分别相加后,将和相减,返回差。各个棋型可以参考这里
。
二、UI和网络对战
介绍这部分之前,先可悲可笑一下。以前玩别人写的五子棋,是有双人对战,可是那时在同一台电脑上!!你下过后,我在抚摸着你在鼠标上留下的体温接着下......想想都可笑,这尼玛是真正的网络对战么?
1、UI
UI布局没什么好说的,coding前先将UI界面在纸上大致的画一下,心里有个谱,这样写起代码才快。UI我卡在了两个问题上,充分反映了对QT的某些机制理解的不够透彻。
第一个problem:是关于paintEvent()的。当当前窗口失去焦点之后,之前所有已经绘好的棋子全部消失。看了看文档才知道,当窗口start,chang时均触发paintEvent(),而我重绘时使用的激发函数恰好是repaint(),只重绘了局部(即落棋的地方)。解决办法是:将已下的棋子的位置、颜色信息保存起来。重绘的时候for循环全部重绘。
第二个problem:是关于不同窗口之间信号通信的。在具体一点来说是父窗口与子窗口之间信号通信的。这个难点主要是不知怎么获得父窗口的实例,即connect( childWidget , SIGNAL() , fatherWidget , SLOT() )中的fatherWidget。最后我扒遍了QT文档,想到了两个办法:
2、是在fatherWidget中使用connect()函数,因为在childWidget是在fatherWidget中创建的,因此使用时就像这样connect( childWidget , SIGNAL() , this , SLOT() )就行了。
李浩师兄又告诉我说,在初始化子窗口时,传递父窗口的this指针,然后在子窗口里使用。我当时怎么就没想到呢?!!
2、网络对战
我决定用一副自己设计时的的手绘图来说明我的实现流程。
其是按照上面标的序号来实现连接的。
端口是举例用的,仅仅是为了说明问题。上面的框图是下面的子窗口。
序号1:客户端向端口1发送udp广播,发送的内容为 "标记+客户端的HostName”
序号2:服务器端坚挺的端口2接收到内容,判断一下标记,如果是发送的标记,记录客户端的HostName,并显示IP
序号3、4:当服务器端点击已经找到的IP时,向其棋局窗口发送指令,令其开始监听tcp连接
序号5:监听成功时,向子窗口发送监听成功信号
序号6:当接收到父窗口监听成功信号时,向客户端的子窗口发送tcp连接信号
序号7:客户端的子窗口向父窗口发送tcp连接信号
序号8:客户端连接服务器,当连接成功时,向子窗口发送连接成功信号
序号9:告诉服务器端子窗口tcp连接成功,令其关闭
需要说明的是我是先用UDP来探路,看是否能够找到服务器(或客户端连接),然后当点击某一IP时才建立TCP连接
三、一些感悟和一些关于QT的经验
1、必须要有敏锐的观察力,丰富的联想力,以及大胆的尝试
2、文档才是精髓!!
3、如果自己定义的UI类作为另外一个自定义UI类的成员变量时,则有时必须在此类上面声明成员变量类,用“class className;"的形式。
4、有一些莫名其妙的问题有时将build文件整个删掉就莫名其妙的好使了。
5、google才是王道!!
四、最终效果图:
首界面:
人机对战:
设置:
双人对战:
建立主机:
寻找主机: