2

我最近一直在研究 Java 中 Android 上标准 8x8 黑白棋(黑白棋)游戏的 MiniMax 算法。每个日志似乎都显示了每个节点的正确值,但不知何故,算法只是没有选择最佳移动(我将行为与其他人的应用程序进行了比较,在某些时候它们有所不同)。

恐怕有相当多的代码要吞下,但我相信你可以做到这一点!

首先,我的算法类(从那个类我继承了 MiniMax 算法):

public abstract class Algorithm 
{
/** game instance to which the strategy is applied */
Game game;

public Algorithm (Game game)
{
    this.game = game;
}

/**Finds player's all possible moves for the current state   * 
 * @param gb - a game board for which the moves are found
 * @param player - current player
 * @return - an array list of possible fields to move
 */
public ArrayList<Field> findAllPossibleMoves(GameBoard gb, boolean player)
{
    ArrayList <Field> moves = new ArrayList <Field> ();
    for (int y= 0; y<gb.getHeight();y++) for (int x= 0; x<gb.getWidth();x++)
    {
        Field f = new Field (x,y);
        if (game.isAvailable(gb, f, player)) moves.add(f);          
    }
    return moves;
}

/** Searches for the right move 
 * @param gb - game board to do the search on
 * @param depth maximum depth of search tree
 * @param player - current player
 * @return - optimum move (Field)
 */
public abstract Field findBestMove(GameBoard gb, int depth, boolean player);

/**Evaluates the player's current state  * 
 * @param gb - game board to be evaluated
 * @param player - player for which we evaluate
 * @return - the current rating (int)
 */ 
public int evaluate(GameBoard gb) 
{
    int rating = 0;     
    int[][] eval_table = {
            {99,  -8,  8,  6,  6,  8,  -8, 99},
            {-8, -24, -4, -3, -3, -4, -24, -8},
            { 8,  -4,  7,  4,  4,  7,  -4,  8},
            { 6,  -3,  4,  0,  0,  4,  -3,  6},
            { 6,  -3,  4,  0,  0,  4,  -3,  6},
            { 8,  -4,  7,  4,  4,  7,  -4,  8},
            {-8, -24, -4, -3, -3, -4, -24, -8},
            {99,  -8,  8,  6,  6,  8,  -8, 99}
    };
    for (int x=0; x<8; x++) for (int y=0;y<8;y++)
    {
        if(gb.board[x][y] == GameBoard.WHITE)
        {
            //rating+=eval_table[x][y];
            rating++;
        }
    }
    return rating;
}
}

findBestMove 函数当然在 MiniMax 子类中被覆盖:

@Override
public Field findBestMove(GameBoard gb, int depth, boolean player) 
{   
    /** maximum depth of search reached, we stop */
    if(depth >= max_depth) return null;

    //player = (depth+1)%2 + 1;

    /** getting a list of moves to chose from */
    ArrayList <Field> moves = findAllPossibleMoves(gb, player); 

    Field best_move = null;

    /** iterating over all possible moves, to find the best one */      
    for (int i=0; i<moves.size(); i++)
    {
        /** board to simulate moves */
        GameBoard temp_board = new GameBoard(gb);
        /** getting the current move */
        Field move = moves.get(i);      
        /** simulating the move for the current node */
        game.move(move, temp_board, player);
        Log.i("board", "Depth:"+depth+" Player:"+player+" Move:"+i+" Rating:"+evaluate(temp_board));
        Log.i("board", ""+moves.get(i).getX()+","+moves.get(i).getY());         
        temp_board.printBoard();
        /** getting to the next inferior node */            
        Field best_deep_move = findBestMove (temp_board, depth + 1, !player);           

        /** if the maximum depth is reached, we have a null, so we evaluate */
        if (best_deep_move == null) 
        {
            move.setRating(evaluate (temp_board));
        }
        /** if we are not the deepest possible, we get the rating from the lower node */
        else 
        {
            move.setRating(best_deep_move.getRating());         
            Log.i("eval", ""+best_deep_move.getRating());
        }           
        if(best_move == null) 
        {
            best_move = move;           
        }

        else
        {   
            Log.i("update", "Current move rating:"+move.getRating());
            Log.i("update", "New move rating:"+best_move.getRating());
            if (depth%2==0)
            {
                Log.i("update", "MAX player");
                /** for us, we look for the maximum */
                if (best_move.getRating() < move.getRating()) 
                    {


                    best_move = move;

                    }

            }
            else
            {
                Log.i("update", "MIN player");
                /** for the opponent, we look for the minimum */
                if (best_move.getRating() > move.getRating())
                { 


                    best_move = move;

                }
            }
            Log.i("update", "Updated move rating"+best_move.getRating());
        }
    }

    return best_move;
}

