谈极小化极大值搜索
在人工智能的机器博弈中首先学习的就是极小极大值搜索Minimax,有人翻译成Minmax,少写了中间的字母i,感觉意思不够准确。Minimax的含义就是极小化对手的最大利益。
维基百科中给出的伪代码如下:http://www.cnblogs.com/pangxiaodong/archive/2011/05/26/2058864.html 加上了中文翻译。
function minimax(node, depth) // 指定当前节点和搜索深度 // 如果能得到确定的结果或者深度为零,使用评估函数返回局面得分 if node is a terminal node or depth = 0 return the heuristic value of node // 如果轮到对手走棋,是极小节点,选择一个得分最小的走法 if the adversary is to play at node let α := +∞ foreach child of node α := min(α, minimax(child, depth-1)) // 如果轮到我们走棋,是极大节点,选择一个得分最大的走法 else {we are to play at node} let α := -∞ foreach child of node α := max(α, minimax(child, depth-1)) return α;
我实现的C++代码:
// 返回值是搜索的评估值,board是当前的局面
// 这里30000是红方取胜的局面,-30000是黑方取胜的局面 int MiniMaxSearch(int depth) { // 所谓确定的结果,在象棋里就是被将死的情况
// 当着法生成中已经有了将军的判断,下面这几行的if else是不需要的
// 因为如果某方被将死了,n_moves将等于0,说明产生不出合法的着法,此时就直接返回-30000或30000了 if (board.WhichTurn == TURN_BLACK && board.IsCheckmated()) { return -30000; } else if (board.WhichTurn == TURN_RED && board.IsCheckmated()) { return +30000; } if (depth == 0) return Evaluate(board); // 搜索达到指定深度时 int bestValue = (board.WhichTurn == TURN_RED) ? -30000 : 30000; Move move_list[256]; int n_moves; n_moves = GenerateAllMoveList( board, move_list ); for(int i=0; i<n_moves; i++) { board.MakeMove(move_list[i]); // 走一着棋 int value = MiniMaxSearch(depth - 1); board.UnmakeMove(move_list[i]); // 撤消一着棋 if (board.WhichTurn == TURN_RED) // 极大节点 bestValue = (value > bestValue)? value : bestValue; else // 极小节点 bestValue = (value < bestValue) ? value: bestValue; } return bestValue; }
在这个网站有一个java applet可以用图示的方式清晰地演示minimax和alphabeta剪枝的运行过程。 http://ksquared.de/gamevisual/launch.php?agent=2
NegaMax的算法更简洁:
但要注意:NegaMax中的评估函数,对轮到谁走棋是敏感的。
例如:
在MiniMax中,轮红方走棋时,假如评估值为100,轮黑方走棋评估值仍是100。
但在NegaMax中,轮红方走棋时,假如评估值为100,轮黑方走棋时评估值要为-100。
1 int Search::NegaMax(int depth, int maxDepth) 2 { 3 /* 在着法生成中有将军的判断,这里就不需要再进行判断了。否则还要进行终止局面的判断。 4 */ 5 6 if (depth == maxDepth) return Eval::Evaluate(board); 7 8 int bestScore = -30000 + depth; //这种写法可以搜索最好的杀着 9 10 Move moveList[256]; 11 int countMoves; 12 13 // 着法生成中要进行将军的判断,也就是轮到红方走棋时,红方的走完后,帅不能被将军 14 countMoves = MoveGenerator::GenerateAllMoveList( board, moveList ); 15 16 for(int i=0; i<countMoves; i++) 17 { 18 board.MakeMove(moveList[i]); 19 int score = -NegaMax(depth + 1, maxDepth); 20 board.UnmakeMove(moveList[i]); 21 bestScore = (score > bestScore)? score : bestScore; 22 } 23 return bestScore; 24 }