C++ 使用alpha-beta修剪将minimax转换为negamax
我已经为游戏跳棋编写了一个算法,现在我正试图用这种方法重写它。我希望这两者是等价的,因为negamax只是一种编写minimax的技术。但由于某些原因,我的两个算法的行为不同。当我在同一个输入上运行它们时,negamax版本似乎评估了更多的状态,因此我认为alpha-beta修剪一定有问题 下面的代码显示了这两种算法(C++ 使用alpha-beta修剪将minimax转换为negamax,c++,algorithm,minimax,alpha-beta-pruning,C++,Algorithm,Minimax,Alpha Beta Pruning,我已经为游戏跳棋编写了一个算法,现在我正试图用这种方法重写它。我希望这两者是等价的,因为negamax只是一种编写minimax的技术。但由于某些原因,我的两个算法的行为不同。当我在同一个输入上运行它们时,negamax版本似乎评估了更多的状态,因此我认为alpha-beta修剪一定有问题 下面的代码显示了这两种算法(minimax和negamax函数),底部是我调用它们的play函数。evaluate函数是我在两种算法中用于评估状态的基本启发式方法 任何有助于发现错误的方法都会非常有用 #in
minimax
和negamax
函数),底部是我调用它们的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*/ }
#包括“player.hpp”
#包括
#包括
#包括
命名空间检查器
{
int evaluatedState=0;
int求值(常量游戏状态和状态)
{
//修正:改进启发式。
int redScore=0;
int-whiteScore=0;
整块=0;
对于(int i=1;i你的minimax()
和negamax()
函数是正确的。我假设state.getNextPlayer()
返回下一个必须移动的玩家。这意味着evaluate()
和negamax()
函数从该玩家的角度返回分数
但是,minimax()
从max
的角度返回一个分数。因此,如果尝试在play()
函数中取消注释minimax()
,则会导致错误
//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;
}
用true
参数替换对minimax()
的调用应该可以解决这个问题:
int v = minimax(move, 10, a, b, true); // assumes perspective of max player
很好。就是这样。非常感谢,你关于getNextPlayer()的假设是正确的。很高兴能为checkers的程序员同事提供帮助:-)顺便说一句,我想把它命名为playerToMoveNext()