在我们的五子棋游戏中,黑白两方轮流下子,会产生不同的棋盘局面。对于一个局面来讲又有不同的应对方法,不同的应对方法,接着又会产生不同的局面。
也就是说黑方先下子,白方就有224种落子方法,如果黑方选择了一其中的一步应对,那白方接下来就有223种方案和223种局面。
这样看就是一个又一个的树,但是在一个五子棋游戏里面博弈树的全部遍历有10的41次方个局面,所以我们基本上就是设定一个深度就不在搜索了,用一个评估函数对局势进行判断,用估计值来代替实际的搜索值。而且搜索算法用的就是极大极小的搜索来减少我们搜索的工作量。我们可以把一个五子棋的走步看成,黑方的节点估计值对标乙方子节点的最大值确定,同时乙方从叶节点选的就是越小越对它有利,两者倒退就可以退出根节点的估计值,这样就可以确定根节点的出发的最佳走步。同时为了减少搜索量,将叶节点的评估,跟就算倒退值与树的生成同时进行。就可以减少搜索的数量,还可以保持效果的不便。
//---------搜索当前搜索状态极大值--------------------------------//
//alpha 祖先节点得到的当前最小最大值,用于alpha 剪枝 //beta 祖先节点得到的当前最大最小值,用于beta 剪枝。 //step 还要搜索的步数 //return 当前搜索子树极大值 protected int findMax(int alpha, int beta, int step) {//只有极小值的方法调用了这里 int max = alpha; if (step == 0) { return evaluate(); } int[][] rt = getBests(1 - sbw); for (int i = 0; i < rt.length; i++) { int x = rt[i][0]; int y = rt[i][1]; if (getType(x, y, 1 - sbw) == 1) //电脑可取胜 return 100 * ( getMark(1) + step*1000 ); isChessOn[x][y] = 1 - sbw; // 预存当前边界值 int temp1=x_min,temp2=x_max,temp3=y_min,temp4=y_max; resetMaxMin(x,y); int t = findMin(max, beta, step - 1); isChessOn[x][y] = 2; // 还原预设边界值 x_min=temp1; x_max=temp2; y_min=temp3; y_max=temp4; if (t > max) max = t; //beta 剪枝 if (max >= beta) return max; } return max; }//-----------------------搜索当前搜索状态极小值---------------------------------//
//alpha 祖先节点得到的当前最小最大值,用于alpha 剪枝 //beta 祖先节点得到的当前最大最小值,用于beta 剪枝 //step 还要搜索的步数 //return 当前搜索子树极小值。 protected int findMin(int alpha, int beta, int step) { int min = beta; if (step == 0) { return evaluate(); } int[][] rt = getBests(sbw); for (int i = 0; i < rt.length; i++) { int x = rt[i][0]; int y = rt[i][1]; int type = getType(x, y, sbw); if (type == 1) //玩家成5 return -100 * ( getMark(1) + step*1000 ); // 预存当前边界值 int temp1=x_min,temp2=x_max,temp3=y_min,temp4=y_max; isChessOn[x][y] = sbw; resetMaxMin(x,y); int t = findMax( alpha, min, step - 1 ); isChessOn[x][y] = 2; // 还原预设边界值 x_min=temp1; x_max=temp2; y_min=temp3; y_max=temp4; if (t < min) min = t; //alpha 剪枝 if (min <= alpha) { return min; } } return min; }