【问题标题】:Implementing alpha beta pruning in a TicTacToe minimax algorithm在井字棋极小极大算法中实现 alpha beta 剪枝
【发布时间】:2015-08-19 01:56:32
【问题描述】:

在我的方法 newminimax49 中,我有一个使用memoization 的极小极大算法以及在此post 中向我建议的其他一般改进。该方法使用简单的启发式棋盘评估函数。我的问题基本上是关于 alpha beta pruning 的,即我的 minimax 方法是否使用 alpha beta pruning。据我所知,我相信它确实如此,但是我用来实现它的方法似乎太简单了,不可能是真的。此外,其他人建议我使用 alpha beta pruning,正如我所说,我认为我的 minimax 方法已经做到了,这让我相信我在这里做的是别的东西。所以这是我的新minimax49:

//This method returns a 2 element int array containing the position of the best possible 
//next move and the score it yields. Utilizes memoization and supposedly alpha beta 
//pruning to achieve better performance. Alpha beta pruning can be seen in lines such as:
/*if(bestScore==-10)
     break;*/
//This basically means that if the best score achieved is the best possible score
//achievable then stop exploring the other available moves. Doing thing I believe
//I'm applying the same principle of alpha beta pruning.
public int[] newminimax49(){
    int bestScore = (turn == 'O') ? +9 : -9;    //X is minimizer, O is maximizer
    int bestPos=-1;
    int currentScore;
    //boardShow();
    String stateString = "";                                                
    for (int i=0; i<state.length; i++) 
        stateString += state[i];                        
    int[] oldAnswer = oldAnswers.get(stateString);                          
    if (oldAnswer != null) 
        return oldAnswer;
    if(isGameOver2()!='N'){
        //s.boardShow();
        bestScore= score();
    }
    else{
        //s.boardShow();
        int i=0;
        for(int x:getAvailableMoves()){
            if(turn=='X'){  //X is minimizer
                setX(x);
                //boardShow();
                //System.out.println(stateID++);
                currentScore = newminimax49()[0];
                revert(x);
                if(i==0){
                    bestScore = currentScore;
                    bestPos=x;
                    if(bestScore==-10)
                        break;
                }
                else if(currentScore<bestScore){
                    bestScore = currentScore;
                    bestPos=x;
                    if(bestScore==-10)
                        break;
                }
            }
            else {  //O is maximizer
                setO(x);
                //boardShow();
                //System.out.println(stateID++);
                currentScore = newminimax49()[0];
                revert(x);
                //boardShow();
                if(i==0){
                    bestScore = currentScore;
                    bestPos=x;
                    if(bestScore==10)
                        break;
                }

                else if(currentScore>bestScore){
                    bestScore = currentScore;
                    bestPos = x;
                    if(bestScore==10)
                        break;
                }
            }
            i++;
        }
    }
    int[] answer = {bestScore, bestPos};                                    
    oldAnswers.put (stateString, answer);                                   
    return answer;
}

我的State2类中使用的字段和构造函数:

private char [] state;  //Actual content of the board
private char turn;  //Whose turn it is
private Map<String,int[]> oldAnswers; //Used for memoization. It saves every state along with the score it yielded which allows us to stop exploring the children of a certain node if a similar node's score has been previously calculated. The key is the board state(i.e OX------X for example), the int array is a 2 element array containing the score and position of last placed seed of the state.  
private Map<Integer, int []> RowCol; //A mapping of positions from a board represented as a normal array to a board represented as a 2d array. For example: The position 0 maps to 0,0 on a 2d array board, 1 maps to 0,1 and so on.
private static int n;   //Size of the board
private static int stateID; //An simple incrementer used to show number of recursive calls in the newminiax49 method. 
private static int countX, countO; //Number of placed Xs and Os
private static int lastAdded; //Position of last placed seed
private char [][] DDState; //A 2d array representing the board. Contains the same values as state[]. Used for simplicity in functions that check the state of the board.

public State2(int n){
    int a=0;
    State2.n=n;
    state=new char[n*n];
    RowCol=new HashMap<Integer, int []>();
    countX=0;
    countO=0;
    //Initializing the board with empty slots
    for(int i = 0; i<state.length; i++){
        state[i]='-';
    }
    //Mapping
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            RowCol.put(a, new int[]{i, j});
            a++;
        }
    }
    a=0;
    DDState=new char[n][n];
    //Initializing the 2d array with the values from state[](empty slots)
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            DDState[i][j]=state[a];
            a++;
        }
    }
    oldAnswers = new HashMap<String,int[]>();
}

补充方法:

getAvailableMoves,返回一个包含棋盘上空槽的数组(即可能的下一步动作)。

public int[] getAvailableMoves(){
    int count=0;
    int i=0;
    for(int j=0; j<state.length; j++){
        if(state[j]=='-')
            count++;
    }
    int [] availableSlots = new int[count];
    for(int j=0; j<state.length; j++){
        if(state[j]=='-')
            availableSlots[i++]=j;      
    }
    return availableSlots;
}

isGameOver2(),简单地检查棋盘的当前状态是否游戏结束。返回一个 char 'X'、'O'、'D' 和 'N',分别代表 X won、O won、Draw 和 Not gameover。

