Minimax算法-当我有两种获胜方式时,计算机不会阻止我。

时间:2018-09-10 17:56:30

标签: java algorithm minimax

在我的minimax算法中,当向计算机展示一个具有两种赢得计算机的方法的玩家时,他将只选择棋盘的第一个打开位置。下面举个例子。 X可以在0,2和1,0位置获胜。

X |   |   
__________
  | x |
__________
x | o | o

当前,我的算法会将o放置在0,1的位置。我相信这样做是因为当minimax运​​行并将o放置在0,1位置时,并且由于这不是胜利,因此再次调用minimax,这次是x。 X然后移至位置0,2获胜。该位置返回-10。如果计算机移动到位置0,2,则调用minimax,并且x最终放置在位置1,0,该移动也将返回-10。实际上,无论计算机将o放在何处,都将返回-10,因为无论玩家将赢什么。因为对于o的每个位置,它都会返回-10,所以计算机会将o放置在第一个可用插槽中,该位置为0.1,因为从不从第一个位置开始更新最大值。我希望将o放置在1,0或0,2位置只是为了表明它可以识别一个块。

我的算法如下。它适用于3x3x3,但概念相同。

public int MiniMax(int pGameState[][][], int Depth, boolean IsMax){

        FunctionCalls++;
        if(CheckForWin(2, pGameState)){ //Max Player (since the computer is always 2)
            return 10 - Depth;
        }
        if(CheckForWin(1, pGameState)){ //Player will win therefore we return -10. If this is the first level of the tree
                                        //then the value return is -10. If the second ply then the value returned is -8. 
                                        //It is more important for the computer to win sooner than later. 
            return -10 - Depth;
        }
        if(Depth >= 2){
            return 0;
        }

        if(IsMax){

            int Value = Integer.MIN_VALUE;

            for(int i=0; i<3; i++){
                for(int j=0; j<3; j++){
                    for(int k=0; k<3; k++){
                        if(pGameState[i][j][k] == 0){
                            pGameState[i][j][k] = 2;

                            int best = MiniMax(CopyArray(pGameState), Depth+1, !IsMax);

                            if(best > Value)
                                Value = best;

                            pGameState[i][j][k] = 0;
                        }
                    }
                }
            }

            return Value;
        }
        else{
            int Value = Integer.MAX_VALUE;

            for(int i=0; i<3; i++){
                for(int j=0; j<3; j++){
                    for(int k=0; k<3; k++){
                        if(pGameState[i][j][k] == 0){
                            pGameState[i][j][k] = 1;

                            int best = MiniMax(CopyArray(pGameState), Depth+1, !IsMax);
                            if(best < Value)
                                Value = best;

                            pGameState[i][j][k] = 0;
                        }
                    }
                }
            }

            return Value;
        }
    }

我最初是这样叫minimax

best = MiniMax(CopyArray(GameState), 0, false);

然后,我将其与之前的最高得分进行比较。如果最好更大,则将此移动保存为计算机的移动。

1 个答案:

答案 0 :(得分:1)

处理第一个可用的移动选择问题的一种简单方法是在对有效移动进行迭代之前对其进行排序。考虑您在问题中描述的职位:

X . .
. X .
X O O

这里O要移动。在以默认方式(从左到右,从上到下)遍历木板之前,请根据每个动作的好坏程度对四个有效动作((0, 1), (0, 2), (1, 0), (1, 2))的向量进行排序。一种做到这一点的方法是使用评估功能,该功能可以计算出潜在的举动之后双方都面临多少威胁。一块P(可以是XO)的威胁是具有一个空正方形和两个P正方形(所以是一块)的行,列或对角线P不足以赢得胜利)。让我们看一下该评估函数将如何针对给定位置的四个有效步伐告诉我们。我们计算这两种威胁的数量,并分配等于差值S的值O_threats - X_threats

如果O进行了(0, 1)的移动,则O_threats = 0X_threats = 2,因此得分为S = 0 - 2 = -2

如果O进行了(0, 2)的移动,则O_threats = 1X_threats = 1,因此得分为S = 1 - 1 = 0

如果O进行了(1, 0)的移动,则O_threats = 0X_threats = 1,因此得分为S = 0 - 1 = -1

如果O进行了(1, 2)的移动,则O_threats = 1X_threats = 2,因此得分为S = 1 - 2 = -1

根据计算出的分数,访问有效步伐的顺序应为:(0, 2), (1, 0), (1, 2), (0, 1)。我们知道,在完美发挥的情况下,所有四个举动都会输掉。并且由于它们的分数相等(等于损失值-10),因此,第一个考虑的移动(0, 2)不会被下一个覆盖。这将使程序的动作“更智能”,因为它现在尊重所进行的动作所创建/阻止的威胁(并且在玩井字游戏时,人们经常会使用威胁因素)。您可以使用不同的评估功能对有效动作进行排序,以强制执行不同顺序。

还请注意,与alpha-beta修剪结合使用时,移动顺序对于增加搜索深度非常有用,因为它允许首先考虑有效的有效移动并增加修剪更多节点的机会。尽管对于这样一个简单的游戏来说,alpha-beta修剪可能是一个过大的杀伤力,但对于更复杂的游戏而言,它确实很有用。