Algorithm 国际象棋:阿尔法-贝塔中的虫子

Algorithm 国际象棋:阿尔法-贝塔中的虫子,algorithm,artificial-intelligence,chess,minimax,alpha-beta-pruning,Algorithm,Artificial Intelligence,Chess,Minimax,Alpha Beta Pruning,我正在实现一个国际象棋引擎,我已经编写了一个相当复杂的alpha-beta搜索例程,包括静态搜索和换位表。然而,我观察到一个奇怪的错误 评估函数使用的是单件方格表,如用于典当的: static int ptable_pawn[64] = { 0, 0, 0, 0, 0, 0, 0, 0, 30, 35, 35, 40, 40, 35, 35, 30, 20, 25, 25, 30, 30, 25, 25, 20, 10, 20, 20, 20, 20, 20,

我正在实现一个国际象棋引擎,我已经编写了一个相当复杂的alpha-beta搜索例程,包括静态搜索和换位表。然而,我观察到一个奇怪的错误

评估函数使用的是单件方格表,如用于典当的:

static int ptable_pawn[64] = {  
   0,  0,  0,  0,  0,  0,  0,  0,
  30, 35, 35, 40, 40, 35, 35, 30,
  20, 25, 25, 30, 30, 25, 25, 20,
  10, 20, 20, 20, 20, 20, 20, 10,
   3,  0, 14, 15, 15, 14,  0,  3,
   0,  5,  3, 10, 10,  3,  5,  0,
   5,  5,  5,  5,  5,  5,  5,  5,
   0,  0,  0,  0,  0,  0,  0,  0
};
轮到布莱克时,工作台将沿x轴反射。具体地说,如果您感到好奇,查找是这样进行的,其中A-H列映射到0-7,行从怀特一侧开始为0-7:

int ptable_index_for_white(int col, int row) {
    return col+56-(row*8);
}

int ptable_index_for_black(int col, int row) {
    return col+(row*8);
}
因此,h4(坐标7,3)上的一个棋子对于白色相当于3分(厘米棋子),f6(坐标5,5)上的一个棋子对于黑色相当于3厘米棋子

目前,整个评估功能是一张方格表和一份材料

在更深入的搜索中,我的引擎选择了一些真正可怕的动作。考虑从起始位置产生的输出:

Iterative Deepening Analysis Results (including cached analysis)
Searching at depth 1... d1 [+0.10]: 1.b1c3 
    (4 new nodes, 39 new qnodes, 0 qnode aborts, 0ms), 162kN/s
Searching at depth 2... d2 [+0.00]: 1.e2e4 d7d5 
    (34 new nodes, 78 new qnodes, 0 qnode aborts, 1ms), 135kN/s
Searching at depth 3... d3 [+0.30]: 1.d2d4 d7d5 2.c1f4 
    (179 new nodes, 1310 new qnodes, 0 qnode aborts, 4ms), 337kN/s
Searching at depth 4... d4 [+0.00]: 1.g1f3 b8c6 2.e2e4 d7d5 
    (728 new nodes, 2222 new qnodes, 0 qnode aborts, 14ms), 213kN/s
Searching at depth 5... d5 [+0.20]: 1.b1a3 g8f6 2.d2d4 h8g8 3.c1f4 
    (3508 new nodes, 27635 new qnodes, 0 qnode aborts, 103ms), 302kN/s
Searching at depth 6... d6 [-0.08]: 1.d2d4 a7a5 2.c1f4 b7b6 3.f4c1 c8b7 
    (21033 new nodes, 112915 new qnodes, 0 qnode aborts, 654ms), 205kN/s
Searching at depth 7... d7 [+0.20]: 1.b1a3 g8f6 2.a1b1 h8g8 3.d2d4 g8h8 4.c1f4 
    (39763 new nodes, 330837 new qnodes, 0 qnode aborts, 1438ms), 258kN/s
Searching at depth 8... d8 [-0.05]: 1.e2e4 a7a6 2.e4e5 a6a5 3.h2h4 d7d6 4.e5d6 c7d6 
    (251338 new nodes, 2054526 new qnodes, 0 qnode aborts, 12098ms), 191kN/s
Calculating...
Iterative Deepening Analysis Results (including cached analysis)
Searching at depth 1... d1 [+2.25]: 3...g8h6 4.(q)c3d5 (q)d8d5 
    (1 new nodes, 3 new qnodes, 0 qnode aborts, 0ms, 23kN/s)
    (ttable: 3/27777778 = 0.00% load, 0 hits, 0 misses, 3 inserts (with 0 overwrites), 0 insert failures)
Searching at depth 2... d2 [-0.13]: 3...f5e4 4.c3e4 (q)d5e4 
    (32 new nodes, 443 new qnodes, 0 qnode aborts, 3ms, 144kN/s)
    (ttable: 369/27777778 = 0.00% load, 0 hits, 0 misses, 366 inserts (with 0 overwrites), 0 insert failures)
Searching at depth 3... d3 [+0.25]: 3...g8h6 4.c3e2 h6g4 
    (230 new nodes, 2664 new qnodes, 0 qnode aborts, 24ms, 122kN/s)
    (ttable: 2526/27777778 = 0.01% load, 0 hits, 0 misses, 2157 inserts (with 0 overwrites), 0 insert failures)
Searching at depth 4... d4 [-0.10]: 3...g8f6 4.e3e4 f5e6 5.f1b5 
    (2084 new nodes, 13998 new qnodes, 0 qnode aborts, 100ms, 162kN/s)
    (ttable: 15663/27777778 = 0.06% load, 0 hits, 0 misses, 13137 inserts (with 0 overwrites), 2 insert failures)
Searching at depth 5... d5 [+0.15]: 3...g8f6 4.f1e2 h8g8 5.g2g4 f5e4 6.(q)c3e4 (q)f6e4 
   (38987 new nodes, 1004867 new qnodes, 0 qnode aborts, 2765ms, 378kN/s)
   (ttable: 855045/27777778 = 3.08% load, 0 hits, 0 misses, 839382 inserts (with 0 overwrites), 302 insert failures)
在深度8处,请注意黑色以“…a7a6…a6a5”的动作打开,根据工件方格表,这些动作非常可怕。此外,“h2h4”对怀特来说是一个可怕的举动。为什么我的搜索功能会选择如此离奇的动作?值得注意的是,这只会在更深的地方发生(在深度3处的移动看起来不错)

而且,搜索经常会出错!考虑以下位置:

Iterative Deepening Analysis Results (including cached analysis)
Searching at depth 1... d1 [+0.10]: 1.b1c3 
    (4 new nodes, 39 new qnodes, 0 qnode aborts, 0ms), 162kN/s
Searching at depth 2... d2 [+0.00]: 1.e2e4 d7d5 
    (34 new nodes, 78 new qnodes, 0 qnode aborts, 1ms), 135kN/s
Searching at depth 3... d3 [+0.30]: 1.d2d4 d7d5 2.c1f4 
    (179 new nodes, 1310 new qnodes, 0 qnode aborts, 4ms), 337kN/s
Searching at depth 4... d4 [+0.00]: 1.g1f3 b8c6 2.e2e4 d7d5 
    (728 new nodes, 2222 new qnodes, 0 qnode aborts, 14ms), 213kN/s
Searching at depth 5... d5 [+0.20]: 1.b1a3 g8f6 2.d2d4 h8g8 3.c1f4 
    (3508 new nodes, 27635 new qnodes, 0 qnode aborts, 103ms), 302kN/s
Searching at depth 6... d6 [-0.08]: 1.d2d4 a7a5 2.c1f4 b7b6 3.f4c1 c8b7 
    (21033 new nodes, 112915 new qnodes, 0 qnode aborts, 654ms), 205kN/s
Searching at depth 7... d7 [+0.20]: 1.b1a3 g8f6 2.a1b1 h8g8 3.d2d4 g8h8 4.c1f4 
    (39763 new nodes, 330837 new qnodes, 0 qnode aborts, 1438ms), 258kN/s
Searching at depth 8... d8 [-0.05]: 1.e2e4 a7a6 2.e4e5 a6a5 3.h2h4 d7d6 4.e5d6 c7d6 
    (251338 new nodes, 2054526 new qnodes, 0 qnode aborts, 12098ms), 191kN/s
Calculating...
Iterative Deepening Analysis Results (including cached analysis)
Searching at depth 1... d1 [+2.25]: 3...g8h6 4.(q)c3d5 (q)d8d5 
    (1 new nodes, 3 new qnodes, 0 qnode aborts, 0ms, 23kN/s)
    (ttable: 3/27777778 = 0.00% load, 0 hits, 0 misses, 3 inserts (with 0 overwrites), 0 insert failures)
Searching at depth 2... d2 [-0.13]: 3...f5e4 4.c3e4 (q)d5e4 
    (32 new nodes, 443 new qnodes, 0 qnode aborts, 3ms, 144kN/s)
    (ttable: 369/27777778 = 0.00% load, 0 hits, 0 misses, 366 inserts (with 0 overwrites), 0 insert failures)
Searching at depth 3... d3 [+0.25]: 3...g8h6 4.c3e2 h6g4 
    (230 new nodes, 2664 new qnodes, 0 qnode aborts, 24ms, 122kN/s)
    (ttable: 2526/27777778 = 0.01% load, 0 hits, 0 misses, 2157 inserts (with 0 overwrites), 0 insert failures)
Searching at depth 4... d4 [-0.10]: 3...g8f6 4.e3e4 f5e6 5.f1b5 
    (2084 new nodes, 13998 new qnodes, 0 qnode aborts, 100ms, 162kN/s)
    (ttable: 15663/27777778 = 0.06% load, 0 hits, 0 misses, 13137 inserts (with 0 overwrites), 2 insert failures)
Searching at depth 5... d5 [+0.15]: 3...g8f6 4.f1e2 h8g8 5.g2g4 f5e4 6.(q)c3e4 (q)f6e4 
   (38987 new nodes, 1004867 new qnodes, 0 qnode aborts, 2765ms, 378kN/s)
   (ttable: 855045/27777778 = 3.08% load, 0 hits, 0 misses, 839382 inserts (with 0 overwrites), 302 insert failures)

引擎推荐了一个可怕的错误(3…f5h3),不知何故错过了明显的回答(4.g2h3):

不涉及静止搜索,因为错误发生在第1层(!!)

这是我的搜索功能的代码。很抱歉,它太长了:我尽可能简化了,但我不知道哪些部分与bug无关。我认为我的算法有点微妙的错误

这个实现几乎完全基于维基百科。(更新:我已经大大简化了搜索,我的bug仍然存在。)

//统一的alpha beta和静止搜索
内部abq(板*b、内部alpha、内部beta、内部厚度){
pthread_testcancel();//允许搜索工作线程终止
布尔静止=(ply=β)返回分数;
}
//更新搜索统计信息
if(静止)sstats.qnodes_++;
else sstats.nodes_++;
//搜索hueristic:使用MVV-LVA对交换进行排序
if(静止和mvvlva)nlopt_qsort_r(移动、可用数量_移动、大小(移动)、b和捕捉_移动_比较器);
移动最佳移动=不移动;
int最佳分数=负无穷大;
int num\u moves\u实际检查=0;//我们可能会被将死
对于(int i=num\u available\u moves-1;i>=0;i--){//向后迭代以匹配MVV-LVA排序顺序
应用(b,移动[i]);
//永远不要进入控制状态
coord king_loc=b->黑色_to_move?b->白色_king:b->黑色_king;//对于刚移动的一侧
如果(在检查中(b,king_loc.col,king_loc.row,!(b->black_to_move))){
(b,移动[i]);
继续;
}
int得分=-abq(b,-beta,-alpha,ply-1);
num_moves_实际检查++;
(b,移动[i]);
如果(得分>=最佳得分){
最佳得分=得分;
最佳移动=移动[i];
}
alpha=最大值(alpha,迄今为止的最佳得分);
如果(α>=β)断裂;
}
//我们没有不受控制的可用移动(或捕获)
//这意味着在正常的搜索中处于将死或相持状态
//这可能意味着静止搜索中没有可用的捕获
if(num\u moves\u实际检查==0){
if(静止)返回相对_求值(b);//TODO:qsearch不理解相持或将死
坐标王位置=b->黑底移动?b->黑底王:b->白底王;
如果(在检查中(b,king_loc.col,king_loc.row,b->black_to_move))返回负无穷大;//将死
else返回0;//相持
}
//在换位表中记录所选的移动
evaltype类型=(静止)?qexact:精确;
评估评估={.best=最佳移动,.score=最佳移动,.type=类型,.depth=ply};
tt_put(b,eval);
返回最佳分数;
}
/* 
*从将要移动的一侧的角度返回板位置的相对评估值。
*/
int相对_评估(委员会*b){
int评估=评估(b);
如果(b->黑色移动)评估=-评估;
回报评估;
}
我是这样调用搜索的:

