在使用位板的国际象棋引擎中,边缘是如何检测的



例如,所有白色棋子的攻击都是通过向左移动7或9位来生成的(或者向右移动,我可能错了,但我认为很容易理解要点(。

看起来像的白色典当比特板

00000000000000000000000000000000000000001111111100000000

会被转移到这个

00000000000000000000000000000001111111100000000000000000

然而,如果你试图用8x8阵列来描绘这个例子,其中一个棋子会穿过棋盘的边缘。

那么,当比特被转移以产生攻击时,引擎如何防止自己产生穿过棋盘边缘的攻击呢?

我们将添加一些空白。假设你从一个典当位置开始:

00000000
00000000
00000000
00000000
00000000
10000000
01100101
00011010
00000000
00000000

换行符只是为了便于阅读,所有的东西都是打包的。典当行在哪里?

00000000
00000000
00000000
00000000
10000000
01100101
00011010
00000000
00000000
00000000

在这里,越过边缘的唯一风险是在顶部,这很容易处理。

接下来,这些小卒能带到哪里?天真的解决方案是;移动";掩码,并将其左右移动1。正如你所注意到的,这会导致缠绕。

00000000
00000000
00000000
00000001
00000000
11001010
00110100
00000000
00000000
00000000
|
00000000
00000000
00000000
00000000
01000000
00110010
10001101
00000000
00000000
00000000

(如果你不喜欢我选择的endianness,请道歉(。

我们可以用一些口罩来解决这个问题。将缠绕的钻头位于已知位置。所以我们在做手术之前把他们戴上口罩。

此掩码:

11111110
11111110
11111110
11111110
11111110
11111110
11111110
11111110

在我们向右移动之前,

01111111
01111111
01111111
01111111
01111111
01111111
01111111
01111111

在我们向左移动之前。

因此,看到那些受到典当威胁的地方;向前";是一个正确的转变,我们做到了:

auto pawn_moves = pawn_mask >> 8;
auto pawn_left_take = (pawn_moves & not_left_column) << 1;
auto pawn_right_take = (pawn_moves & not_right_column) << 1;
auto pawn_take = pawn_left_take | pawn_right_take;

我们有了它。

在其他情况下也可以进行类似的掩蔽。例如,如果我们计算骑士的面具,我们需要8个面具和8个位移,然后或者全部加在一起。

对于向右移动3的车,你屏蔽掉右边的3列,然后在车屏蔽上向右移动3。

然而,在某些情况下,您可能希望使用内部函数和SIMD处理;因此,快速的钻头操作将取决于你的手。也许将8个8位值拆包为8个24位值,在那里进行无掩码操作,然后提取中间的8位是特定硬件上最快的方法。

最新更新