换位表导致测试失败(但在游戏中运行良好)
Transposition Table is causing tests to fail(but runs fine in game)
我在我的TicTacToe minmax算法中添加了一个换位表
int AI::findBestMove()
{
hash = tTable->recalculateHash();
int bestMove = minMax().second;
return bestMove;
}
std::pair<int, int> AI::minMax(int reverseDepth, std::pair<int, int> bestScoreMove, player currentPlayer, int alpha, int beta, int lastPlay)
{
Entry e = (*tTable)[hash];
if (e && e.depth == reverseDepth)
return e.scoreMove;
if (reverseDepth == 0)
return { 0, -2 };
else if (field->canDrawOrWin() && lastPlay != -1)
{
if (field->hasWon(lastPlay))
return { evaluateScore(currentPlayer), -1 };
else if (field->isDraw())
return { 0, -1 };
}
bestScoreMove.first = currentPlayer == player::AI ? INT_MIN : INT_MAX;
for (int i = 0; i < field->size(); i++)
{
if ((*field)[i] == player::None && field->isCoordWorthChecking(i))
{
(*field)[i] = currentPlayer;
hash = tTable->calculateHash(hash, i);
std::pair<int, int> scoreMove = minMax(reverseDepth - 1, bestScoreMove, getOpponent(currentPlayer), alpha, beta, i);
if (currentPlayer == player::AI)
{
alpha = std::max(alpha, scoreMove.first);
if (bestScoreMove.first < scoreMove.first)
bestScoreMove = { scoreMove.first, i };
}
else
{
beta = std::min(beta, scoreMove.first);
if (bestScoreMove.first > scoreMove.first)
bestScoreMove = { scoreMove.first, i };
}
hash = tTable->calculateHash(hash, i);
(*field)[i] = player::None;
if (beta <= alpha)
break;
}
}
tTable->placeEntry(hash, bestScoreMove, reverseDepth);
return bestScoreMove;
}
为了测试它,我做了一个验收测试,播放所有可能的棋盘并检查人类是否赢得
TEST(AcceptanceTest, EveryBoard)
{
int winstate = 0;
std::shared_ptr<Field> field = std::make_shared<Field>(4);
AI ai(field);
playEveryBoard(ai, field, winstate);
std::cout <<"Human wins: " << winstate << std::endl;
}
void playEveryBoard(AI& ai, std::shared_ptr<Field> f, int& winstate)
{
int bestMove = 0;
auto it = f->begin();
while (true)
{
it = std::find(it, f->end(), player::None);
if (it == f->end())
break;
*it = player::Human;
if (f->hasWon())
winstate++;
EXPECT_TRUE(!f->hasWon());
bestMove = ai.findBestMove();
if (bestMove == -1)//TIE
{
*it = player::None;
break;
}
(*f)[bestMove] = player::AI;
if (f->hasWon())//AI WIN
{
*it = player::None;
(*f)[bestMove] = player::None;
break;
}
playEveryBoard(ai, f, winstate);
*it = player::None;
(*f)[bestMove] = player::None;
if (it == f->end())
break;
it++;
}
}
在我添加换位表之前,测试从未返回任何松动状态,为了测试松动状态何时出现,我做了一个测试,播放松动字段的每个排列,但它从未发现松动状态,是什么导致AI只在EveryBoard测试中松动?
TEST(LoosePossible, AllPermutations)
{
std::vector<int> loosingField = { 2, 3, 7, 11, 12, 13, 15 };
do{
std::shared_ptr<Field> field = std::make_shared<Field>(4);
AI *ai = new AI(field);
for (auto i : loosingField)
{
if ((*field)[i] != player::None || field->hasWon())
break;
(*field)[i] = player::Human;
EXPECT_TRUE(!field->hasWon());
(*field)[ai->findBestMove()] = player::AI;
}
delete ai;
} while (next_permutation(loosingField.begin(), loosingField.end()));
}
我看到至少有两个地方可能会出现这些错误。
一个潜在的问题就在这条线上:
Entry e = (*tTable)[hash];
if (e && e.depth == reverseDepth)
return e.scoreMove;
除了检查换位表是否存储了相同深度的搜索结果外,还需要检查表中存储的边界是否与表中的边界兼容。
我在回答另一个问题时提到了这一点:
在换位表中存储值时,还需要存储搜索过程中使用的alpha和beta边界。当您在搜索中期从节点处获取值时,它要么是真值的上界(因为值=β(,要么是真数值的下界(因为值=alpha(,要么就是节点的实际值(alpha<value<beta(。您需要将其存储在换位表中。然后,当你想重用该值时,你必须检查你是否可以使用给定当前阿尔法和贝塔边界的值。(在换位表中找到值后,您可以通过实际搜索来验证这一点,看看您是否从搜索中获得了与表中相同的值。(
对此进行测试的方法是修改AI::minMax
。当从换位表返回值时,将标志设置为true。然后,每次返回值时,如果换位表标志为true,请将要返回的值与在换位表中找到的值进行比较。如果它们不一样,那么就有问题了。
此外,minimax通常与零和游戏一起使用,这意味着两个玩家的分数之和应该加成0。我不知道所有返回的值在您的代码中意味着什么,但有时您返回{0, -1}
,有时返回{0, -2}
。这是有问题的,因为现在你有一个非零和游戏,很多理论都崩溃了。
特别地,最大玩家可以相同地对待{0, -1}
和{0, -2}
,但是最小玩家不会。因此,如果移动顺序以任何方式发生变化,您可能会以不同的顺序看到这些,因此树根部的值将不稳定。
顺便说一句,这是多人游戏中的一个基本问题。实际上,当一个玩家是造王者时,就会出现这种情况。他们自己不能赢得比赛,但他们可以决定谁赢。
- 换位表导致测试失败(但在游戏中运行良好)
- 我的代码运行良好,但在游戏循环中中断
- 有没有办法让我编写一个可以在Windows和Linux上运行的命令行游戏?
- 我制作了 2048 游戏,但它没有按预期运行
- 在运行时创建和更改游戏状态
- C++ Qt - 检查 Steam 游戏是否以编程方式运行
- 如何使用运行时检查错误来修复我的C 格斗游戏
- 将鼠标单击发送到以管理员身份运行的DirectX游戏
- 如何在游戏运行时创建新函数并执行它
- 如何在游戏C++中运行两个循环
- 为wp7构建了一个将在wp8和w8上运行的游戏
- 使用CreateProcess运行游戏可执行文件
- 为什么我的 SFML 游戏只能在终端中运行时使用
- Cocos 2dx游戏无法在Mac OS 64位上运行,但在iOS模拟器上运行
- 运行SFML游戏时出现奇怪错误
- 在Qt窗口中运行游戏循环
- 使用do while循环和while循环来编写一个程序来运行随机猜谜游戏…我卡在了do while循环上
- IPhone游戏可以在设备上运行.在模拟器中的特定关卡崩溃
- CPP中的井字游戏程序未运行
- SDL_Surface在游戏运行几分钟后消失