int result = abq(b, NEG_INFINITY, POS_INFINITY, ply);
编辑:即使我简化了搜索程序,错误仍然存在。这台发动机只是把零件弄得乱七八糟。您可以将其加载到XBoard(或任何其他与UCI兼容的GUI)中,并在强大的引擎上播放,从而轻松看到这一点。应manlio的要求,我上传了代码:


这是GitHub存储库(链接已删除;问题在上面的代码段中)。它将使用OS X或任何*nix系统上的“make”进行构建。

我很乐意看看实际的回购协议,但我在实现类似的游戏算法时多次遇到过这个确切的问题。我会告诉你是什么导致了我的问题,你可以检查你是否犯了同样的错误。这些都是按照我猜最有可能解决你的问题的顺序列出的

ply不是移动,每次迭代移动应增加2(这就是ply的含义) 这一错误几乎总是通过对第一个玩家的几乎每一个动作都做出错误的选择来表明,因为他们永远看不到做出错误动作的后果。避免这种情况的方法是将移动次数增加2次(或者更一般地说是根据游戏中的玩家数量,但你使用的是最小最大值,所以是2次)。这确保每个玩家在下一回合之前都会考虑后果

必须始终从当前玩家的角度进行评估 这听起来很明显,但我发誓每次我实现一个求值函数时我都会搞砸它。在设计评估时,我们总是从第一个玩家的角度进行设计,而我们应该做的是设计
Iterative Deepening Analysis Results (including cached analysis)
Searching at depth 1... d1 [+0.10]: 1.e2e4 
    (1 new nodes, 4 new qnodes, 0 qnode aborts, 0ms, 65kN/s)
    (ttable: 1/27777778 = 0.00% load, 0 hits, 0 misses, 1 inserts (with 0 overwrites), 0 insert failures)