该算法在我当前的 Game 实例中实例化:

public class Game {

public GameBoard game_board;
public Algorithm alg;

boolean active_player;
//int other_player;
int white_count;
int black_count;
int depth;

public Game()
{
    game_board = new GameBoard(8, 8);
    active_player = true;
    //other_player = GameBoard.WHITE;
    white_count = 0;
    black_count = 0;


    // AI strategy
    alg = new MiniMaxAlgorithm(this);
}


public boolean getActive_player() {
    return active_player;

}


public void setActive_player(boolean active_player) {
    this.active_player = active_player;
}

//public boolean getOther_player() {
    //return other_player;
//}


//public void setOther_player(boolean other_player) {
    //this.other_player = other_player;
//  }



public int getWhite_count() {
    return white_count;
}


public void setWhite_count(int white_count) {
    this.white_count = white_count;
}


public int getBlack_count() {
    return black_count;
}


public void setBlack_count(int black_count) {
    this.black_count = black_count;
}

// array of 8 possible directions
public final int[][] directions = new int [][]{
        {-1, 0},
        {-1, 1},
        {0, 1},
        {1, 1},
        {1, 0},
        {1, -1},
        {0, -1},
        {-1, -1}            
};


public int playerToPiece (boolean player)
{
    return player == true? GameBoard.BLACK : GameBoard.WHITE;
}

/** Function checking if a given field is available to move
 * 
 * @param f - field to be checked
 * @return - whether or not the field is available (boolean)
 */

public boolean isAvailable (GameBoard gb, Field f, boolean player)
{
    if(gb.board[f.x][f.y] != GameBoard.EMPTY) return false;
    /** iterating over all 8 possible directions */
    for (int k=0; k<directions.length; k++)
    {
        int dx = directions[k][0];
        int dy = directions[k][1];          
        /** scanning through all fields in line with the current direction */           
        for (int i = f.x + dx, j = f.y + dy; 
                i>=0 && i<gb.getWidth() && j>=0 && j<gb.getHeight() ; i+=dx, j+=dy)
        {
            if(i == f.x + dx && j == f.y + dy && gb.board[i][j] == playerToPiece(player)) break;
            if(gb.board[i][j] == GameBoard.EMPTY) break;
            else if(gb.board[i][j] == playerToPiece(player)) return true;
        }
    }
    return false;
}

/** Function performing a move
 * 
 * @param f - destination field of the move
 * @param board - board to do the move on
 * @param player - player that performs the move
 */

public void move (Field f, GameBoard board, boolean player)
{

    if (f == null) return;
    if (isAvailable(board, f, player))
    {
        board.board[f.x][f.y] = playerToPiece(player); 
        /** iterating over all 8 possible directions */
        for (int k=0;k < directions.length; k++)
        {
            int dx = directions[k][0];
            int dy = directions[k][1];
            /** scanning through all fields in line with the current direction */               
            for (int i = f.x + dx, j = f.y + dy; 
                    i>=0 && j>=0 && i<board.getWidth() && j<board.getHeight() ; i+=dx, j+=dy)
            {
                /** if there is an empty field, there can be no line, so we move on to the next dir */
                if(board.board[i][j] == GameBoard.EMPTY) break;
                /** if the field is the same as active, we fill all the fields up to that one with 
                 * that color  */
                else if(board.board[i][j] == playerToPiece(player))
                {
                    for (int a = f.x, b = f.y; a*dx<=i*dx && b*dy<=j*dy; a+=dx, b+=dy)
                    {
                        board.board[a][b] = playerToPiece(player);
                    }
                    break;
                }
            }
        }

        white_count = black_count = 0;
        for (int i = 0; i < board.getWidth(); i++)
        {
            for (int j=0; j < board.getHeight(); j++)
            {
                if (board.board[i][j] == GameBoard.WHITE) white_count++;
                else if (board.board[i][j] == GameBoard.BLACK) black_count++;
            }
        }
    }
}


/** Function changing the active player
 * 
 */
public void changePlayer ()
{
    active_player = !active_player;
    //active_player = active_player == GameBoard.BLACK ? GameBoard.WHITE : GameBoard.BLACK;
    //other_player = other_player == GameBoard.BLACK ? GameBoard.WHITE : GameBoard.BLACK;
}

}

我确实做了研究,但我必须承认,仅仅通过谷歌搜索问题并不容易追踪和错误的循环函数。这让我非常坚持进一步的优化(alpha-beta 等),我现在真的真的需要加快速度。

4

0 回答 0