在位板上找到对角线的计算可行方法

时间:2019-05-04 11:09:29

标签: bitmap bit-manipulation bit-shift bitboard

我正在努力寻找一种有效的方法来计算正方形的对角线的64位表示形式。

有问题的游戏是一个叫做Othello或Reversi的棋盘游戏。我目前正在研究一种确定一块稳定性的方法。稳定的片段可以定义为不能翻转的片段。我正在处理的当前问题可以定义为:“如果在所有四个方向上的所有正方形都被占用,一块就无法翻转。”

例如,如果板子如下,则无法捕获标有A的棋子:

/*      1 2 3 4 5 6 7 8 
 *   1  . . x . . . x . 
 *   2  . . x . . x . . 
 *   3  x . x . x . . . 
 *   4  . x x x . . . . 
 *   5  x x A x x x x x 
 *   6  . x x x . . . . 
 *   7  x . x . x . . . 
 *   8  . . x . . x . . 
 */

其中x表示在该渗碳中存在一块(无论其颜色如何)。由于棋子在各个方向上都被棋子包围,因此无法捕获。

垂直线很容易计算。在嵌套的8x8 for循环中,可以通过ver0 >> j找到下一条垂直线,而可以通过hor0 >> i * 8找到下一条水平线。

const unsigned long long hor0 = 255ULL;
const unsigned long long ver0 = 72340172838076673ULL;
for (i = 0; i < 8; i++) {
    for (j = 0; j < 8; j++) {
    currHor = hor0 << i;
    currVer = ver0 << j;
    }
}

作为一个直观示例,hor0看起来像:

/*      1 2 3 4 5 6 7 8 
 *   1  x x x x x x x x 
 *   2  . . . . . . . . 
 *   3  . . . . . . . . 
 *   4  . . . . . . . . 
 *   5  . . . . . . . . 
 *   6  . . . . . . . . 
 *   7  . . . . . . . . 
 *   8  . . . . . . . . 
 */

所以移动8会把线向下移动一。

ver0看起来像:

/*      1 2 3 4 5 6 7 8 
 *   1  x . . . . . . . 
 *   2  x . . . . . . . 
 *   3  x . . . . . . . 
 *   4  x . . . . . . . 
 *   5  x . . . . . . . 
 *   6  x . . . . . . . 
 *   7  x . . . . . . . 
 *   8  x . . . . . . . 
 */

因此,将档位移一将使行右移一。

要找到它们的组合光标,我只需对结果进行“或”操作:

currCursor = (currHor | currVer);

现在真正的问题开始了。为了确定稳定性,我需要所有四个方向。我不确定如何仅用(i,j)位置来计算对角线。

我的第一个尝试是使用移位。当我不想要垃圾位时,这种转变导致镜像,这可怕地失败了。

我的第二次尝试是将所有对角线简单地放置在数组中,并使用索引查找对应的线。数组很大,但这是一些元素外观的示例(仅左对角线):

const unsigned long long rightDiagonals[15] = { \
    9241421688590303745ULL, \
/*      1 2 3 4 5 6 7 8 
 *   1  x . . . . . . . 
 *   2  . x . . . . . . 
 *   3  . . x . . . . . 
 *   4  . . . x . . . . 
 *   5  . . . . x . . . 
 *   6  . . . . . x . . 
 *   7  . . . . . . x . 
 *   8  . . . . . . . x
 *   [0] //This is the index in the array.
 */
    36099303471055874ULL, \
/*      1 2 3 4 5 6 7 8 
 *   1  . x . . . . . . 
 *   2  . . x . . . . . 
 *   3  . . . x . . . . 
 *   4  . . . . x . . . 
 *   5  . . . . . x . . 
 *   6  . . . . . . x . 
 *   7  . . . . . . . x 
 *   8  . . . . . . . . 
 *   [1] 
 */
...
    144396663052566528ULL, \
/*      1 2 3 4 5 6 7 8 
 *   1  . . . . . . . . 
 *   2  . . . . . . . . 
 *   3  . . . . . . . . 
 *   4  . . . . . . . . 
 *   5  . . . . . . . . 
 *   6  . . . . . . . . 
 *   7  x . . . . . . . 
 *   8  . x . . . . . . 
 *   [13] 
 */
    72057594037927936ULL}
/*      1 2 3 4 5 6 7 8 
 *   1  . . . . . . . . 
 *   2  . . . . . . . . 
 *   3  . . . . . . . . 
 *   4  . . . . . . . . 
 *   5  . . . . . . . . 
 *   6  . . . . . . . . 
 *   7  . . . . . . . . 
 *   8  x . . . . . . . 
 *   [14] 
 */

