5

ゲームチェッカー用にアルファベータプルーニングを使用してミニマックスアルゴリズムを作成しましたが、現在、ネガマックスアプローチを使用してそれを書き直そうとしています。negamax はミニマックスを記述するための単なる手法であるため、2 つが同等であると期待しています。しかし、何らかの理由で、2 つのアルゴリズムの動作が異なります。両方を同じ入力で実行すると、negamax バージョンの方がより多くの状態を評価するように見えるので、アルファ ベータの剪定に何か問題があるに違いないと思います。

以下のコードは両方のアルゴリズム (minimaxnegamax関数) を示しており、一番下playにはそれらを呼び出す関数が示されています。evaluate関数は、両方のアルゴリズムで状態を評価するために使用する基本的なヒューリスティックです。

エラーを見つけるための助けは非常に高く評価されます。

#include "player.hpp"
#include <algorithm>
#include <limits>
#include <cstdlib>

namespace checkers
{

int evaluatedStates = 0;

int evaluate(const GameState &state)
{
    // FIXME: Improve heuristics.
    int redScore = 0;
    int whiteScore = 0;
    int piece = 0;
    for (int i = 1; i <= 32; ++i)
    {
        piece = state.at(i);
        if (piece & CELL_RED) {
            ++redScore;
            if (piece & CELL_KING)
                redScore += 2;   // King bonus.
        } else if (piece & CELL_WHITE) {
            ++whiteScore;
            if (piece & CELL_KING)
                whiteScore += 2; // King bonus.
        }
    }
    return state.getNextPlayer() == CELL_RED ? whiteScore - redScore : redScore - whiteScore;
}

int minimax(const GameState &state, int depth, int a, int b, bool max)
{
    if (depth == 0 || state.isEOG()) {
        ++evaluatedStates;
        return evaluate(state);
    }
    std::vector<GameState> possibleMoves;
    state.findPossibleMoves(possibleMoves);
    if (max) {
        for (const GameState &move : possibleMoves) {
            a = std::max(a, minimax(move, depth - 1, a, b, false));
            if (b <= a)
                return b; // β cutoff.
        }
        return a;
    } else {
        for (const GameState &move : possibleMoves) {
            b = std::min(b, minimax(move, depth - 1, a, b, true));
            if (b <= a)
                return a; // α cutoff.
        }
        return b;
    }
}

int negamax(const GameState &state, int depth, int a, int b)
{
    if (depth == 0 || state.isEOG()) {
        ++evaluatedStates;
        return evaluate(state);
    }
    std::vector<GameState> possibleMoves;
    state.findPossibleMoves(possibleMoves);
    for (const GameState &move : possibleMoves) {
        a = std::max(a, -negamax(move, depth - 1, -b, -a));
        if (b <= a)
            return b; // β cutoff.
    }
    return a;
}

GameState Player::play(const GameState &pState, const Deadline &pDue)
{
    GameState bestMove(pState, Move());

    std::vector<GameState> possibleMoves;
    pState.findPossibleMoves(possibleMoves);

    int a = -std::numeric_limits<int>::max();
    int b = std::numeric_limits<int>::max();
    for (const GameState &move : possibleMoves) {
        int v = negamax(move, 10, a, b);
        //int v = minimax(move, 10, a, b, false);
        if (v > a) {
            a = v;
            bestMove = move;
        }
    }
    std::cerr << "Evaluated states: " << evaluatedStates << std::endl;
    return bestMove;
}

/*namespace checkers*/ }
4

1 に答える 1

1

あなたminimax()negamax()機能は正しいです。state.getNextPlayer()次に動かなければならないプレイヤーを返すと思います。これは、 関数evaluate()negamax()関数がそのプレーヤーの観点からスコアを返すことを意味します。

ただし、 はminimax()の観点からスコアを返しますmax。したがってminimax()play()関数でコメントを外そうとすると、バグが発生します

//int v = negamax(move, 10, a, b);
int v = minimax(move, 10, a, b, false); // assumes perspective of min player
                                ^^^^^

if (v > a) {                            // assumes perspective of max player
    a = v;
    bestMove = move;
}

minimax()への呼び出しをtrueパラメーターに置き換えると、次のように解決されます。

int v = minimax(move, 10, a, b, true); // assumes perspective of max player
于 2013-09-15T20:21:14.777 に答える