当前位置: 首页 > 知识库问答 >
问题:

如何为魔术位板生成这个预初始化数组?

呼延衡
2023-03-14

我目前正在尝试使我的国际象棋引擎更快,并正在考虑为我的滑块攻击生成实现魔术位板。我使用棋盘的位板表示,a1方格是最右边的位,h8是最左边的位。我正在查看这个网站:

https://rhysre.net/fast-chess-move-generation-with-magic-bitboards.html#:~:text=位板就是一个,位63=h8)

特别是在页面底部找到的代码片段,内容如下:

  U64 getBishopAttacksMagic(int square, U64 blockers) {
  // Mask blockers to only include bits on diagonals
  blockers &= BISHOP_MASKS[square];

  // Generate the key using a multiplication and right shift
  U64 key = (blockers * BISHOP_MAGICS[square]) >> (64 - BISHOP_INDEX_BITS[square]);

  // Return the preinitialized attack set bitboard from the table
  return BISHOP_TABLE[square][key];
}

我已经有了浅蓝色魔法数字(每个数字对应一个正方形),并且我已经有了存储在64长度数组中的毕晓普作品的预初始化攻击掩码(同样,每个数字对应一个正方形)。所以我知道怎么拿到钥匙。但我如何生成最后一个接受密钥的数组,“BISHOP_TABLE”数组呢?我不知道如何在给定攻击面具和每个方块的魔法数的情况下生成2d阵列。提前感谢您的帮助。

共有1个答案

梁浩涆
2023-03-14

