本文共 3974 字,大约阅读时间需要 13 分钟。
如果棋类游戏中一方有利,就是另一方不利,这时称为零和博弈。这种类型的博弈,可以使用minimax算法,下面简单介绍一下:
我们知道,如果不出差错,一般我们轮流下棋时,都是坚持这样的原则:如果是自己下,坚持走计算好的最好的棋步;到对手时,他也一定是这样,根据零和博弈原理也就是对手走对我们最坏的棋步。如果对盘面的评估值越大越对我们有利时,我们追求的就是最大化盘面评估值(max),对手追求的是让我们的盘面评估值最小化(min)。
(4)
最大化 / /
[2] (4)
最小化 / /
(4) (5)
最大化 // //
[3] [4] [2] [5]
说明:上面图中,"()"中的内容由"[]"内的内容按照最大或最小原则得当。
再看看我们走棋时过程实际也是要在一个这样的树中寻找一个最好的棋步。一般来说,一个棋局不可能完全遍历,可以设置一个最大搜索深度,在到达最大深度节点时给出评估值,然后利用最大最小原则来一步一步得到顶层评估值。下面是一个例子:
MinMax (GamePosition game)
{
return MaxMove (game);
}
MaxMove (GamePosition game)
{
if (GameEnded(game))
{
return EvalGameState(game);
}
else {
best_move <- {};
moves <- GenerateMoves(game);
ForEach moves
{
move <- MinMove(ApplyMove(game));
if (Value(move) > Value(best_move))
{
best_move <- move;
}
}
return best_move;
}
}
MinMove (GamePosition game)
{
best_move <- {};
moves <- GenerateMoves(game);
ForEach moves
{
move <- MaxMove(ApplyMove(game));
if (Value(move) > Value(best_move))
{
best_move <- move;
}
}
return best_move;
}
同样,如果我们求最小的值加上负号,那么就能将最小原则统一到最大原则(变成求最大)。如下:
(-4)
最大化 / /
[2] (4)
最大化 / /
(-4) (-5)
最大化 // //
[3] [4] [2] [5]
这样一来,最小最大算法可以简化为负最大算法(negaMax)。下面是例子:
Int NegaMax(局面 p, int depth) { int bestvalue = -INF, value; if(Game over) return Evaluation(p); if(depth<=0) //叶子节点 return Evaluation(p); for each(可能的走法 m) { Move(m); //局面p随之改变 value = -NegaMax(p, depth-1);//搜索子节点 UnMove(m); //恢复局面p bestvalue = Max(bestvalue,value); } //end for each return bestvalue; } // end function 后来人们发现,上面的算法效率太低。1958年有人发明了alpha-beta剪枝算法,这样一来可以去除大量不必要的搜索,大大提高了效率。 其主要思想是: (1)alpha剪枝:当我们知道对于某一层要选出最大值传给上层,当前层最大值为alpha,如果发现后面有小于这个alpha的值时就不需要再搜索那个节点和其子节点,因为它的评估值一定不会大于alpha。如下图: (8)
最大化 / /(剪枝)
[8] (5)
最小化 / /
(4) (5)
最大化 // //
[3] [4] [2] [5]
说明:alpha=8,由发现其下一个兄弟节点第一个有值叶子值为3,可以不用搜索整个兄弟节点和子节点(剪枝)。
(2)类似有beta剪枝。
(8)
最大化 / /(剪枝)
[8] (5)
最小化 / /
(4) (5)
最大化 // //
[3] [4] [2] [5]
说明:alpha=8,由发现其下一个兄弟节点第一个有值叶子值为3,可以不用搜索整个兄弟节点和子节点(剪枝)。
(2)类似有beta剪枝。
如果利用负最大算法,可以得到如下算法:
long NegaAlphaBeta(局面 p, int depth, long alpha, long beta)
int value;
if(Game over)
return Evaluation(p);
if( depth <= 0 )
return Evaluation(p);
for(每一个合法的走法 m)
{
Move(m); //局面p随之改变
value = - NegaAlphaBeta(p, depth-1, -beta, -alpha);
UnMove(m);
if(value >= alpha)
//取最大值
alpha = value;
if(alpha >= beta)
break;//剪枝
}
}
return alpha;
}
当然,还有许多方法来提高效率,如:
PVS
历史启发
杀手启发
SSS *
MTD(f)
等等,这里不详细论述。
大家可以去百度和Google上搜索,会发现不少好的资料。
南开大学软件学院《棋类游戏(人机博弈)的一般算法》课件
百度搜索: (有很多中文好东东)
游戏编程精粹1
(8)
最大化 / /(剪枝)
[8] (5)
最小化 / /
(4) (5)
最大化 // //
[3] [4] [2] [5]
说明:alpha=8,由发现其下一个兄弟节点第一个有值叶子值为3,可以不用搜索整个兄弟节点和子节点(剪枝)。
(2)类似有beta剪枝。
如果利用负最大算法,可以得到如下算法:
long NegaAlphaBeta(局面 p, int depth, long alpha, long beta)
int value;
if(Game over)
return Evaluation(p);
if( depth <= 0 )
return Evaluation(p);
for(每一个合法的走法 m)
{
Move(m); //局面p随之改变
value = - NegaAlphaBeta(p, depth-1, -beta, -alpha);
UnMove(m);
if(value >= alpha)
//取最大值
alpha = value;
if(alpha >= beta)
break;//剪枝
}
}
return alpha;
}
当然,还有许多方法来提高效率,如:
PVS
历史启发
杀手启发
SSS *
MTD(f)
等等,这里不详细论述。
大家可以去百度和Google上搜索,会发现不少好的资料。
南开大学软件学院《棋类游戏(人机博弈)的一般算法》课件
百度搜索: (有很多中文好东东)
游戏编程精粹1
(8)
最大化 / /(剪枝)
[8] (5)
最小化 / /
(4) (5)
最大化 // //
[3] [4] [2] [5]
说明:alpha=8,由发现其下一个兄弟节点第一个有值叶子值为3,可以不用搜索整个兄弟节点和子节点(剪枝)。
(2)类似有beta剪枝。
如果利用负最大算法,可以得到如下算法:
long NegaAlphaBeta(局面 p, int depth, long alpha, long beta)
int value;
if(Game over)
return Evaluation(p);
if( depth <= 0 )
return Evaluation(p);
for(每一个合法的走法 m)
{
Move(m); //局面p随之改变
value = - NegaAlphaBeta(p, depth-1, -beta, -alpha);
UnMove(m);
if(value >= alpha)
//取最大值
alpha = value;
if(alpha >= beta)
break;//剪枝
}
}
return alpha;
}
当然,还有许多方法来提高效率,如:
PVS
历史启发
杀手启发
SSS *
MTD(f)
等等,这里不详细论述。
大家可以去百度和Google上搜索,会发现不少好的资料。
南开大学软件学院《棋类游戏(人机博弈)的一般算法》课件
百度搜索: (有很多中文好东东)
游戏编程精粹1
转载地址:http://uewqb.baihongyu.com/