You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#实现Minimax算法井字棋游戏逻辑异常求助

C# 井字棋Minimax算法异常修复

问题表现

  • 玩家在棋盘(1,1)坐标落子"O"后,AI未按预期在(2,1)位置落"X"阻挡玩家获胜,反而选择(1,0)位置落子,直接放任玩家取胜
  • 调试抓取Minimax返回值发现所有结果均为0,无法区分落子优先级

核心错误点

  1. Minimax递归终止顺序错误

现有代码先遍历所有空位执行递归,最后才判断棋盘胜负状态。胜负已分的节点会被当作普通节点继续向下递归落子,导致胜负状态对应的得分完全被覆盖,最终所有返回值趋近于0。Minimax标准逻辑要求先判断当前节点是否为终止节点(获胜/平局),终止节点直接返回得分,非终止节点才遍历落子递归。

  1. 胜利线校验逻辑漏项
    CheckWinner方法中仅校验了3条横线、1条竖线、2条对角线,缺失第一列、第三列的竖线胜利判定,无法正确识别玩家即将连成竖线获胜的场景。
  2. 得分深度折扣逻辑错误
    现有代码根据当前回合方选择深度加减规则,实际深度折扣和回合方无关:AI获胜时得分=10-深度(越快赢得分越高),玩家获胜时得分=-10+深度(越晚输得分越高),平局固定返回0。原逻辑会导致落子优先级判断完全混乱。
  3. 缺失平局终止判断
    没有判断棋盘填满无空位的平局场景,递归终止逻辑不完整。

修复后完整代码

// 全局变量定义(保持和原有逻辑一致)
List<int> scores = new List<int>();
int[] index = new int[2];

public static void AIMove(int[,] board)
{
    int bestScore = int.MinValue;
    scores.Clear();

    for (int i = 0; i < board.GetLength(0); i++)
    {
        for (int j = 0; j < board.GetLength(1); j++)
        {
            if (board[i, j] == -1)
            {
                board[i, j] = 1;
                // AI落子后轮到玩家(turn=0),初始深度为0
                var score = MiniMax(board, 0, 0);
                scores.Add(score);
                board[i, j] = -1;

                if (score > bestScore)
                {
                    bestScore = score;
                    index[0] = i;
                    index[1] = j;
                }
            }
        }
    }

    board[index[0], index[1]] = 1;
}

public static int MiniMax(int[,] board, int turn, int depth)
{
    // 第一步先判断终止状态:胜负已分直接返回折扣后得分
    int winner = CheckWinner(board);
    if (winner != 0)
    {
        // AI获胜返回10-depth(优先选最快赢的路径),玩家获胜返回-10+depth(优先选最慢输的路径)
        return winner == 10 ? 10 - depth : -10 + depth;
    }

    // 判断平局:无空位且无胜者
    bool isFull = true;
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            if (board[i, j] == -1)
            {
                isFull = false;
                break;
            }
        }
        if (!isFull) break;
    }
    if (isFull) return 0;

    // 非终止节点遍历所有落子可能
    int bestScore = turn == 1 ? int.MinValue : int.MaxValue;
    for (int i = 0; i < board.GetLength(0); i++)
    {
        for (int j = 0; j < board.GetLength(1); j++)
        {
            if (board[i, j] == -1)
            {
                board[i, j] = turn;
                var score = MiniMax(board, 1 - turn, depth + 1);
                board[i, j] = -1;
                // AI回合最大化得分,玩家回合最小化得分
                bestScore = turn == 1 ? Math.Max(bestScore, score) : Math.Min(bestScore, score);
            }
        }
    }

    return bestScore;
}

public static int CheckWinner(int[,] board)
{
    // 补全全部8条胜利线:3横+3竖+2对角线
    int[,] winLines = new int[,]
    {
        {board[0,0], board[0,1], board[0,2]}, // 第一横
        {board[1,0], board[1,1], board[1,2]}, // 第二横
        {board[2,0], board[2,1], board[2,2]}, // 第三横
        {board[0,0], board[1,0], board[2,0]}, // 第一竖
        {board[0,1], board[1,1], board[2,1]}, // 第二竖
        {board[0,2], board[1,2], board[2,2]}, // 第三竖
        {board[0,0], board[1,1], board[2,2]}, // 主对角线
        {board[0,2], board[1,1], board[2,0]}  // 副对角线
    };

    for (int i = 0; i < 8; i++)
    {
        if (winLines[i,0] == 1 && winLines[i,1] ==1 && winLines[i,2] ==1)
            return 10; // AI获胜
        if (winLines[i,0] ==0 && winLines[i,1] ==0 && winLines[i,2] ==0)
            return -10; // 玩家获胜
    }

    return 0; // 无胜者
}

修复验证

修复后Minimax返回值会正确区分不同落子的得分:玩家在(1,1)落子后,AI选择(2,1)阻挡的得分会高于选择(1,0)的得分,会优先选择阻挡玩家的落子点,不会再出现放任玩家获胜的问题。

内容的提问来源于stack exchange,提问作者HeadEx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 06:24:16