Searching at depth 2... d2 [+0.00]: 1.e2e4 g8f6 
    (21 new nodes, 41 new qnodes, 0 qnode aborts, 0ms, 132kN/s)
    (ttable: 26/27777778 = 0.00% load, 0 hits, 0 misses, 25 inserts (with 0 overwrites), 0 insert failures)
Searching at depth 3... d3 [+0.30]: 1.d2d4 g8f6 2.c1f4 
    (118 new nodes, 247 new qnodes, 0 qnode aborts, 5ms, 73kN/s)
    (ttable: 187/27777778 = 0.00% load, 0 hits, 0 misses, 161 inserts (with 0 overwrites), 0 insert failures)
Searching at depth 4... d4 [+0.00]: 1.e2e4 g8f6 2.f1d3 b8c6 
    (1519 new nodes, 3044 new qnodes, 0 qnode aborts, 38ms, 119kN/s)
    (ttable: 2622/27777778 = 0.01% load, 0 hits, 0 misses, 2435 inserts (with 0 overwrites), 1 insert failures)
Searching at depth 5... d5 [+0.10]: 1.g2g3 g8f6 2.f1g2 b8c6 3.g2f3 
    (10895 new nodes, 35137 new qnodes, 0 qnode aborts, 251ms, 184kN/s)
    (ttable: 30441/27777778 = 0.11% load, 0 hits, 0 misses, 27819 inserts (with 0 overwrites), 0 insert failures)
