在一定深度的 Minimax 树中计算移动分数

Posted

技术标签:

【中文标题】在一定深度的 Minimax 树中计算移动分数【英文标题】:Computing a move score in a Minimax Tree of a certain depth 【发布时间】:2015-11-07 23:44:02 【问题描述】:

我用 C 语言实现了一个国际象棋游戏,具有以下结构:

move - 表示在 char board[8][8](棋盘)上从 (a,b) 到 (c,d) 的移动

moves - 这是一个带有头部和尾部的动作的链接列表。

变量: play_color 是“W”或“B”。 minimax_depth 是之前设置的 minimax 深度。

这是我的带有 alpha-beta 修剪的 Minimax 函数和 getMoveScore 函数的代码,该函数应该返回之前设置的某个 minimax_depth 的 Minimax Tree 中移动的分数。

我也在使用 getBestMoves 函数,我也会在这里列出,它基本上会在 Minimax 算法中找到最佳移动并将它们保存到一个全局变量中,以便我以后可以使用它们。

我必须补充的是,我将在此处添加的三个函数中列出的所有函数都可以正常工作并经过测试,所以问题要么是alphabetaMax算法的逻辑问题,要么是实现 getBestMoves/getMoveScore。

问题主要在于,当我在深度 N 处获得最佳移动(也不是以某种方式计算正确)然后使用 getMoveScore 函数检查它们在相同深度上的分数时,我得到的分数不同'与那些实际最佳动作的分数不匹配。我已经花了几个小时调试这个并且看不到错误,我希望也许有人可以给我一个关于找到问题的提示。

代码如下:

/*
* Getting best possible moves for the playing color with the minimax algorithm
*/
moves* getBestMoves(char playing_color)
    //Allocate memory for the best_moves which is a global variable to fill it in   a minimax algorithm//
    best_moves = calloc(1, sizeof(moves));
    //Call an alpha-beta pruned minimax to compute the best moves//
    alphabeta(playing_color, board, minimax_depth, INT_MIN, INT_MAX, 1);
    return best_moves;


/*
* Getting the score of a given move for a current player
*/
int getMoveScore(char playing_color, move* curr_move)
    //Allocate memory for best_moves although its not used so its just freed    later//
    best_moves = calloc(1, sizeof(moves));
    int score;
    char board_cpy[BOARD_SIZE][BOARD_SIZE];
    //Copying a a current board and making a move on that board which score I   want to compute//
    boardCopy(board, board_cpy);
    actualBoardUpdate(curr_move, board_cpy, playing_color);
    //Calling the alphabeta Minimax now with the opposite color , a board after     a given move and as a minimizing player, because basicly I made my move so  its now the opponents turn and he is the minimizing player//
    score = alphabeta(OppositeColor(playing_color), board_cpy, minimax_depth, INT_MIN, INT_MAX, 0);
    freeMoves(best_moves->head);
    free(best_moves);
    return score;