public char isGameOver2(){
    char turnOpp;
    int count;
    if(turn=='X'){
        count=countO;
        turnOpp='O';
    }
    else {
        count=countX;
        turnOpp='X';
    }
    if(count>=n){ 
        //^No win available if each player has less than n seeds on the board

        //Checking begins
                //DDState[RowCol.get(lastAdded)[0]][RowCol.get(lastAdded)[1]]=turn;

                //Check column for win
                for(int i=0; i<n; i++){
                    if(DDState[i][RowCol.get(lastAdded)[1]]!=turnOpp)
                        break;
                    if(i==(n-1)){
                        //DDState[RowCol.get(x)[0]][RowCol.get(x)[1]]='-';
                        return turnOpp;
                    }
                }

                //Check row for win
                for(int i=0; i<n; i++){
                    if(DDState[RowCol.get(lastAdded)[0]][i]!=turnOpp)
                        break;
                    if(i==(n-1)){
                        //DDState[RowCol.get(x)[0]][RowCol.get(x)[1]]='-';
                        return turnOpp;
                    }
                }

                //Check diagonal for win
                if(RowCol.get(lastAdded)[0] == RowCol.get(lastAdded)[1]){

                    //we're on a diagonal
                    for(int i = 0; i < n; i++){
                        if(DDState[i][i] != turnOpp)
                            break;
                        if(i == n-1){
                            //DDState[RowCol.get(x)[0]][RowCol.get(x)[1]]='-';
                            return turnOpp;
                        }
                    }
                }

                //check anti diagonal 
                for(int i = 0; i<n; i++){
                    if(DDState[i][(n-1)-i] != turnOpp)
                        break;
                    if(i == n-1){
                        //DDState[RowCol.get(x)[0]][RowCol.get(x)[1]]='-';
                        return turnOpp;
                    }
                }

                //check for draw
                if((countX+countO)==(n*n))
                    return 'D';
            }
    return 'N';
}

boardShow,返回板子当前状态的矩阵显示:

public void boardShow(){
    if(n==3){
        System.out.println(stateID);
        for(int i=0; i<=6;i+=3)
            System.out.println("["+state[i]+"]"+" ["+state[i+1]+"]"+" ["+state[i+2]+"]");
        System.out.println("***********");
    }
    else {
        System.out.println(stateID);
        for(int i=0; i<=12;i+=4)
            System.out.println("["+state[i]+"]"+" ["+state[i+1]+"]"+" ["+state[i+2]+"]"+" ["+state[i+3]+"]");
        System.out.println("***********");
    }   
}

score,是一个简单的评估函数,O 获胜返回 +10,X 获胜返回 -10,平局返回 0:

public int score(){
    if(isGameOver2()=='X')
        return -10;
    else if(isGameOver2()=='O')
        return +10;
    else 
        return 0;
}

播种者:

//Sets an X at a certain location and updates the turn, countX and lastAdded variables
public void setX(int i){
    state[i]='X';
    DDState[RowCol.get(i)[0]][RowCol.get(i)[1]]='X';
    turn='O';
    countX++;
    lastAdded=i;
}

//Sets an O at a certain location and updates the turn, countO and lastAdded variables
public void setO(int i){
    state[i]='O';
    DDState[RowCol.get(i)[0]][RowCol.get(i)[1]]='O';
    turn='X';
    countO++;
    lastAdded=i;
}

Revert,简单地还原一个动作。例如,如果 X 已放置在位置 0,revert(0) 会在其位置设置一个“-”并更新由 setX 更改的变量:

public void revert(int i){
    state[i]='-';
    DDState[RowCol.get(i)[0]][RowCol.get(i)[1]]='-';
    if(turn=='X'){
        turn = 'O';
        countO--;
    }
    else {
        turn = 'X';
        countX--;
    }
}

那么这对你们来说看起来像是 alpha beta 修剪吗?如果不是,我该如何实现?

【问题讨论】:

    标签: java algorithm artificial-intelligence tic-tac-toe minimax


    【解决方案1】:

    您已经在使用某种“简化的”Alpha-Beta:目前,只要玩家找到获胜位置,您就会进行修剪。

    一个适当的 AB 会传递给自己一个 Alpha 和一个 Beta 值,以确定玩家将达到的最小值和最大值。在那里,只要分数低于或等于对方玩家当前的“最坏情况”,你就会进行修剪。

    在您的情况下,您不仅可以修剪获胜分数(就像您目前所做的那样),还可以修剪某些分数为 0。

    【讨论】:

    • 是的,这似乎是互联网上的通用词。问题是,关于如何为使用简单评估函数的极小极大方法实现它的信息并不多。您对我将如何做类似于您在此处为我的 newminimax49 方法建议的事情有什么想法吗?
    • @Omar 只需按照您通过更复杂的评估实现它的方式来实现它:将两个整数参数添加到您的方法(alpha/beta),以正确的 alpha-beta 方式更新这些值,然后你会完全没事的。有关更多信息,我仍然非常建议国际象棋编程维基...
    猜你喜欢
    • 2017-09-06
    • 1970-01-01
    • 2016-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-07
    • 2019-05-09
    • 1970-01-01
    相关资源
    最近更新 更多