如何找到魔术位板?

paulwal222
const int BitTable[64] = {
  63, 30, 3, 32, 25, 41, 22, 33, 15, 50, 42, 13, 11, 53, 19, 34, 61, 29, 2,
  51, 21, 43, 45, 10, 18, 47, 1, 54, 9, 57, 0, 35, 62, 31, 40, 4, 49, 5, 52,
  26, 60, 6, 23, 44, 46, 27, 56, 16, 7, 39, 48, 24, 59, 14, 12, 55, 38, 28,
  58, 20, 37, 17, 36, 8
};

int pop_1st_bit(uint64 *bb) {
  uint64 b = *bb ^ (*bb - 1);
  unsigned int fold = (unsigned) ((b & 0xffffffff) ^ (b >> 32));
  *bb &= (*bb - 1);
  return BitTable[(fold * 0x783a9b23) >> 26];
}

uint64 index_to_uint64(int index, int bits, uint64 m) {
  int i, j;
  uint64 result = 0ULL;
  for(i = 0; i < bits; i++) {
    j = pop_1st_bit(&m);
    if(index & (1 << i)) result |= (1ULL << j);
  }
  return result;
}

它来自国际象棋编程维基:https
//www.chessprogramming.org/Looking_for_Magics

这是一些用于查找幻数的代码的一部分

该参数uint64 m是一个位板,表示针对菜鸟或主教移动的可能被遮挡的正方形。e4广场上的一个车的示例:

0 0 0 0 0 0 0 0 
0 0 0 0 1 0 0 0 
0 0 0 0 1 0 0 0 
0 0 0 0 1 0 0 0 
0 1 1 1 0 1 1 0 
0 0 0 0 1 0 0 0 
0 0 0 0 1 0 0 0 
0 0 0 0 0 0 0 0 

边缘正方形为零,因为它们总是阻塞,因此减少所需的位数显然是有帮助的。

/* Bitboard, LSB to MSB, a1 through h8:
 * 56 - - - - - - 63
 *  - - - - - - - -
 *  - - - - - - - -
 *  - - - - - - - -
 *  - - - - - - - -
 *  - - - - - - - -
 *  - - - - - - - -
 *  0 - - - - - - 7
 */

因此,在上面的示例中,index_to_uint64采用一个索引(0到2 ^ bits),在位板(10)和位板中设置的位数。

然后,pops_1st_bit对于每个位数,其后是另一个移位的代码位。pops_1st_bit对位板本身进行XOR减一(为什么?)。然后将其与一个完整的32位进行“与”运算,然后我的大脑在内存不足的地方耗尽。不知何故涉及到了神奇的十六进制数字0x783a9b23(这是Lost的数字序列吗?)。还有一个荒谬的神秘数组,其中包含0-63(BitTable[64])之间随机排列的数字

paulwal222

好吧,我知道了。

首先,一些术语:

阻止器遮罩:包含给定棋子类型和该棋子所在正方形的所有方格的位板。它不包括终止边缘方块,因为它们总是会阻塞。

封闭板:包含占用正方形的位它仅具有正方形,也位于阻止程序蒙版中。

移动板:包含一块所有正方形的位板(给定一块类型),一块正方形和一块封闭板。如果工件可以移动到那里,包括终止边缘正方形。

一个在e4正方形上的白嘴鸦的示例,在e2,e5,e7,b4和c4上有一些随机块。

 The blocker mask        A blocker board         The move board
 0 0 0 0 0 0 0 0         0 0 0 0 0 0 0 0         0 0 0 0 0 0 0 0 
 0 0 0 0 1 0 0 0         0 0 0 0 1 0 0 0         0 0 0 0 0 0 0 0 
 0 0 0 0 1 0 0 0         0 0 0 0 0 0 0 0         0 0 0 0 0 0 0 0 
 0 0 0 0 1 0 0 0         0 0 0 0 1 0 0 0         0 0 0 0 1 0 0 0 
 0 1 1 1 0 1 1 0         0 1 1 0 0 0 0 0         0 0 1 1 0 1 1 1 
 0 0 0 0 1 0 0 0         0 0 0 0 0 0 0 0         0 0 0 0 1 0 0 0 
 0 0 0 0 1 0 0 0         0 0 0 0 1 0 0 0         0 0 0 0 1 0 0 0 
 0 0 0 0 0 0 0 0         0 0 0 0 0 0 0 0         0 0 0 0 0 0 0 0 

注意事项:

  • 对于给定的正方形和小块类型(rook或bishop),遮罩始终是相同的。
  • 阻挡器板包括友军和敌方物品,它是阻挡器面具的子集。
  • 最终的移动板可能包括捕获您自己的棋子的移动,但是这些移动之后很容易删除: moveboard &= ~friendly_pieces)

该目标幻数的方法是非常快速查找预先计算的移动板对于给定的拦截板否则,您每次都必须(缓慢地)计算移动板。这仅适用于滑动件,即车子和主教。女王只是新秀和主教的结合。

可以为每个方块和棋子组合找到魔术数字。为此,您必须为每个平方/块组合计算每种可能的隔板板变化。这就是有问题的代码在做什么。怎么它做它仍然是一个谜给我,但也似乎是明显的原作者,马特·泰勒的情况(感谢@Pradhan的链接)

因此,我所做的就是重新实现了用于生成所有可能的插板变化的代码。它使用了不同的技术,虽然速度稍慢一些,但阅读和理解起来却容易得多。它稍微慢一点的事实不是问题,因为此代码对速度没有严格要求。该程序仅需在程序启动时执行一次,并且在双核i5上只需花费几微秒的时间。

/* Generate a unique blocker board, given an index (0..2^bits) and the blocker mask 
 * for the piece/square. Each index will give a unique blocker board. */
static uint64_t gen_blockerboard (int index, uint64_t blockermask) 
{
    /* Start with a blockerboard identical to the mask. */
    uint64_t blockerboard = blockermask;

    /* Loop through the blockermask to find the indices of all set bits. */
    int8_t bitindex = 0;
    for (int8_t i=0; i<64; i++) {
        /* Check if the i'th bit is set in the mask (and thus a potential blocker). */
        if ( blockermask & (1ULL<<i) ) {
            /* Clear the i'th bit in the blockerboard if it's clear in the index at bitindex. */
            if ( !(index & (1<<bitindex)) ) {
                blockerboard &= ~(1ULL<<i); //Clear the bit.
            }
            /* Increment the bit index in the 0-4096 index, so each bit in index will correspond 
             * to each set bit in blockermask. */
            bitindex++;
        }
    }
    return blockerboard;
}

要使用它,请执行以下操作:

int bits = count_bits( RookBlockermask[square] );
/* Generate all (2^bits) blocker boards. */ 
for (int i=0; i < (1<<bits); i++) {
    RookBlockerboard[square][i] = gen_blockerboard( i, RookBlockermask[square] );
}

工作原理:有2位的阻塞器板,其中bits阻塞器掩码中的1是唯一相关的位。同样,每个从0到2 ^ bits的整数都有一个长度为1和0的唯一序列bits因此,此功能仅将给定整数中的每个位与阻止程序掩码中的相关位相对应,并相应地将其关闭/打开以生成唯一的阻止程序板。

它不那么聪明或快速,但可读性强。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章