1

我已经为此工作了很长时间,以至于它不再有趣了。我正在尝试在井字游戏上实现 Minmax,虽然我已经获得了几个可以做出合理初始动作的 AI 版本,但我永远无法做出永远不会失败的版本。

我无法解决的问题之一是启发式价值。它当前在第一次 Minmax 调用时返回 -10,而它应该返回 0(无论发生什么都应该能够绘制)。

另一个问题是它运行了 400,000 次迭代,而 322,000 是最大值,考虑到早期获胜的情况,它甚至应该停止在 250,000 左右。

任何帮助将不胜感激。

int MiniMax(TGameBoard _GameBoard)
{
    //Always goes for max of course, just expanded in case you wanted two AIs

    int iBestMove;      
    int iHeuristicReturned = 0;

    if (_GameBoard.ePlayer == COMPUTER)
    {
        iHeuristicReturned = MaxMove(_GameBoard, iBestMove);
    }
    else
    {
        iHeuristicReturned = MinMove(_GameBoard, iBestMove);
    }
    //cout<<"\nHeuristic is "<<iHeuristicReturned<<endl;

    g_iHeuristic = iHeuristicReturned;
    return iBestMove;
}

int MaxMove(TGameBoard _GameBoard, int& _iMove)
{
    //Logic
    //If its an end node, calculate the score
    //Otherwise, do minmax until the end node, and pass back the value
    //If returned value is greater than v, then pass the move back upwards
    ++g_iIterations;
    if(_GameBoard.CheckWinner(_GameBoard) || _GameBoard.IsFull())
    {
        int x;
        x = EvaluateStaticPosition(_GameBoard, MAX);
        return EvaluateStaticPosition(_GameBoard, MAX);
    }
    vector<int> moveList;
    GenerateMoveList(_GameBoard, moveList);
    int iNumMoves = moveList.size();
    int v = -10000;

    for(int i = 0; i < iNumMoves; ++i)
    {
        int iMove = moveList[i];

        _GameBoard.Set(iMove, CROSS);
        int opponentsBestMove;
        ++g_iDepth;
        int curRating = MinMove(_GameBoard, opponentsBestMove);
        --g_iDepth;
        if (curRating > v)
        {
            v = curRating;
            _iMove = iMove;
        }
        RetractMove(&_GameBoard, iMove);
    }
    return v;
}

int MinMove(TGameBoard _GameBoard, int& _iMove)
{
    ++g_iIterations;
    if (g_iIterations > 320000)
    {
        int x = 0;
    }

    if(_GameBoard.CheckWinner(_GameBoard) || _GameBoard.IsFull())
    {
        return EvaluateStaticPosition(_GameBoard, MIN);
    }

    vector<int> moveList;
    GenerateMoveList(_GameBoard, moveList);
    int iNumMoves = moveList.size();
    int v = 10000;

    for(int i = 0; i < iNumMoves; ++i)
    {
        int iMove = moveList[i];
        _GameBoard.Set(iMove, NAUGHT);
        int opponentsBestMove;
        ++g_iDepth;
        int curRating = MaxMove(_GameBoard, opponentsBestMove);
        --g_iDepth;
        if (curRating < v)
        {
            v = curRating;
            _iMove = iMove;
        }
        RetractMove(&_GameBoard, iMove);
    }
    return v;
}

int EvaluateStaticPosition(TGameBoard _GameBoard, EGoal _eGoal)
{
    if(_GameBoard.CheckWinner(_GameBoard, COMPUTER))
    {
        return 10;
    } 
    if(_GameBoard.CheckWinner(_GameBoard, PLAYER))
    {
        return -10;
    } 
    return 0;
}

其他相关功能可以在这里检查,但我很确定它们没问题。 http://pastebin.com/eyaNfBsq

是的,我知道有一些不必要的参数 - 在我自己的版本失败后,我尝试按照互联网上的教程进行操作。不幸的是,他们给出了相同的结果。

我已经做了 12 个小时,这似乎是一个简单的任务,无法找出问题所在

4

1 回答 1

1

以下代码可以帮助您:

(奖励:alphabet 少于 8000 个板检查。)

#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>

enum class Square
{
    Empty,
    O,
    X
};

