如果棋类游戏中一方有利,就是另一方不利,这时称为零和博弈。这种类型的博弈,可以使用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 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
(8)
最大化 / /(剪枝)
[8] (5)
最小化 / /
(4) (5)
最大化 // //
[3] [4] [2] [5]
说明:alpha=8,由发现其下一个兄弟节点第一个有值叶子值为3,可以不用搜索整个兄弟节点和子节点(剪枝)。
(2)类似有beta剪枝。
最大化 / /(剪枝)
[8] (5)
最小化 / /
(4) (5)
最大化 // //
[3] [4] [2] [5]
说明:alpha=8,由发现其下一个兄弟节点第一个有值叶子值为3,可以不用搜索整个兄弟节点和子节点(剪枝)。
(2)类似有beta剪枝。
long NegaAlphaBeta(局面 p, int depth, long alpha, long beta)
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;
}
当然,还有许多方法来提高效率,如:
历史启发
杀手启发
SSS *
MTD(f)
大家可以去百度和Google上搜索,会发现不少好的资料。
百度搜索: http://www.baidu.com/s?wd=alpha-beta%BC%F4%D6%A6&cl=3(有很多中文好东东)
游戏编程精粹1
最大化 / /(剪枝)
[8] (5)
最小化 / /
(4) (5)
最大化 // //
[3] [4] [2] [5]
说明:alpha=8,由发现其下一个兄弟节点第一个有值叶子值为3,可以不用搜索整个兄弟节点和子节点(剪枝)。
(2)类似有beta剪枝。
long NegaAlphaBeta(局面 p, int depth, long alpha, long beta)
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;
}
当然,还有许多方法来提高效率,如:
历史启发
杀手启发
SSS *
MTD(f)
大家可以去百度和Google上搜索,会发现不少好的资料。
百度搜索: http://www.baidu.com/s?wd=alpha-beta%BC%F4%D6%A6&cl=3(有很多中文好东东)
游戏编程精粹1
最大化 / /(剪枝)
[8] (5)
最小化 / /
(4) (5)
最大化 // //
[3] [4] [2] [5]
说明:alpha=8,由发现其下一个兄弟节点第一个有值叶子值为3,可以不用搜索整个兄弟节点和子节点(剪枝)。
(2)类似有beta剪枝。
long NegaAlphaBeta(局面 p, int depth, long alpha, long beta)
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;
}
当然,还有许多方法来提高效率,如:
历史启发
杀手启发
SSS *
MTD(f)
大家可以去百度和Google上搜索,会发现不少好的资料。
百度搜索: http://www.baidu.com/s?wd=alpha-beta%BC%F4%D6%A6&cl=3(有很多中文好东东)
游戏编程精粹1