识别带有位板的棋子

时间:2015-07-29 07:01:53

标签: c++ c++11 if-statement chess bitboard

当棋盘存放在各种位板中时,现代国际象棋引擎如何识别特定单元格上的哪种类型/侧面棋子?我遇到了这方面的问题,因为要找出特定位的类型/侧面部分,我必须始终这样做:

if((bit_check & occupied) == 0ULL) ... // empty
else if((bit_check & white) != 0ULL) // white
    if((bit_check & white_pawns) != 0ULL) ... // white pawns
    else if((bit_check & white_rooks) != 0ULL) ... // white rooks
    ....
    else if((bit_check & white_kings) != 0ULL) ... // white kings
else if((bit_check & black) != 0ULL) // black
    if((bit_check & black_pawns) != 0ULL) ... // black pawns
    ....
    else if((bit_check) & black_kings) != 0ULL) ... // black kings

这是一个非常繁琐的过程,必须完成很多次(例如,在移动生成过程中看到正在捕获的内容)。我不确定我是否应该这样做,或者仅仅创建一个类型为Piece[64]的64数组会更快,这将固有地存储该类型。

对于搜索功能中的捕获分析,考虑到它必须达到数百万倍才会更好。我做错了吗?

2 个答案:

答案 0 :(得分:2)

位检查本身很快;我主要担心的是分支。

相反,请将uint64_t bitboards[12]视为所有部分的12个位板的数组。现在它在内存中是连续的,可以循环扫描:

for (int i = 0; i != 12; ++i)
{
  if (bitboards[i] && bit_check) return i;
}
return -1; // empty.

分支预测器只有两个分支(循环和检查)更容易,连续内存优化预取器。

明显的变化是仅针对白色部件检查位板[0]至[5],仅针对黑色部件检查[6]至[11]。

更微妙的变体:

uint64_t bitboards[13];
bitboards[12] = ~uint64_t(0);
for (int i = 0; /* NO TEST*/ ; ++i)
{
     if (bitboards[i] && bit_check) return i;
}

而不是返回-1为空,这将返回12(sentinel值)。但是,这将使用更快的无条件分支替换条件循环分支。这也意味着返回值始终为int i

另一个不相关的优化是识别棋子是最常见的棋子,因此对白棋子使用bitboards[0]以及对黑棋子使用bitboards[1]bitboards[6]效率更高,具体取决于是否交错黑色或白色碎片。

[编辑] 如果color有一个单独的位板,则不需要两个用于白色棋子和黑色棋子的位板。相反,为棋子设置一个位板。检查黑色棋子,和两个值。 (bit_check & color & bitboard[0])。要检查白色棋子,请反转颜色(bit_check & ~color & bitboard[0]

答案 1 :(得分:1)

这是比特板的最慢操作。但是,您很少需要执行它。

我看到你正在保持一个按位'或者'所有白色碎片white和一个或多个黑色碎片black。使用它们,您可以快速拒绝移动到您自己的棋子上并轻松检测捕获。

在不太可能的捕获事件中,你必须测试6个敌人棋盘中最多5个,因为国王捕获应该已被排除。而且,这并不像你想象的那么乏味;在64位系统上,每个掩码每个位板只有1个操作然后进行比较,所以10个整数运算。 And / Or是处理器上最轻微的操作。单独维护Piece[64]比花费更多时间。

我相信没有其他情况(在引擎代码中)您需要从给定方块获取一个pieceID。

位板的主要优点是移动生成和位置分析。没有什么可比的,所以无论如何你都要保持这种结构。