Square other(Square c) {
    switch (c) {
        case Square::O: return Square::X;
        case Square::X: return Square::O;
        default: assert(0); return Square::Empty;
    };
}

template <typename STREAM>
STREAM& operator << (STREAM& stream, Square c)
{
    switch (c)
    {
        case Square::Empty: stream << "."; break;
        case Square::X: stream << "X"; break;
        case Square::O: stream << "O"; break;
    }
    return stream;
}

class Board
{
public:
    Board() : board({{Square::Empty, Square::Empty, Square::Empty,
                    Square::Empty, Square::Empty, Square::Empty,
                    Square::Empty, Square::Empty, Square::Empty}})
    {}

    void display() const {
        for (int y = 0; y != 3; ++y) {
            for (int x = 0; x != 3; ++x) {
                std::cout << board[3 * y + x] << " ";
            }
            std::cout << std::endl;
        }
    }

    void play(unsigned int x, unsigned int y, Square c)
    {
        assert(x < 3);
        assert(y < 3);

        board[3 * y + x] = c;
    }
    void play(unsigned int offset, Square c)
    {
        assert(offset < 9);

        board[offset] = c;
    }

    bool isFull() const {
        return std::find(board.cbegin(), board.cend(), Square::Empty) == board.cend();
    }

    int computeScore(Square c) const
    {
        for (int i = 0; i < 3; ++i) {
            if (board[3 * i] != Square::Empty && board[3 * i] == board[3 * i + 1] && board[3 * i] == board[3 * i + 2]) {
                return board[3 * i] == c ? 1 : -1;
            }
            if (board[i] != Square::Empty && board[i] == board[i + 3] && board[i] == board[i + 6]) {
                return board[i] == c ? 1 : -1;
            }
        }
        if (board[4] == Square::Empty) {
            return 0;
        }
        if ((board[4] == board[0] && board[4] == board[8])
            || (board[4] == board[2] && board[4] == board[6])) {
            return board[4] == c ? 1 : -1;
        }
        return 0;
    }

    int minmax(Square c, unsigned int* counter, unsigned int* pos = NULL)
    {
        const int currentScore = computeScore(c);
        if (currentScore != 0 || isFull()) {
            if (counter) {++*counter; }
            return currentScore;
        }
        int bestScore = -10;

        for (unsigned int i = 0; i != 9; ++i) {
            if (board[i] != Square::Empty) { continue; }

            play(i, c);
            int score = -minmax(other(c), counter);
            if (bestScore < score) {
                bestScore = score;
                if (pos) { *pos = i; }
            }
            play(i, Square::Empty);
        }
        return bestScore;
    }

    int alphabeta(Square c, int alpha, int beta, unsigned int* counter, unsigned int* pos = NULL)
    {
        const int currentScore = computeScore(c);
        if (currentScore != 0 || isFull()) {
            if (counter) {++*counter; }
            return currentScore;
        }

        for (unsigned int i = 0; i != 9; ++i) {
            if (board[i] != Square::Empty) { continue; }

            play(i, c);
            int score = -alphabeta(other(c), -beta, -alpha, counter);
            if (beta <= score) {
                if (pos) { *pos = i; }
                play(i, Square::Empty);
                return score;
            }
            if (alpha < score) {
                alpha = score;
                if (pos) { *pos = i; }
            }
            play(i, Square::Empty);
        }
        return alpha;
    }

private:
    std::array<Square, 9> board;
};

int main()
{
    Board b;
    Square c = Square::X;

    while (b.computeScore(Square::X) == 0 && b.isFull() == false) {
        std::cout << c << " to play" << std::endl;
        b.display();
        unsigned int counter = 0;
        unsigned int pos;
        const int s = b.minmax(c, &counter, &pos);
        //const int s = b.alphabeta(c, -10, 10, &counter, &pos);
        b.play(pos, c);
        std::cout << "score for "<< c <<" = " << s << std::endl;
        std::cout << "#final boards examined = " << counter << std::endl;
        std::cout << "----------------" << std::endl;
        c = other(c);
    }
    std::cout << "Final score for X = " << b.computeScore(Square::X) << std::endl;
    b.display();

    return 0;
}

“迭代”的计数是最终检查的板数。

于 2013-09-03T17:15:04.983 回答