例如,所有白色棋子的攻击都是通过向左移动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位是特定硬件上最快的方法。