我已经为游戏跳棋编写了一个带有alpha-beta修剪的minimax算法,现在我正尝试使用negamax方法重写它。我希望这两者是等价的,因为negamax只是一种编写minimax的技术。但由于某种原因,我的两种算法表现不同。当我在相同的输入上运行它们时,negamax版本似乎评估了更多的状态,所以我认为alpha-beta修剪一定有问题。
下面的代码显示了这两种算法(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*/ }
匿名用户
您的函数是正确的。我假设状态为。getNextPlayer()返回下一个必须移动的玩家。这意味着您的evaluate()
和negamax()
函数从该玩家的角度返回一个分数。
但是,ximax()
从max
的角度返回一个分数。因此,如果您尝试在play()
函数中取消注释ximax()
,这将导致错误
//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
参数替换对最小值()
的调用应该可以解决它:
int v = minimax(move, 10, a, b, true); // assumes perspective of max player
我最近实现了极小极大和阿尔法贝塔修剪算法,我100%确定(自动分级器)我正确地实现了它们。但是当我执行我的程序时,它们的行为不同。我99%确定极小极大和阿尔法贝塔的结束状态应该是相同的。我说得对吗?它们在实现结果的路径上会有所不同吗?因为我们忽略了min将选择的一些值,而max不会选择这些值,反之亦然。
我到处寻找修复代码的答案,但在花了很长时间调试代码后,我发现自己陷入了绝望。问题是,我的minimax函数不会为可能的最佳移动返回正确的值,我甚至试图通过存储最佳的第一个移动(当深度=0时)来修复它,但如果解决方案不明显,那么该算法将严重失败。我还尝试修改基本案例的返回值,以便优先考虑早期的胜利,但这并没有解决问题。 目前我正在TictoE板上测试这个函数,助手类(如getMoves()或getW
我已经实现了一个带有alpha beta修剪的NegaMax算法(这只是一个较短版本的极小值算法)。现在我想实现迭代深化,这样我就可以为每个深度找到最佳移动,然后根据之前层的分数重新排序树下的节点,以便我的alphabeta修剪工作更有效。 以下是我迄今为止所做的工作: 这里gs是随每一步移动而变化的游戏属性,包含了所有关于游戏在t点的信息,比如是否可以施法或者是否有可能的内移。我的egamax算
我在为游戏筷子做一个C程序。 这是一个非常简单的游戏,总共只有625个游戏状态(如果考虑到对称性和不可到达的状态,它甚至更低)。我读过minimax和alpha-beta算法,主要是针对tic-tac-toe的,但我遇到的问题是,在tic-tac-toe中,不可能循环回到以前的状态,而这在筷子中很容易发生。因此,当运行代码时,它将以堆栈溢出结束。 我通过添加以前访问过的州的标志来解决这个问题(我不
我试图在我的象棋引擎中实现alpha-beta剪枝,但没有性能差异,我可能做错了什么?我试着用控制台记录算法剪切一个分支的次数,但它的数量是数百次,因此它可以正确地修剪搜索树。即使这样,该算法也没有明显的性能改进。 董事会评估平均需要80毫秒左右。使用alpha-beta修剪,查看深度3时,minimax/alpha-beta算法需要1.8秒,而不使用minimax/alpha-beta算法需要1
我想我终于对minimax和Alpha-beta修剪有所了解了,但实现它完全是另一回事! 根据我的理解,基础是:您为某些动作分配一个启发式函数分数(Gomoku为例)。 如果一行有5个,我们应该分配一个高值,比如9999,因为这是一个胜利的举动 当我们必须在Java中实现这一点时,我的问题来了! 我有一块彩色[][]板(8x8),其中黑色是播放器1,白色是播放器2,null表示空白,我不知道我们应