对于每一个方块,你需要在bishop掩模中生成每一个块的排列。例如,将此遮罩用于方形e4(#28):

8 | 0 0 0 0 0 0 0 0
7 | 0 1 0 0 0 0 0 0
6 | 0 0 1 0 0 0 1 0
5 | 0 0 0 1 0 1 0 0
4 | 0 0 0 0 0 0 0 0
3 | 0 0 0 1 0 1 0 0
2 | 0 0 1 0 0 0 1 0
1 | 0 0 0 0 0 0 0 0
    ---------------
    a b c d e f g h

由于有9个集合位,因此有2^9=512个不同的阻塞块模式。排列号339(作为二进制=0b101010011)如下所示:

8 | 0 0 0 0 0 0 0 0
7 | 0 1 0 0 0 0 0 0
6 | 0 0 1 0 0 0 0 0
5 | 0 0 0 1 0 0 0 0
4 | 0 0 0 0 0 0 0 0
3 | 0 0 0 0 0 0 0 0
2 | 0 0 1 0 0 0 1 0
1 | 0 0 0 0 0 0 0 0
    ---------------
    a b c d e f g h

位在数字中从右(lsb)到左(msb)读取,并在掩码中从a文件设置为h文件,从第1位到第8位。置换0是一个空板,511(0b111111111)是完整掩码。

下面是一个方法,它使用一个置换数和bishop掩码,并返回相应的阻止程序位板:

private static long blockersPermutation(int iteration, long mask) {
    long blockers = 0;

    while (iteration != 0) {
        if ((iteration & 1) != 0) {
            int shift = Long.numberOfTrailingZeros(mask);
            blockers |= (1L << shift);
        }

        iteration >>>= 1;
        mask &= (mask - 1); // used in Kernighan's bit count algorithm
        // it pops out the least significant bit in the number
    }

    return blockers;
}

使用这个,我们可以计算出带有魔法数字和拦截器的密钥,我们可以创建它们相应的值,即攻击掩码。

对于块的每个排列,创建相应的攻击掩码并将其存储在表中。包括遮罩板侧面的挡块和方块。阻挡者#339 on square#28的攻击面具是:

8 | 0 0 0 0 0 0 0 0
7 | 0 0 0 0 0 0 0 1
6 | 0 0 0 0 0 0 1 0
5 | 0 0 0 1 0 1 0 0
4 | 0 0 0 0 0 0 0 0
3 | 0 0 0 1 0 1 0 0
2 | 0 0 1 0 0 0 1 0
1 | 0 0 0 0 0 0 0 0
    ---------------
    a b c d e f g h

下面是初始化64个bishop查找表的Java方法:

private final long[][] BISHOP_LOOKUP = new long[64][512];

private static int getFile(int square) {
    return square % 8;
}

private static int getRank(int square) {
    return square / 8;
}

private static int getSquare(int rank, int file) {
    return rank * 8 + file;
}

// just like the code snippet, generates the key
private static int transform (long blockers, long magic, int shift) {
    return (int) ((blockers * magic) >>> (64 - shift));
}

private void initBishopLookup() {
    for (int square = 0; square < 64; square++) {
        long mask = BISHOP_MASKS[square];
        int permutationCount = (1 << Long.bitCount(mask));

        for (int i = 0; i < permutationCount; i++) {
            long blockers = blockersPermutation(i, mask);
            long attacks = 0L;
            int rank = getRank(square), r;
            int file = getFile(square), f;

            for (r = rank + 1, f = file + 1; r <= 7 && f <= 7; r++, f++) {
                attacks |= (1L << getSquare(r, f));
                if ((blockers & (1L << getSquare(r, f))) != 0) {
                    break;
                }
            }

            for (r = rank - 1, f = file + 1; r >= 0 && f <= 7; r--, f++) {
                attacks |= (1L << getSquare(r, f));
                if ((blockers & (1L << getSquare(r, f))) != 0) {
                    break;
                }
            }

            for (r = rank - 1, f = file - 1; r >= 0 && f >= 0; r--, f--) {
                attacks |= (1L << getSquare(r, f));
                if ((blockers & (1L << getSquare(r, f))) != 0) {
                    break;
                }
            }

            for (r = rank + 1, f = file - 1; r <= 7 && f >= 0; r++, f--) {
                attacks |= (1L << getSquare(r, f));
                if ((blockers & (1L << getSquare(r, f))) != 0) {
                    break;
                }
            }

            int key = transform(blockers, BISHOP_MAGICS[square], Long.bitCount(mask));

            BISHOP_LOOKUP[square][key] = attacks;
        }
    }
}

这使用了带有固定大小查找表的普通魔术位板,当设置位较少的掩码可以容纳较少的空间时,所有长度都为512。与正方形b1一样,遮罩在对角线上使用5位,表格可以放入长度为2^5=32的数组中。我们在浪费(512-32)*(每64位数字8字节)/1024字节/Kio=3.75Kio的空间。普通魔法比特板为Rook占用2MB内存,为Bishop占用256Kib内存,使用奇特的魔法比特板可以将总内存减少到~800Kib。但实际上并不需要,2.25Mib的内存很小。

 类似资料:
  • 本质上,我想要一个具有数组的模板类,其大小是一个模板参数,以保存常量内容。 类似于: 我一直在搜索和修补一点,几乎有一个解决方法实现了一个中间静态方法,并使用std::array: ...这已经是相当多的样板,但仍然d::array似乎不是从初始化列表中构建的?:-(

  • 在下面的示例中,我需要初始化A::A(H H)构造函数初始值设定项列表中的std::array(因为类H没有默认的构造函数),但我不能使用初始值设定项列表,因为数组大小是一个模板参数。 有办法解决这个问题吗?

  • 问题内容: 我的应用程序使用TreeMap保持数据排序并进行log(n)查找和插入。在一般情况下,当应用程序运行时,这种方法效果很好,但是当应用程序首次启动时,我需要使用以 排序顺序 (升序)得到的数百万个长度来初始化TreeMap 。 由于这些初始化值 已经 排序,是否有任何方法可以将它们插入到TreeMap中,而无需支付树插入和重新平衡的log(n)成本? 问题答案: 当然!该方法(以及采用S

  • 问题内容: 我只是看了这个SO Post: 但是,哥伦比亚大学教授的笔记按以下方式进行。请参阅第9页。 哪种方法正确?他们似乎在说不同的话。 特别是在注释版本中没有。 问题答案: 这根本不会在Java中编译(因为您正在将数组类型的值分配给非数组类型的变量): 被以下错误拒绝(另请参见:http : //ideone.com/0jh9YE): 要进行编译,请声明其类型,然后在其上循环:

  • 根据此堆栈溢出问题的公认(且唯一)答案, 使用 将改为零初始化对象。 那么,为什么呢?, 生成此输出: 定义的两个构造函数都是默认的?正当对于POD类型,默认初始化为零初始化。 根据这个问题的公认答案, 如果POD成员未在构造函数中初始化,也未在类初始化中通过C11初始化,则默认为已初始化。 不管是堆栈还是堆,答案都是一样的。 在C 98中(而不是之后),new int()被指定为执行零初始化。

  • 问题内容: 我通过以下方式初始化了SunPKCS11提供程序: 然后,我使用此提供程序初始化KeyStore,以将密钥用于密码操作。 密码操作完成后, 如何使用PKCS11令牌完成会话? 我曾尝试删除该提供程序,但没有成功。 下次我尝试与令牌通信时,我从令牌 CKR_CRYPTOKI_ALREADY_INITIALIZED* 抛出此异常 * 更新 : 我努力了 但它也不起作用。 我有一个用例,其中