博弈树:在进行游戏时,双方轮流选择,每次执行一个步骤。所有可能的步骤就构成了一棵博弈树。
终端位置:通过考察盘面能够确定这局棋输赢的位置。如果一个位置不是终端位置那么该位置的值通过递归地假设双方最优棋步而确定的。
极小极大策略:极小极大策略常用于二人博弈游戏,目的是寻找最优的方案使得自己能够利益最大化。
基本思想就是假设博弈的双方都足够聪明,一方总是能选择最有利于自己的方案,使得自己胜率值极大,而另一方总会选择最不利对手的方案,使得对手的胜率值极小。
对于计算机下棋,一个最重要的因素看来是程序能够向前看的棋步的数目。称之为层(ply),也就是递归的深度。
计算机和人进行博弈:计算机先手,则在博弈树中,计算机总会选择最大的值(称为MAX层),人总会选择最不利于计算机的方案,使计算机的值极小(称为MIN层)。
例:在图中MIN层从MAX层选择最小的值,在MAX层从MIN层中选择最大的值。这是一个博弈的过程。
ɑ-β剪枝:AlphaBeta剪枝算法是一个搜索算法旨在减少在其搜索树中,被极大极小算法评估的节点数。这是一个常用人机游戏对抗的搜索算法。它的基本思想是根据上一层已经得到的当前最优结果,决定目前的搜索是否要继续下去。
Alpha代表MAX层的取值,Beta代表MIN层的取值。
Alpha剪枝:Alpha的值位于MAX层,Alpha是下一层MIN层中取值的最大值,当MIN层的一边已经得出数据,则另一边的左右子树要都比另一侧数据大,才有计算的价值,当其中一个比另一侧数据小,另一侧就没有计算的价值,进行剪枝操作。
如图所示:
Beta剪枝:Beta的值位于MIN层,Beta是下一层MAX层中取值的最小值,当MAX层的一边已经得出数据,则另一边的左右子树要都比另一侧数据小,才有计算的价值,当其中一个比另一侧数据大,另一侧就没有计算的价值,进行剪枝操作。
如图所示:
三连游戏棋:两人轮流在印有九格方盘上划“+”或“O”字, 谁先把三个同一记号排成横线、直线、斜线, 即是胜者。
核心代码
void FindComMove(BoardType Board, int *BestMove, int *Value, int Alpha, int Beta)
{
int Dc, i, Response;
if (FullBoard(Board)) //判断棋盘是否下满,若下满,则平局
*Value = Draw;
else if (ImmediateHumanWin(Board)) //判断人类是否赢
*Value = ComLoss;
else
{
*Value = Alpha;
//Alpha剪枝,Beta的值必须大于Alpha的值,否则剪枝
for (i = 0; i < 9 && *Value < Beta; i++)
{
if (IsEmpty(Board, i / 3, i % 3))
{
Place(Board, i / 3, i % 3, 'C'); //下棋
FindHumanMove(Board, &Dc, &Response, *Value, Beta);
//模拟人类下棋,构建博弈树
Unplace(Board, i / 3, i % 3); //撤销下棋
if (Response > *Value) //若位置更好,则更新位置
{
*Value = Response;
*BestMove = i;
}
}
}
}
}
void FindHumanMove(BoardType Board, int *BestMove, int *Value, int Alpha, int Beta)
{
int Dc, i, Response;
if (FullBoard(Board)) //判断棋盘是否已满,若是则平局
*Value = Draw;
else if (ImmediateComWin(Board)) //判断计算机是否赢
*Value = ComWin;
else
{
*Value = Beta;
//Beta剪枝,若Alpha的值大于Beta,则进行剪枝
for (i = 0; i<9 && *Value > Alpha; i++)
{
if (IsEmpty(Board, i / 3, i % 3))
{
Place(Board, i / 3, i % 3, 'H'); //下棋
FindComMove(Board, &Dc, &Response, Alpha, *Value);
//模拟计算机下棋,构建博弈树
Unplace(Board, i / 3, i % 3); //撤销下棋
if (Response < *Value)
{
//若有更好的位置,则更新
*Value = Response;
*BestMove = i;
}
}
}
}
}