极大值极小值搜索设计五子棋
源代码可在这里下载
摘要:
设计一个五子棋对战AI,使用极大值极小值搜索,并使用α-β剪枝减少复杂度。采用启发式函数对整个棋局形式进行评估,并作为极大值极小值搜索的依据。
一、导言
1.1 问题描述:
本次实验要求设计一个五子棋的AI,要求能与人类对战,并具有较好的用户交互界面。
1.2 背景介绍:
五子棋是世界智力运动会竞技项目之一,是一种两人对弈的纯策略型棋类游戏,是世界智力运动会竞技项目之一,通常双方分别使用黑白两色的棋子,下在棋盘直线与横线的交叉点上,先形成5子连线者获胜。棋盘由横纵各15条等距离,垂直交叉的平行线构成,在棋盘上,横纵线交叉形成了225个交叉点为对弈时的落子点。
尽管五子棋先后于1992年、2001年被计算机证明原始无禁手、原始有禁手规则下先手必胜,在五子棋专业比赛中采用现代开局规则(如基于无禁手的两次交换规则(Swap-2),基于有禁手的索索夫-8规则(Soosorv-8))远比原始规则复杂,并未被终结。然而,相比电脑象棋,电脑五子棋的发展是缓慢的。顶级五子棋程序虽长于局部计算,但缺乏大局观,因此很多五子棋专家相信目前的五子棋程序依旧无法超越最强的人类棋手。
1.3 使用方法:
极大极小搜索算法、α-β剪枝算法。
1.4 方法介绍:
极大极小策略是考虑双方对弈若干步之后,从可能的步中选一步相对好的步法来走,即在有限的搜索深度范围内进行求解。
定义一个静态估价函数f,以便对棋局的态势做出优劣评估。
规定:
max和min代表对弈双方;
p代表一个棋局(即一个状态);
有利于MAX的态势,f(p)取正值;
有利于MIN的态势,f(p)取负值;
态势均衡,f(p)取零值;
1.5 MINMAX的基本思想:
- 当轮到MIN走步时,MAX应该考虑最坏的情况(即f(p)取极小值)
- 当轮到MAX走步时,MAX应该考虑最好的情况(即f(p)取极大值)
- 相应于两位棋手的对抗策略,交替使用上面两种方法传递倒推值。
α-β剪枝算法是采用有界深度优先策略进行搜索,当生成节点达到规定的深度时,就立即进行静态估计,而一旦某个非端节点有条件确定倒推值时,就立即赋值。
定义:
- α值:有或后继的节点,取当前子节点中的最大倒推值为其下界,称为α值。节点倒推值>=α;
- β值:有与后继的节点,取当前子节点中的最小倒推值为其上界,称为β值。节点倒推值<=β;
α-β 剪枝:
- β剪枝:节点x的α值不能降低其父节点的β值,x以下的分支可停止搜索,且x的倒推值为α;
- α 剪枝:节点x的β值不能升高其父节点的α值,x以下的分支可停止搜索,且x的倒推值为β;
二、对弈AI落子的程序设计
AI落子的函数如下。当前落子轮到AI时,进行落子。如果是先手第一步,直接下天元位置,否则调用choose函数选择一个落子位置。最后改变下棋的次序状态。
void AI::putChess()
{
if(AIBoard->now) return; //不是AI下时,跳过
if(first == 1 && AIBoard->chess[7][7] == 0)
{ //先手第一步直接下中间
AIBoard->chess[7][7] = 1;
}
else
{
choose(); //选择一个位置
AIBoard->chess[chooseX][chooseY] = first; //将这个位置放置我们的棋子
}
AIBoard->now = !AIBoard->now; //改变下棋次序的状态
}
在计算落子位置之前,有两个预备的函数。一个是就算某一条线的分值,一个是计算整个棋盘的分值,后者需调用前者。而计算落子时,又需要计算整个棋盘的分值。
int AI::getGoalOfPoint(int *a, int color)
{
if(a[0] == color && a[1] == color && a[2] == color && a[3] == color && a[4] == color) return 100000; //五子
if(a[0] == 0 && a[1] == color && a[2] == color && a[3] == color && a[4] == color && a[5] == 0) return 10000; //活四子
if(a[0] == 0 && a[1] == color && a[2] == color && a[3] == color && a[4] == 0) return 1000; //活三子
if((a[