在四排(connect4)游戏中实现和使用MinMax [英] Implementing and using MinMax with four in row (connect4) game

查看:92
本文介绍了在四排(connect4)游戏中实现和使用MinMax的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为连续四个游戏(或connect4或连接四个)实现MinMax算法.

I'm trying to implement the MinMax algorithm for four in a row (or connect4 or connect four) game.

我想我有个主意,它应该在可能的深度上构建一棵可能的董事会树,评估它们并返回其分数,然后我们只取那些分数中的最大值.

I think I got the idea of it, it should build a tree of possible boards up to a certain depth, evaluate them and return their score, then we just take the max of those scores.

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

So, aiChooseCol() checks the score of every possible column by calling MinMax() and returns the column with the max score.

现在我不确定,这是调用MinMax()的正确方法吗?

Now I wasn't sure, is this the right way to call MinMax()?

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

我还没有做启发式功能,但这至少应该识别一个获胜的列并选择它,但是目前它只是从左边选择第一个空闲列...我不知道我在做什么错误的.

I still haven't made the heuristic function but this should at least recognize a winning column and choose it, but currently it just choose the first free column from the left... I can't figure out what am I doing wrong.

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 from heurisitic function
        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;   // tie
    }


    if (PlayerSign == m_Sign)   // maximizing Player is myself
    {
        temp = -1000;
        for (int i = 0; i < Board.Cols; i++)
        {
            if (Board.FillSignInBoardAccordingToCol(i, PlayerSign)) // so we don't open another branch in a full column
            {
                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)) // so we don't open another branch in a full column
            {
                var v = MinMax(Depth - 1, Board, otherPlayerSign);
                temp = Math.Min(temp, v);
                Board.RemoveTopCoinFromCol(i);
            }
        }
    }
    return temp;
}

一些注意事项:

FillSignInBoardAccordingToCol()如果成功,则返回布尔值.

FillSignInBoardAccordingToCol() returns a boolean if it was successful.

board类型具有一个char[,]数组,其中包含玩家的实际棋盘和标志.

The board type has a char[,] array with the actual board and signs of the players.

此代码在AI Player类中.

This code is in the AI Player class.

推荐答案

因此,我决定编写自己的MinMax Connect4.我使用深度来确定获胜或失败的价值,以便使您更接近赢得或阻止损失将优先.我还决定,如果有多个具有相同的启发式方法,我将随机选择此举.最终,我将深度扩展到6,因为从一开始就需要多少步才能找到可能的获胜路线.

So I decided to write my own MinMax Connect 4. I used the depth to determine the value of a win or loss so that a move that gets you closer to winning or blocking a loss will take precedence. I also decide that I will randomly pick the move if more than one has the same heuristic. Finally I stretched out the depth to 6 as that's how many moves are required to find possible win paths from the start.

private static void Main(string[] args)
{
    var board = new Board(8,7);
    var random = new Random();

    while (true)
    {
        Console.WriteLine("Pick a column 1 -8");
        int move;
        if (!int.TryParse(Console.ReadLine(), out move) || move < 1 || move > 8)
        {
            Console.WriteLine("Must enter a number 1-8.");
            continue;
        }

        if (!board.DropCoin(1, move-1))
        {
            Console.WriteLine("That column is full, pick another one");
            continue;
        }

        if (board.Winner == 1)
        {
            Console.WriteLine(board);
            Console.WriteLine("You win!");
            break;
        }

        if (board.IsFull)
        {
            Console.WriteLine(board);
            Console.WriteLine("Tie!");
            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("You lost!");
            break;
        }

        if (board.IsFull)
        {
            Console.WriteLine("Tie!");
            break;
        }
    }

    Console.WriteLine("DONE");
    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();
    }
}

这篇关于在四排(connect4)游戏中实现和使用MinMax的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