您是正确的,仅使用位运算无法确定滑动棋子的有效移动。您需要使用位运算和预先计算的查找表。
国际象棋案例
大多数最新的国际象棋引擎都使用称为Magic Bitboards的技术。
实现方式各不相同,但基本原理始终相同:
- 从给定的位置中找到一个棋子可能到达的方块,不考虑棋盘上的占用情况。这将为我们提供一个潜在目标方块的64位掩码。我们称其为“T”(目标)。
- 对“T”执行按位与操作,与棋盘上占用方块的掩码进行比较。我们称后者为“O”(占用)。
- 将结果乘以一个“魔术”值“M”,并将结果向右移动一个“魔术”数量“S”。这将给我们带来一个“I”(索引)。
- 使用“I”作为查找表中的索引,检索可以实际到达此配置的方块的掩码。
总之:
I = ((T & O) * M) >> S
reachable_squares = lookup[I]
T、M、S和lookup都是预先计算的,取决于棋子的位置(P=0...63)。因此,更准确的公式应该是:
I = ((T[P] & O) * M[P]) >> S[P]
reachable_squares = lookup[P][I]
第三步的目的是将64位值
T&O
转换为一个更小的值,以便可以使用大小合理的表格。通过计算
((T&O)* M)>> S
得到的实质上是随机比特序列,我们想将每个序列映射到可达目标方块的唯一位掩码。
该算法中的“神奇”部分是确定产生尽可能小的无冲突查找表的
M和
S值。正如Bo Persson在评论中指出的那样,这是一个
完美哈希函数问题。然而,到目前为止还没有发现适用于魔术位板的完美哈希,这意味着通常使用的查找表包含许多未使用的“空洞”。大多数情况下,它们是通过运行广泛的暴力搜索来构建的。
你的测试案例如下:
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ │ │ ▲ │ │ │ ● │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┘
7 6 5 4 3 2 1 0
在这里,棋子的位置在[0 ... 7]
,占用位掩码在[0x00 ... 0xFF]
(因为它是8位宽)。
因此,在不应用“魔法”部分的情况下,基于位置和当前棋盘构建直接查找表是完全可行的。
我们将得到:
reachable_squares = lookup[P][board]
这将导致一个包含查找表的结果:
8 * 2^8 = 2048 entries
显然,对于国际象棋这样的游戏,我们无法这样做,因为它将包含:
64 * 2^64 = 1,180,591,620,717,411,303,424 entries
因此需要使用魔术乘法和位移操作以更紧凑的方式存储数据。
以下是一个JS片段来说明这种方法。单击棋盘以切换敌方棋子。
var xPos = 5,
board = 1 << xPos,
lookup = [];
function buildLookup() {
var i, pos, msk;
for(pos = 0; pos < 8; pos++) {
for(lookup[pos] = [], msk = 0; msk < 0x100; msk++) {
lookup[pos][msk] = 0;
for(i = pos + 1; i < 8 && !(msk & (1 << i)); i++) {
lookup[pos][msk] |= 1 << i;
}
for(i = pos - 1; i >= 0 && !(msk & (1 << i)); i--) {
lookup[pos][msk] |= 1 << i;
}
}
}
}
function update() {
var target = lookup[xPos][board];
for(var n = 0; n < 8; n++) {
if(n != xPos) {
$('td').eq(7 - n)
.html(board & (1 << n) ? 'O' : '')
.toggleClass('reachable', !!(target & (1 << n)));
}
}
}
$('td').eq(7 - xPos).html('X');
$('td').click(function() {
var n = 7 - $('td').index($(this));
n != xPos && (board ^= 1 << n);
update();
});
buildLookup();
update();
td { width:16px;border:1px solid #777;text-align:center;cursor:pointer }
.reachable { background-color:#8f8 }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
</table>