Searching at depth 6... d6 [-0.08]: 1.d2d4 g8f6 2.c1g5 b8c6 3.g5f6 g7f6 
    (88027 new nodes, 249718 new qnodes, 0 qnode aborts, 1281ms, 264kN/s)
    (ttable: 252536/27777778 = 0.91% load, 0 hits, 0 misses, 222095 inserts (with 0 overwrites), 27 insert failures)
Searching at depth 7... d7 [+0.15]: 1.e2e4 g8f6 2.d2d4 b8c6 3.d4d5 c6b4 4.g1f3 
    (417896 new nodes, 1966379 new qnodes, 0 qnode aborts, 8485ms, 281kN/s)
    (ttable: 1957490/27777778 = 7.05% load, 0 hits, 0 misses, 1704954 inserts (with 0 overwrites), 817 insert failures)
Calculating...
Iterative Deepening Analysis Results (including cached analysis)
Searching at depth 1... d1 [+2.25]: 3...g8h6 4.(q)c3d5 (q)d8d5 
    (1 new nodes, 3 new qnodes, 0 qnode aborts, 0ms, 23kN/s)
    (ttable: 3/27777778 = 0.00% load, 0 hits, 0 misses, 3 inserts (with 0 overwrites), 0 insert failures)
Searching at depth 2... d2 [-0.13]: 3...f5e4 4.c3e4 (q)d5e4 
    (32 new nodes, 443 new qnodes, 0 qnode aborts, 3ms, 144kN/s)
    (ttable: 369/27777778 = 0.00% load, 0 hits, 0 misses, 366 inserts (with 0 overwrites), 0 insert failures)
Searching at depth 3... d3 [+0.25]: 3...g8h6 4.c3e2 h6g4 
    (230 new nodes, 2664 new qnodes, 0 qnode aborts, 24ms, 122kN/s)
    (ttable: 2526/27777778 = 0.01% load, 0 hits, 0 misses, 2157 inserts (with 0 overwrites), 0 insert failures)
Searching at depth 4... d4 [-0.10]: 3...g8f6 4.e3e4 f5e6 5.f1b5 
    (2084 new nodes, 13998 new qnodes, 0 qnode aborts, 100ms, 162kN/s)
    (ttable: 15663/27777778 = 0.06% load, 0 hits, 0 misses, 13137 inserts (with 0 overwrites), 2 insert failures)
Searching at depth 5... d5 [+0.15]: 3...g8f6 4.f1e2 h8g8 5.g2g4 f5e4 6.(q)c3e4 (q)f6e4 
   (38987 new nodes, 1004867 new qnodes, 0 qnode aborts, 2765ms, 378kN/s)
   (ttable: 855045/27777778 = 3.08% load, 0 hits, 0 misses, 839382 inserts (with 0 overwrites), 302 insert failures)