我不知道如何将数组的索引与(i,j)索引匹配。例如,如果在索引(2,1)上有一个正方形,则数组中的对应索引应为[1]。但是,如果正方形在(3,2)上,则数组的索引也应为[1]。并且如果正方形在(1,1),...(7,7),(8,8)上,则索引应为[0]。

我无法确定轻松找到这条线的方法。我想到的一件事是获取当前正方形的64位表示形式,并将其与两条线的交点进行或运算。问题在于这将需要一个for循环,并且此操作将执行数千次,这在计算上不是最佳选择。

有人知道从单个正方形计算对角线的方法还是计算对角线数组中相应索引的方法吗?

非常感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

  

我不知道如何将数组的索引与(i,j)索引匹配。例如,如果在索引(2,1)上有一个正方形,则数组中的对应索引应为[1]。但是,如果正方形在(3,2)上,则数组的索引也应为[1]。如果在(1,1),...(7,7),(8,8)上有一个正方形,则索引应为[0]。

在您提供的对中有一个简单的图案,可能更容易发现几何图案。看看下面的网格,看看是否能在继续阅读之前弄清楚,尝试考虑一下需要(如何使线上的每个x,y对产生相同的数字):

    0 1 2 3 4 5 6 7

0   . . x . . . . .
1   . . . x . . . .
2   . . . . x . . .
3   . . . . . x . .
4   . . . . . . x .
5   . . . . . . . x
6   . . . . . . . .
7   . . . . . . . .

如果从左边缘到对角线再到底部边缘(曼哈顿距离)画一条线,您会注意到该线上的所有点都具有相同的距离7 - x + y。同样,我们可以对距“主要对角线”的距离进行x - y运算。下面说明了所有要点:

    0 1 2 3 4 5 6 7

0   0 1 2 3 4 5 6 7
1  -1 0 1 2 3 4 5 6
2  -2-1 0 1 2 3 4 5
3  -3-2-1 0 1 2 3 4
4  -4-3-2-1 0 1 2 3
5  -5-4-3-2-1 0 1 2
6  -6-5-4-3-2-1 0 1
7  -7-6-5-4-3-2-1 0

这时您可以将16个预先计算的对角线映射到x - y ...但是,我们可以做得更好。您最初的移动对角线的想法很好,但是需要花一些精力来弄清楚如何有效地避免多余的位绕在网格上。

要注意的关键是,在网格上向右移动向右等效于向上移动向左移动,然后向左移动向左移动等效于将其向下向下移动(假设位只是从网格上掉下来)。当然,当我们实际向左或向右移动时,会得到一些环绕,但是当我们向上或向下移动 (使用8的倍数,向左/向右)时,多余的位总是被拖到单词的结尾。次要对角线也是如此,但具有相反的等效性。

如果我们从“主要对角线”(左上到右下)和“第二对角线”(右上到左下)开始,则可以使用x - y上下移动它们产生对角线的所有其他组合,然后像对待正交线一样对它们进行或运算:

const uint64_t
    HP = 0xff00000000000000, // horizontal primary
    VP = 0x8080808080808080, // vertical primary
    DP = 0x8040201008040201, // diagonal primary
    DS = 0x0102040810204080; // diagonal secondary

uint64_t stable (int x, int y) {
    uint64_t m =
        VP >> x | HP >> (y << 3);
    if (x >= y) {
        m |= DP << ((x - y) << 3);
    } else {
        m |= DP >> ((y - x) << 3);
    }
    int z = 7 - x;
    if (z >= y) {
        m |= DS << ((z - y) << 3);
    } else {
        m |= DS >> ((y - z) << 3);
    }
    return m;
}

void main () {
    for (int y = 0; y < 8; y ++) {
        for (int x = 0; x < 8; x ++) {
            uint64_t m = stable(x, y);
            printf("\n%d,%d:\n", x, y);
            for (int i = 7; i >= 0; i --) {
                int line = m >> (i << 3);
                printf("%c %c %c %c %c %c %c %c\n",
                    line & 0x80 ? 'x' : '.',
                    line & 0x40 ? 'x' : '.',
                    line & 0x20 ? 'x' : '.',
                    line & 0x10 ? 'x' : '.',
                    line & 0x08 ? 'x' : '.',
                    line & 0x04 ? 'x' : '.',
                    line & 0x02 ? 'x' : '.',
                    line & 0x01 ? 'x' : '.'
                );
            }
        }
    }
}

所有<< 3只是为了提高效率,它等效于* 8

我猜您会想用m来掩盖您的董事会价值,看它是否像board & m == m那样“稳定”?

好玩的小问题,谢谢:)