在五子棋中有7种有效的棋型(连五、活四、冲四、活三、眠三、活二、眠二)。我们可以创建黑棋和白棋两个数组,记录棋盘上黑棋和白棋分别形成的所有棋型的个数,然后按照一定的规则进行评分。
在棋盘上设置有15条水平线和15条竖直线,不考虑长度小于5的斜线,有21条左上到右下的斜线,21条左下到右上的斜线。然后在每一条线上分别对黑棋和白棋查找是否有符合的棋型。这种方法比较直观,但是实现不方便。
所以,我们的方法是对整个棋盘进行遍历,对于每一个白棋或黑棋,以它为中心,记录符合的棋型个数。具体的实现方式如下:
1、评估棋盘上的每个点。如果是黑棋或者白棋,则对这个点所在四个方向形成4条线分别进行评估。四个方向即水平、竖直、两个斜线,四个方向依次按照从左到右、从上到下、从左上到右下、从左下到右上来检测。
2、对于具体的一条直线,以选取点为中心,取该方向上前面4个点和后面4个点,组成一个长度为9的数组。
3、找出和中心点相连的同色棋子有几个,根据相连棋子的个数再分别进行判断,最后得出这行属于哪一种棋型。需要注意的是,已经被判断过的棋子要标记下,避免重复统计棋型。
4、根据棋盘上黑棋和白棋的棋型统计信息,按照一定的规则进行评分。假设形成该棋局的最后一步是黑棋下的,则最后的评分是(黑棋得分-白棋得分),在相同棋型、相同个数的情况下,白棋会占优,因为下一步是白棋下。比如白棋有个冲四,黑棋也有个冲四,显然白棋占优,因为下一步白棋就能形成连五。最后按照下面的规则依次匹配:
黑棋连五,评分为;
白棋连五,评分为-;
黑棋两个冲四,可以当成一个活四;
白棋有活四,评分为-9050;
白棋有冲四,评分为-9040;
黑棋有活四,评分为9030;
黑棋有冲四和活三,评分为9020;
黑棋没有冲四,且白棋有活三,评分为9010;
黑棋有2个活三,且白棋没有活三或眠三,评分为9000;
5、最后针对黑棋或白棋的活三、眠三、活二、眠二的个数依次增加分数,具体评分值为(黑棋得分-白棋得分)。
有了上面的评估标准,当轮到AI下棋时,就要针对当前的棋局,找到一个最有利的位置。AI会尝试在每个空点下棋,每次都形成一个新的棋局,然后用评估函数来获取这个棋局的评分,只需要在最后从中选取评分最高的位置就可以了。
下面是AI获取最有利位置的逻辑:
1、首先遍历棋盘上的每一个空点,并在这个空点下棋,获取新的棋局的评分;
2、如果是更高的评分,则保存该位置;
3、然后将这个位置恢复为空点;
4、最后会获取最高评分的位置。
具体实现流程如下:
构造函数实现初始化功能
在数组record中记录所有位置的4个方向是否被检测过,使用二维数组count记录黑棋和白棋的棋型个数统计。通过position_isgreat方法给棋盘上的每个位置设一个初试分数,越靠近棋盘中心,分数越高,这样在最初没有任何棋型时,AI会优先选取靠近中心的位置。对应代码:
返回所有未下棋的坐标
函数genmove()能够获取棋盘上所有的空点,然后依次尝试,获得评分最高的位置并返回
返回当前最优解下标
此函数是上述AI逻辑的代码实现。先通过genmove()获取棋盘上所有的空点,然后依次尝试,获得评分最高的位置并返回:
AI的入口函数
对黑棋和白棋进行区分
对得分进行进一步处理
参数turn表示最近一步棋是谁下的,根据turn决定的me(表示自己棋的值)和you(对手的棋,也就是下一步棋是谁下),在对评分时会用到。checkWin用来判断是否有一方获胜:
对某一个位置的4个方向分别进行检查
把当前方向棋型存储下来
保存棋型时为了方便后续使用,此函数能够根据棋子的位置和方向,获取上面说的长度为9的线。如果线上的位置超出了棋盘范围,就将这个为止的值设置成对手的值,因为超出范围和被对手棋挡着,对棋型判断的结果是相同的。
把当前方向的棋型识别成具体情况
例如把MMMX识别成活四冲四、活三眠三等: