我正在尝试编写国际象棋游戏,并花了几天时间尝试修复代码。我甚至尝试了min max但结果却相同。人工智能总是从角落开始,然后将一个棋子移开,然后车轮每回合就来回移动。如果被吃掉,人工智能会将每一块从一侧移到另一侧,直到所有的一块都被吃掉。你知道下面的代码有什么问题吗?
public Move MakeMove(int depth)
{
bestmove.reset();
bestscore = 0;
score = 0;
int maxDepth = depth;
negaMax(depth, maxDepth);
return bestmove;
}
public int EvalGame() //calculates the score from all the pieces on the board
{
int score = 0;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
if (AIboard[i, j].getPiece() != GRID.BLANK)
{
score += EvalPiece(AIboard[i, j].getPiece());
}
}
}
return score;
}
private int negaMax(int depth, int maxDepth)
{
if (depth <= 0)
{
return EvalGame();
}
int max = -200000000;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
for (int k = 0; k < 8; k++)
{
for (int l = 0; l < 8; l++)
{
if(GenerateMove(i, j, k, l)) //generates all possible moves
{
//code to move the piece on the board
board.makemove(nextmove);
score = -negaMax(depth - 1, maxDepth);
if( score > max )
{
max = score;
if (depth == maxDepth)
{
bestmove = nextmove;
}
}
//code to undo the move
board.undomove;
}
}
}
}
}
return max;
}
public bool GenerateMove(int i, int j, int k, int l)
{
Move move;
move.moveFrom.X = i;
move.moveFrom.Y = j;
move.moveTo.X = k;
move.moveTo.Y = l;
if (checkLegalMoves(move.moveTo, move.moveFrom)) //if a legal move
{
nextMove = move;
return true;
}
return false;
}
答案 0 :(得分:0)
此代码:
public Move MakeMove(int depth)
{
bestscore = 0;
score = 0;
int maxDepth = depth;
negaMax(depth, maxDepth);
return bestmove;
}
请注意,最好的动作永远不会被设定!将negaMax
的回报分数与移动替代方案进行比较。你甚至没有绕过可能的动作。
此外,当您提交的代码不完全一致时,很难找到错误。 negaMax
方法在代码中的一个位置有两个参数,然后在递归调用中需要四个参数?
我还建议您在代码中使用更好的抽象。单独的板表示,移动表示,移动生成和搜索算法。这对你有很大的帮助。举个例子:为什么在移动生成中需要深度计数器?
-Øystein
答案 1 :(得分:0)
您有两个可能的问题:
由于您没有向我们展示您的变量声明,因此有些含糊不清,但我认为您使用了太多的全局变量。 Negamax通过计算每个节点的最佳移动来工作,因此在搜索值和移动时应该是本地的。无论如何,优良作法是尽可能地保持变量的范围。当遍历游戏树时,很难对代码进行推理,从而改变了很多变量。但是,您的搜索看起来应该返回正确的值。
您的评估似乎没有区分哪一方正在播放。我不知道EvalPiece
是否处理这个问题,但无论如何评估应该从当前有权移动的方面进行评估。
您还有其他与您的问题不直接相关的问题:
你的移动一代是可怕的。你成对地遍历棋盘上每一对可能的方块。这是非常低效的,我不明白这种方法是如何工作的。您只需要遍历电路板上的所有部件,或者使用较慢的方法,即电路板上的每个方块(而不是4096个方格)。
MakeMove
似乎可能是根节点的位置。现在,您的方案有效,因为搜索退出的最后一个节点将是root。但是,通常在根处使用特殊例程,例如迭代加深,因此在根处有一个单独的循环可能是好的。