【问题标题】:Tic Tac Toe: Evaluating Heuristic Value of a Node井字游戏:评估节点的启发式价值
【发布时间】:2018-09-09 11:15:17
【问题描述】:

如果这个问题已经存在,请原谅我,我已经搜索了很多,但我没有得到我想问的问题的答案。所以,基本上,我正在尝试实现一个井字游戏 AI,它使用 Minimax 算法进行移动。

但是,我不明白的一件事是,当在空棋盘上使用 Minimax 时,返回的值始终为 0(这是有道理的,因为如果两个玩家都玩得最好,游戏总是以平局结束)。

因此,当 AI 为 X 时,Minimax 总是选择第一个图块作为最佳移动(因为所有移动都返回 0 作为值)。第二步也是如此,它总是选择第二个图块。我该如何解决这个问题,让我的 AI 选择获胜概率更高的棋步?这是我使用的评估和 Minimax 函数(使用 Alpha-Beta 剪枝):

int evaluate(char board[3][3], char AI)
{
for (int row = 0; row<3; row++)
{
    if (board[row][0] != '_' && board[row][0] == board[row][1] && board[row][1] == board[row][2])
    {
        if (board[row][0]==AI)
        {
            return +10;
        }
        else
        {
            return -10;
        }
    }
}

for (int col = 0; col<3; col++)
{
    if (board[0][col] != '_' && board[0][col] == board[1][col] && board[1][col] == board[2][col])
    {
        if (board[0][col]==AI)
        {
            return +10;
        }

        else
        {
            return -10;
        }
    }
}

if (board[1][1] != '_' && ((board[0][0]==board[1][1] && board[1][1]==board[2][2]) || (board[0][2]==board[1][1] && board[1][1]==board[2][0])))
{
    if (board[1][1]==AI)
    {
        return +10;
    }
    else
    {
        return -10;
    }
}

return 0;
}

int Minimax(char board[3][3], bool AITurn, char AI, char Player, int depth, int alpha, int beta)
{
bool breakout = false;
int score = evaluate(board, AI);

if(score == 10)
{
    return score - depth;
}
else if(score == -10)
{
    return score + depth;
}
else if(NoTilesEmpty(board))
{
    return 0;
}

if(AITurn == true)
{
    int bestvalue = -1024;
    for(int i = 0; i < 3; i++)
    {
        for(int j = 0; j<3; j++)
        {
            if(board[i][j] == '_')
            {
                board[i][j] = AI;
                bestvalue = max(bestvalue, Minimax(board, false, AI, Player, depth+1, alpha, beta));
                alpha = max(bestvalue, alpha);
                board[i][j] = '_';
                if(beta <= alpha)
                {
                    breakout = true;
                    break;
                }
            }
        }
        if(breakout == true)
        {
            break;
        }
    }
    return bestvalue;
}

else if(AITurn == false)
{
    int bestvalue = +1024;
    for(int i = 0; i < 3; i++)
    {
        for(int j = 0; j<3; j++)
        {
            if(board[i][j] == '_')
            {
                board[i][j] = Player;
                bestvalue = min(bestvalue, Minimax(board, true, AI, Player, depth+1, alpha, beta));
                beta = min(bestvalue, beta);
                board[i][j] = '_';
                if(beta <= alpha)
                {
                    breakout = true;
                    break;
                }
            }
        }
        if(breakout == true)
        {
            break;
        }
    }
    return bestvalue;
}
}

【问题讨论】:

    标签: c++ tic-tac-toe minimax


    【解决方案1】:

    Minimax 假设最佳游戏,因此最大化“获胜概率”并不是一个有意义的概念:由于其他玩家可以强制平局但不能强制获胜,他们将始终强制平局。如果您想与一个不完全理性的玩家进行最佳比赛(当然,这是仅有的两种获胜方式之一*),您需要假设对手动作的一些概率分布并使用ExpectMinimax之类的东西,其中有一定概率对手的移动被随机错误覆盖。或者,您可以故意限制极小极大搜索的层数,对对手的游戏使用超出一定深度的启发式算法(但仍然在游戏树中搜索您自己的动作。)

     * 另一个不玩了。

    【讨论】:

    • 另一种获胜方式是偶尔回显错误的字符。人类会认为他们打错了。
    • 那么,确保 AI 更好地使用第一和第二步的最佳方法是什么?
    • @Arnil 没有“更好”,除非你定义一个。再读一遍我的答案。
    【解决方案2】:

    将您的代码组织成更小的例程,使其看起来更整洁且更易于调试。除了递归极小极大函数之外,一个全可能有效的移动生成函数和一个鲁棒的评估子例程是必不可少的(这里似乎缺少)。

    例如,在比赛开始时,评估算法应该返回一个非零分数,每个位置都应该有一个相对的得分指数(例如中间位置可能比角落的权重略高)。

    您的极小极大边界条件 - 如果没有空单元格位置则返回;有缺陷,因为即使在前一层中发生了赢/输动作,它也会进行评估。这种情况在更复杂的 AI 游戏中会加剧。

    如果您是 minimax 新手,您可以在 CodeReview 上找到大量可编译的示例代码

    【讨论】:

      猜你喜欢
      • 2014-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-12
      • 1970-01-01
      • 2015-01-08
      相关资源
      最近更新 更多