实现和使用四子棋游戏中的MinMax算法

我正在尝试为四子棋(或称为Connect4)游戏实现MinMax算法。

我认为我已经理解了它的原理,它应该构建一个可能的棋盘树到一定深度,评估它们并返回它们的分数,然后我们只需取这些分数的最大值。

因此,aiChooseCol()通过调用MinMax()检查每个可能的列的分数,并返回具有最高分数的列。

现在我不确定,这样调用MinMax()是否正确?

检查temp = Math.Max(temp, 1000);是否正确?

我还没有制作启发式函数,但这至少应该能识别出获胜的列并选择它,但目前它只是从左边选择第一个空闲的列…我搞不清楚我做错了什么。

private int AiChooseCol(){    int best = -1000;    int col=0;    for (int i = 0; i < m_Board.Cols; i++)    {        if (m_Board.CheckIfColHasRoom(i))        {            m_Board.FillSignInBoardAccordingToCol(i, m_Sign);            int t = MinMax(5, m_Board, board.GetOtherPlayerSign(m_Sign));            if (t > best)            {                best = t;                col = i;            }            m_Board.RemoveTopCoinFromCol(i);        }    }    return col;}private int MinMax(int Depth, board Board, char PlayerSign){    int temp=0;    if (Depth <= 0)    {        // 从启发式函数返回        return temp;    }    char otherPlayerSign = board.GetOtherPlayerSign(PlayerSign);    char checkBoard = Board.CheckBoardForWin();    if (checkBoard == PlayerSign)    {        return 1000;    }    else if (checkBoard == otherPlayerSign)    {        return -1000;    }    else if (!Board.CheckIfBoardIsNotFull())    {        return 0;   // 平局    }    if (PlayerSign == m_Sign)   // 最大化玩家是我自己    {        temp = -1000;        for (int i = 0; i < Board.Cols; i++)        {            if (Board.FillSignInBoardAccordingToCol(i, PlayerSign)) // 所以我们不会在满列中打开另一个分支            {                var v = MinMax(Depth - 1, Board, otherPlayerSign);                temp = Math.Max(temp, v);                Board.RemoveTopCoinFromCol(i);            }        }    }    else    {        temp = 1000;        for (int i = 0; i < Board.Cols; i++)        {            if (Board.FillSignInBoardAccordingToCol(i, PlayerSign)) // 所以我们不会在满列中打开另一个分支            {                var v = MinMax(Depth - 1, Board, otherPlayerSign);                temp = Math.Min(temp, v);                Board.RemoveTopCoinFromCol(i);            }        }    }    return temp;}

一些说明:

FillSignInBoardAccordingToCol()如果成功会返回一个布尔值。

board类型有一个char[,]数组,包含实际的棋盘和玩家的标记。

这段代码位于AI玩家类中。


回答:

因此,我决定编写自己的MinMax Connect 4算法。我使用深度来确定赢或输的价值,以便更接近赢或阻止输的移动会优先考虑。我还决定,如果有多个移动具有相同的启发式值,我将随机选择一个移动。最后,我将深度扩展到6,因为从开始需要六个移动来找到可能的赢路径。

private static void Main(string[] args){    var board = new Board(8,7);    var random = new Random();    while (true)    {        Console.WriteLine("选择列 1 - 8");        int move;        if (!int.TryParse(Console.ReadLine(), out move) || move < 1 || move > 8)        {            Console.WriteLine("必须输入1到8之间的数字。");            continue;        }        if (!board.DropCoin(1, move-1))        {            Console.WriteLine("该列已满,请选择另一列");            continue;        }        if (board.Winner == 1)        {            Console.WriteLine(board);            Console.WriteLine("你赢了!");            break;        }        if (board.IsFull)        {            Console.WriteLine(board);            Console.WriteLine("平局!");            break;        }        var moves = new List<Tuple<int, int>>();        for (int i = 0; i < board.Columns; i++)        {            if (!board.DropCoin(2, i))                continue;            moves.Add(Tuple.Create(i, MinMax(6, board, false)));            board.RemoveTopCoin(i);        }        int maxMoveScore = moves.Max(t => t.Item2);        var bestMoves = moves.Where(t => t.Item2 == maxMoveScore).ToList();        board.DropCoin(2, bestMoves[random.Next(0,bestMoves.Count)].Item1);        Console.WriteLine(board);        if (board.Winner == 2)        {            Console.WriteLine("你输了!");            break;        }        if (board.IsFull)        {            Console.WriteLine("平局!");            break;        }    }    Console.WriteLine("完成");    Console.ReadKey();}private static int MinMax(int depth, Board board, bool maximizingPlayer){    if (depth <= 0)        return 0;    var winner = board.Winner;    if (winner == 2)        return depth;    if (winner == 1)        return -depth;    if (board.IsFull)        return 0;    int bestValue = maximizingPlayer ? -1 : 1;    for (int i = 0; i < board.Columns; i++)    {        if (!board.DropCoin(maximizingPlayer ? 2 : 1, i))            continue;        int v = MinMax(depth - 1, board, !maximizingPlayer);        bestValue = maximizingPlayer ? Math.Max(bestValue, v) : Math.Min(bestValue, v);        board.RemoveTopCoin(i);    }    return bestValue;}public class Board{    private readonly int?[,] _board;    private int? _winner;    private bool _changed;    public Board(int cols, int rows)    {        Columns = cols;        Rows = rows;        _board = new int?[cols, rows];    }    public int Columns { get; }    public int Rows { get; }    public bool ColumnFree(int column)    {        return !_board[column, 0].HasValue;    }    public bool DropCoin(int playerId, int column)    {        int row = 0;        while (row < Rows && !_board[column,row].HasValue)        {            row++;        }        if (row == 0)            return false;        _board[column, row - 1] = playerId;        _changed = true;        return true;    }    public bool RemoveTopCoin(int column)    {        int row = 0;        while (row < Rows && !_board[column, row].HasValue)        {            row++;        }        if (row == Rows)            return false;        _board[column, row] = null;        _changed = true;        return true;    }    public int? Winner    {        get        {            if (!_changed)                return _winner;            _changed = false;            for (int i = 0; i < Columns; i++)            {                for (int j = 0; j < Rows; j++)                {                    if (!_board[i, j].HasValue)                        continue;                    bool horizontal = i + 3 < Columns;                    bool vertical = j + 3 < Rows;                    if (!horizontal && !vertical)                        continue;                    bool forwardDiagonal = horizontal && vertical;                    bool backwardDiagonal = vertical && i - 3 >= 0;                    for (int k = 1; k < 4; k++)                    {                        horizontal = horizontal && _board[i, j] == _board[i + k, j];                        vertical = vertical && _board[i, j] == _board[i, j + k];                        forwardDiagonal = forwardDiagonal && _board[i, j] == _board[i + k, j + k];                        backwardDiagonal = backwardDiagonal && _board[i, j] == _board[i - k, j + k];                        if (!horizontal && !vertical && !forwardDiagonal && !backwardDiagonal)                            break;                    }                    if (horizontal || vertical || forwardDiagonal || backwardDiagonal)                    {                        _winner = _board[i, j];                        return _winner;                    }                }            }            _winner = null;            return _winner;        }    }    public bool IsFull    {        get        {            for (int i = 0; i < Columns; i++)            {                if (!_board[i, 0].HasValue)                    return false;            }            return true;        }    }    public override string ToString()    {        var builder = new StringBuilder();        for (int j = 0; j < Rows; j++)        {            builder.Append('|');            for (int i = 0; i < Columns; i++)            {                builder.Append(_board[i, j].HasValue ? _board[i,j].Value.ToString() : " ").Append('|');            }            builder.AppendLine();        }        return builder.ToString();    }}

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注