我现在为我糟糕的英语道歉,我是意大利人。
我正在用C#,Player vs Player和Player vs Computer编写完整的国际象棋游戏,现在我在实现NegaMax算法时遇到了一些困难。 对于谁感兴趣,这是我的github存储库:https://github.com/crybot/ChessEngine_NOGUI/tree/master/Chess%20Engine%20-%20NOGUI
我试图让这个项目成为OO,所以,如果您认为我的设计不正确,请告诉我:)
这是我的问题:
我实现了一个非常简单的评估函数,对所有玩家都是对称的:
public static float Evaluate(PieceColor playerColor, Game game)
{
float score = 0;
score += 1 * (game.board.GetNumberOfPieces(PieceType.Pawn, playerColor)
- game.board.GetNumberOfPieces(PieceType.Pawn, playerColor.GetOpposite()));
score += 3 * (game.board.GetNumberOfPieces(PieceType.Bishop, playerColor)
- game.board.GetNumberOfPieces(PieceType.Bishop, playerColor.GetOpposite()));
score += 3 * (game.board.GetNumberOfPieces(PieceType.Knight, playerColor)
- game.board.GetNumberOfPieces(PieceType.Knight, playerColor.GetOpposite()));
score += 5 * (game.board.GetNumberOfPieces(PieceType.Rook, playerColor)
- game.board.GetNumberOfPieces(PieceType.Rook, playerColor.GetOpposite()));
score += 9 * (game.board.GetNumberOfPieces(PieceType.Queen, playerColor)
- game.board.GetNumberOfPieces(PieceType.Queen, playerColor.GetOpposite()));
score += 0.1f * (game.GetPlayer(playerColor).GetMoves(game).Count);
return score;
}
这是我的NegaMax功能,它接受一个游戏参数(包括棋盘,玩家,ecc.ecc。),由枚举提供的playerColor,以及深度搜索;
我首先扫描EnginePlayer的所有可能动作,然后我从NegaMax函数得到它们的分数,但是有些东西不起作用......
private float NegaMax(Game game, PieceColor playerColor, int depth)
{
if (depth == 0)
return EvaluateMove(playerColor, game);
float max = float.MinValue;
float score;
foreach (Move move in game.GetPlayer(playerColor.GetOpposite()).GetMoves(game))
{
game.board.SimulateMove(move);
score = Math.Max(max, -NegaMax(game, playerColor.GetOpposite(), depth - 1));
game.board.CancelMove(move);
if (score > max)
max = score;
}
return max;
}
public Move ThinkMove(Game game, PieceColor playerColor, int depth)
{
Move bestMove = new NullMove();
float bestScore = float.MinValue;
float temp = 0;
foreach (Move move in GetMoves(game))
{
game.board.SimulateMove(move);
temp = NegaMax(game, playerColor, depth);
game.board.CancelMove(move);
if (temp > bestScore)
{
if (Judge.IsLegal(move, game))
{
bestMove = move;
bestScore = temp;
}
}
}
if (bestMove is NullMove)
throw new NotImplementedException();
return bestMove;
}
我认为这就是全部...我希望你会发现我做错了:) 感谢。
修改 我实现了一个Perft,它显示了移动生成器的非正确性。它帮助了很多人找到了一些相对容易发现的错误,但最后一些结果仍然是错误的。 结果如下:
Perft Depth: 1
Nodes: 20 Captures: 0 Checks: 0 Castles: 0 Mates: 0 EnPassant: 0
Perft Depth: 2
Nodes: 400 Captures: 0 Checks: 0 Castles: 0 Mates: 0 EnPassant: 0
Perft Depth: 3
Nodes: 8902 Captures: 34 Checks: 12 Castles: 0 Mates: 0 EnPassant: 0
Perft Depth: 4
Nodes: 197281 Captures: 1610 Checks: 473 Castles: 0 Mates: 8 EnPassant: 0
以下是正确的结果:https://chessprogramming.wikispaces.com/Perft+Results
正如您所看到的,在深度4处,我得到了正确的节点分析,但是错误的捕获和检查值(配合是正确的)。
感谢这些结果,我试图通过将每次移动分析的节点划分为深度4来隔离错误,从而获得这些结果:
Move Nodes
a2a3 8457
a2a4 9329
b2b3 9345
b2b4 9332
c2c3 9272
c2c4 9744
d2d3 11959
d2d4 12435
e2e3 13134
e2e4 13160
f2f3 8457
f2f4 8929
g2g3 9345
g2g4 9328
h2h3 8457
h2h4 9329
b1a3 8885
b1c3 9755
g1f3 9748
g1h3 8881
Total Nodes: 197281
与从Sharper获得的结果进行比较:
Sharper v0.17 by Albert Bertilsson
divide 4
b1c3 9755
b1a3 8885
g1h3 8881
g1f3 9748
a2a3 8457
a2a4 9329
b2b3 9345
b2b4 9332
c2c3 9272
c2c4 9744
d2d3 11959
d2d4 12435
e2e3 13134
e2e4 13160
f2f3 8457
f2f4 8929
g2g3 9345
g2g4 9328
h2h3 8457
h2h4 9329
Nodes: 197281
Moves: 20
但即使在这里价值也是正确的...... 所以我尝试了5深度的深度来获得这些结果:
Move Nodes
a2a3 181046
a2a4 217813
b2b3 215255
b2b4 216110
c2c3 222861
c2c4 240044
d2d3 328511
d2d4 361753
e2e3 402988
e2e4 405348
f2f3 178891
f2f4 198437
g2g3 217210
g2g4 214017
h2h3 181044
h2h4 218810
b1a3 198572
b1c3 234656
g1f3 233491
g1h3 198502
Total Nodes: 4865359
然后与Sharper的结果进行比较:
Sharper v0.17 by Albert Bertilsson
divide 5
b1c3 234656
b1a3 198572
g1h3 198502
g1f3 233491
a2a3 181046
a2a4 217832
b2b3 215255
b2b4 216145
c2c3 222861
c2c4 240082
d2d3 328511
d2d4 361790
e2e3 402988
e2e4 405385
f2f3 178889
f2f4 198473
g2g3 217210
g2g4 214048
h2h3 181044
h2h4 218829
Nodes: 4865609
Moves: 20
所以我意识到这个问题是由双兵推动所产生的动作造成的...但我真的找不到这个错误... 有没有人遇到同样的问题? 或者类似的东西,或只是提示找到那个bug ...
感谢所有人:)
答案 0 :(得分:0)
首先确保移动生成是一致的,have a look here on how to。这被称为perft测试。另一个奇怪的事情是我无法在你的算法中看到“伙伴”威胁:当没有任何动作并且国王被捕获时你应该返回一个非常糟糕的价值(否则就是一个立场)。那么你应该采取一些策略来对动作进行排序,以便从negamax算法中获益,然后采用一些“视野”策略(也就是说,如果深度== 0时仍有捕获会发生什么?)。 如果您对移动生成有问题,请尝试使用更有趣的位置have a look here for example来挑战您的引擎,或者如果您想要更难的this is the file that I use for testing my engine。它由许多包含FEN格式位置的行组成,然后按以下格式进行深度移动计数:
D1 50; D2 279
这意味着(例如)在深度1处移动50次,在深度2处移动279次
我提出的立场来自我对护目镜的旧研究,关于具有挑战性的立场。你需要能够support FEN notation,如果你还没有我强烈建议实施,因为它是在国际象棋引擎(而不仅仅是)世界中传播位置的“事实上的”标准。
答案 1 :(得分:0)
而且,this家伙似乎也有同样的问题。
我仔细检查了代码(并用天真的替代品替换了一些复杂的代码),但是找不到任何错误。毕竟,总移动数是正确的,不是吗?
然后我意识到:1610 = 1576 + 34.这只是一个例子,我检查了其余的perft结果,并确定这是问题。
那些'标准'perft结果表计数中的捕获计数仅捕获深度等于0的叶节点,包括en passant捕获。