【问题标题】:What is wrong with my minimax algorithm for tictactoe我的 tictactoe 的极小极大算法有什么问题
【发布时间】:2015-07-24 18:33:55
【问题描述】:

我正在构建一个井字游戏,以获得有趣的学习体验。我已经构建了一个极小极大算法来返回计算机的最佳移动,但不知何故我出错并得到这样的奇怪输出

TIC TAC TOE V1.0
---
---
---
Enter row, column of your move
1,1
---
-X-
---
...
0, 0: -1038
0, 1: -1470
0, 2: -1038
1, 0: -1470
1, 2: -1470
2, 0: -1038
2, 1: -1470
2, 2: -1038
O--
-X-
---
Enter row, column of your move
1,2
O--
-XX
---
...
0, 1: -15
0, 2: -9
1, 0: -10
2, 0: -1
2, 1: -29
2, 2: -41
O--
-XX
O--
Enter row, column of your move
1,0
O--
XXX
O--
WINNER: PLAYER

您可以看到计算机选择了左下角而不是切断播放器。我的代码尝试通过所有可能的游戏状态在回合之间递归翻转,总结回合可能导致的每次输赢的分数,然后返回具有最高分数的移动。打印出来的是每回合的得分(你可以看到它选择了最高的),那我为什么不切断玩家呢?我怎样才能解决这个问题?这是我的代码。

int compMoveScoreRecursive(state_t **board, int dimension, int row, int col, state_t turn) {
    board[row][col] = turn;
    state_t winner = checkWinner(board, dimension);
    if (winner == COMPUTER) {
        return 1;
    } else if (winner == PLAYER) {
        return -1;
    } else {
        int score = 0;
        state_t nextTurn = turn == COMPUTER ? PLAYER : COMPUTER;
        for (int i = 0; i < dimension; i++) {
            for (int j = 0; j < dimension; j++) {
                if (board[i][j] == NIL) {
                    state_t **boardCopy = copyBoard(board, dimension);
                    score += compMoveScoreRecursive(boardCopy, dimension, i, j, nextTurn);
                    destroyBoard(boardCopy, dimension);
                }
            }
        }
        return score;
    }
}

move_t optimalCompMove(state_t **board, int dimension) {
    move_t optMove;
    int optScore = INT_MIN;
    for (int row = 0; row < dimension; row++) {
        for (int col = 0; col < dimension; col++) {
            if (board[row][col] == NIL) {
                state_t **boardCopy = copyBoard(board, dimension);
                int score = compMoveScoreRecursive(boardCopy, dimension, row, col, COMPUTER);
                printf("%d, %d: %d\n", row, col, score);
                if (score > optScore) {
                    optMove.row = row;
                    optMove.col = col;
                    optScore = score;
                }
                destroyBoard(boardCopy, dimension);
            }
        }
    }
    return optMove;
}

【问题讨论】:

  • 在每次递归期间打印前景板。结果可能会让您大吃一惊。

标签: c algorithm tic-tac-toe minimax


【解决方案1】:

minmax 算法的概念是“最小化最大损失”(Wikipedia),所以你的算法首先出错的是你的总和。

对于游戏的任何状态S,以及对于当前玩家(假设玩家1 P1)的任何移动Mminmax (S + M, P2) 的值是最大值 如果P1 播放MP2 的可能输出。所以如果P1想要最大化他的获胜机会,他应该尽可能减少P2的最大输出,即他必须找到输出的最小值

tictactoe minmax中,可以测试整个游戏(最多9步),这意味着如果PX赢(1),输(-1)或使你总是现在平局(0)。所以minmax (state, PX) 只会返回这三个值之一。

在很多游戏中,你不能玩整个游戏(例如选秀),所以返回值是状态的指示,例如-oo如果你输了,+oo如果你输了你赢了,否则你的选秀数和你的对手之间的差异。

【讨论】:

  • 这是否意味着我可以在任何时候选择几个具有相同最小值最大值的动作?即可能的移动产生+1、+1、-1、0、+1。我将如何在这些之间进行选择?还是没关系?
  • 是的,您会发现多个具有相同值的移动,对于 minmax 算法,选择无关紧要。
【解决方案2】:

据我了解,在compMoveScoreRecursive的实现中,递归计算的分数是通过

score += compMoveScoreRecursive(boardCopy, dimension, i, j, nextTurn);

而不是最大化或最小化值。但是,根据参数turn,要返回的值应该在最小化时最大化,这也是该方法被称为 MinMax 的原因。

【讨论】:

  • 您的理解是正确的,在state 状态下,对于p1 可用的任何moveminmax (state + move, p2) 应该是@987654328 的最大 可能输出@如果p1move,那么为了最大化他的获胜机会,p1必须打出move,最小化p2的最大输出。
  • 因此,我不需要将每个可能的移动的得分相加,而是需要根据对手是否可以从该游戏点达到获胜状态来简单地返回 +1 或 -1?
  • 完全正确;在游戏完美进行的假设下,移动的值应该在 {0,-1,1} 中表示哪个玩家赢得了游戏(或 0 表示平局),这意味着每个后续移动也是最优的。
  • 我将如何在相同分数的移动之间进行选择,或者没有关系
  • 没关系,可能有不止一个最佳移动。
【解决方案3】:

您的算法背后的概念似乎有缺陷。根据您描述的方式,您正在考虑每条游戏线,而不是假设对手会做出正确的举动。正因为如此,对手可以在下一步中获胜这一事实的重要性很小,因为您还考虑了其​​他 4 步提供的所有选项(尽管这些选项显然永远不会出现)。您必须将您的算法改进为正确的最小-最大,而不是搜索整个棋盘状态

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多