/*
* Minimax function - finding the score of the best move possible from the input board
*/
int alphabeta(char playing_color, char curr_board[BOARD_SIZE][BOARD_SIZE], int depth,int alpha,int beta, int maximizing) 
    if (depth == 0)
        //If I'm at depth 0 I'm evaluating the current board with my scoring            function//
        return scoringFunc(curr_board, playing_color);
    
    int score;
    int max_score;
    char board_cpy[BOARD_SIZE][BOARD_SIZE];
    //I'm getting all the possible legal moves for the playing color//
    moves * all_moves = getMoves(playing_color, curr_board);
    move* curr_move = all_moves->head;
    //If its terminating move I'm evaluating board as well, its separate from depth == 0 because    only here I want to free memory//
    if (curr_move == NULL)
        free(all_moves);
        return scoringFunc(curr_board,playing_color);
    
    //If maximizing player is playing//
    if (maximizing) 
        score = INT_MIN;
        max_score = score;
        while (curr_move != NULL)
            //Make the move and call alphabeta with the current board               after the move for opposite color and !maximizing player//
            boardCopy(curr_board, board_cpy);
            actualBoardUpdate(curr_move, board_cpy, playing_color);
            score = alphabeta(OppositeColor(playing_color), board_cpy, depth - 1,alpha,beta, !maximizing);
            
            alpha = MAX(alpha, score);
            if (beta <= alpha)
                break;
            
            //If I'm at the maximum depth I want to get current player              best moves//
            if (depth == minimax_depth)
                move* best_move;
                //If I found a move with a score that is bigger then                    the max score, I will free all previous moves and                   append him, and update the max_score//
                if (score > max_score)
                    max_score = score;
                    freeMoves(best_moves->head);
                    free(best_moves);
                    best_moves = calloc(1, sizeof(moves));
                    best_move = copyMove(curr_move);
                    concatMoves(best_moves, best_move);
                
                //If I have found a move with the same score and want                   to concatenate it to a list of best moves//
                else if (score == max_score)
                    best_move = copyMove(curr_move);
                    concatMoves(best_moves, best_move);
                
                
            
            //Move to the next move//
            curr_move = curr_move->next;
        
        freeMoves(all_moves->head);
        free(all_moves);
        return alpha;
    
    else 
        //The same as maximizing just for a minimizing player and I dont want       to look for best moves here because I dont want to minimize my          outcome//
        score = INT_MAX;
        while (curr_move != NULL)
            boardCopy(curr_board, board_cpy);
            actualBoardUpdate(curr_move, board_cpy, playing_color);
            score = alphabeta(OppositeColor(playing_color), board_cpy, depth - 1,alpha,beta, !maximizing);
            beta = MIN(beta, score);
            if (beta <= alpha)
                break;
            
            curr_move = curr_move->next;
        
        freeMoves(all_moves->head);
        free(all_moves);
        return beta;
    

正如 Eugene 所指出的,我在这里添加一个示例: http://imageshack.com/a/img910/4643/fmQvlm.png

我目前是白色玩家,我只有king-k和queen-q,相反的颜色有king-K和rook-R。显然,我在这里最好的举动是吃掉车或至少引起检查。棋子的移动经过测试,它们工作正常。虽然当我在深度 3 调用 get_best_moves 函数时,我在该深度得到了很多不必要的移动和负分数。也许现在它更清楚了。谢谢!

【问题讨论】:

无 MCVE,无预期行为,无实际行为。我们有一点关系。 @EugeneSh。我现在添加了一个详细的示例,我是否应该添加其他内容? @EvgenyA.:在其他地方进行建设性合作时,给了你一个 +1。你比我更需要它。 ;-) 【参考方案1】:

如果不调试您的整个代码,至少有一个问题是您的 scoreverification 可能适用于 minimax 算法,但不适用于 Alpha-Beta。以下问题:

getMoveScore() 函数必须从打开的 AB 窗口开始。

getBestMoves() 然而调用 getMoveScore() 与一个已经关闭的 AB 窗口。

因此,在 getBestMoves 的情况下,可能存在未在 getMoveScore() 中修剪的分支,因此分数不准确,这就是(或至少其中一个)这些值可能不同的原因。

【讨论】:

我不太明白关闭 AB 窗口是什么意思,你的意思是我应该用 OppositeColor 调用 getMoveScore 中的函数 alphabeta 但作为最大化播放器?据我了解,在 getMoveScore 中我采取了行动,所以我应该为对手调用字母,但他应该最小化还是最大化? AB Window 与 min 或 max 无关。例如,Alpha Beta 窗口为 -300 +100,表示您的 alpha 和 beta 值。由于截止,不同的 Alpha 或 Beta 值通常会导致不同的移动值。 好的我明白了,你说的打开AB窗口是什么意思?我应该尝试什么价值观?或者我如何计算我需要的值?顺便说一句,getBestMoves 不调用 getMoveScore,它们是独立的。 @Evgeny 如果您希望两者的输出正常,则需要存储用于特定分数的 alpha beta 值,或者关闭 alpha beta 修剪。无论如何,我不知道你为什么要做这样的检查...... 好的,谢谢,我会记住的。我只是在学习 CS,这是我们一门课程中的一个项目。

以上是关于在一定深度的 Minimax 树中计算移动分数的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 中的代码度量计算

如何在Haskell中创建函数minimax?

二叉树中的权值是啥?

二叉树中的权值是啥?

设计一个算法,计算出给定二叉树中任意2 个结点之间的最短路径。

我想知道连接 4 游戏的 minimax 算法有啥问题?