11

这是一个关于如何使用魔术位板验证国际象棋中的滑动棋子移动的全局问题。澄清一下,我不是在问魔术位板是如何在内部工作的

现在,关于这个问题的更多细节。我正在使用位板编写棋盘表示,并且我想使用魔术位板来验证滑动块的移动。有人可以列出如何实现这一目标的主要步骤吗?例如,考虑以下董事会职位:

白动。 验证 g3 上车的给定移动

假设我们已经初始化并准备好使用所有神奇的位板功能和数据结构。因此,仅使用魔术位板的函数签名,您能否列出步骤(伪代码或任何语言)来验证 g3 上白车的给定动作?

4

3 回答 3

36

简而言之,魔术位板是一种有效的方式来获取位置并获得滑动件的合法移动。

首先,你需要找到一些神奇的数字。当您使用幻数时,您编写的一些用于查找幻数的代码也将被重用。

首先,您需要编写 5 个函数。这些不需要特别快,因为您只会在查找幻数时使用它们,并且在使用幻数之前在程序启动时使用一次。您可以在这些函数中使用任何旧技术。

uint64_t blockermask_rook   (int square);
uint64_t blockermask_bishop (int square);
uint64_t moveboard_rook     (int square, uint64_t blockerboard);
uint64_t moveboard_bishop   (int square, uint64_t blockerboard);
uint64_t blockerboard       (int index, uint64_t blockermask);

所以你可能会问自己,da f%q 是阻挡面具、移动板和阻挡板吗?好吧,我只是编造了这些条款,但这就是我的意思:

/* Example, Rook on e4:
 *  
 *    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 
 */

阻挡面罩是所有可以被占据阻挡你的棋子进一步移动的方格。边缘方块不需要成为其中的一部分,因为无论如何你的棋子都不能进一步超过那个方块。此位板中 1 的数量决定了您需要多大的查找表来进行此块和正方形组合。在这种情况下,有 10 个,所以有 2^10 (1024) 种可能的排列可以阻挡 e4 车。

阻塞板是这些排列之一。在本例中,b4、c4、e2、e5 和 e7 上有棋子。这些是敌方友方的棋子。挡板始终是挡板掩码的子集(它不需要在其他方格(例如 blockers = occupancy & blockermask;)上显示棋子)。

对于给定的阻挡板,移动板是您的棋子的可用移动。这包括您的作品的可能捕获。请注意,它还包括捕获您自己的作品(但您可以将其与您自己的作品位置中的 NOT 进行 AND 以删除这些作品)。

所以,基本上你需要在所有方格上为车和主教生成阻挡掩码。您还需要在每个方格上为车和主教生成所有可能的阻挡板。在生成阻挡板时,您还应该生成生成的移动板。将所有这些东西存储在数组中以备后用。

现在你已经完成了,对于每个方块/块组合,你尝试随机的 64 位数字,看看它们是否神奇。您将通过使用魔术公式来知道它们是否神奇return ((blockerboard*magic) >> (64-bits));,它将从 0..2^bits(在 e4 车的情况下为 0..1024)创建一个神奇的索引。对于某个棋子/方格,如果两个阻挡板曾经产生相同的魔法指数,但这两个阻挡板有不同的移动板,那么这是一个麻瓜号码,你应该尝试一个新的。

一旦你把它记下来,你将拥有 64 个魔术车号和 64 个魔术主教号。要使用它们,在程序启动时,您将初始化所有的拦截器掩码、拦截器板和移动板。现在,您的程序可以有效地在任何方格(因此也包括皇后)上查找主教和白嘴鸦的移动棋盘。代码如下所示:

/* Retrieves the move board for the given square and occupancy board. */
uint64_t magic_move_rook  (int8_t square, uint64_t occupancy)
{
    /* Remove occupants that aren't in the blocker mask for this square. */
    occupancy &= Rook.blockmask[square];
    /* Calculate the magic move index. */
    int index = (occupancy*Rook.magic[square]) >> (64-Rook.bits[square]);
    /* Return the pre-calculated move board. */
    return Rook.moveboard[square][index];
}
于 2015-06-16T08:09:36.450 回答
6

我们可以假设魔法位板函数可用,这很好,但通常位板移动生成函数可以接受任何产生位板的技术,该位板提供可能的方块移动到。SayRookMoves是这样一个函数,那么您将按如下方式填充移动列表:

UInt64 pieceBitboard = Bitboard[SideToMove | Piece.Rook];
UInt64 targetBitboard = ~Bitboard[SideToMove | Piece.All];

while (pieceBitboard != 0) {
   Int32 from = Bit.Pop(ref pieceBitboard);
   UInt64 moveBitboard = targetBitboard & RookMoves(from, OccupiedBitboard);

   while (moveBitboard != 0) {
       Int32 to = Bit.Pop(ref moveBitboard);
       moveList[index++] = Move.Create(this, from, to);
   }
}

whereBit.Pop(ref x)返回 in 中的最低有效位,x同时从x.

要验证移动(我将其解释为确认移动有效性),您可以检查移动是否在移动列表中,或者执行移动并查看它是否让您处于检查状态。当然,您可能需要检查它是否符合棋子的运动规则,但这很简单。

if ((RookRays[move.From] & Bit.At[move.To]) == 0)
   return false;

Int32 side = SideToMove;
position.Make(move);
Boolean valid = position.InCheck(side);
position.Unmake(move);

return valid; 
于 2013-06-09T05:07:52.987 回答
-12

哈哈从未听说过“魔术位板”。谷歌它,这正是我所期望的。虽然我看不到它有什么神奇之处。无论如何要回答您的问题,您需要生成当前所选棋子的可用移动位位置。不确定还需要什么。

至于伪代码,我猜是这样的:

Positions KingChessPiece::getMovablePositions(){
   (x,y) = current bit position in the bitboard
   availablePosition = [ (x+1,y),(x-1,y),(x,y+1),(x,y-1) ]
   for each position in availablePosition
      if p_i is occupied then remove it from list

   return availablePosition
   }

我的意思是这没有什么难的,您只需要确保以与您使用的内部结构兼容的方式获取和设置位置。

编辑:

女王的例子:

Position QueenChessPiece::getMovablePosition(){
     (x,y) = queens current position
      availablePosition = []; //empty list
     //check diagonal positions
     //move top left diagonal 
     availablePosition.concat( this.generateAvailablePosition(x,y,-1,1);
     //move top right diagonal 
     availablePosition.concat( this.generateAvailablePosition(x,y,1,1);
     //move bottom right diagonal 
     availablePosition.concat( this.generateAvailablePosition(x,y,1,-1);
     //move bottom left diagonal 
     availablePosition.concat( this.generateAvailablePosition(x,y,-1,-1);

    //move straight up 
    availablePosition.concat( this.generateAvailablePosition(x,y,0,1) )
    //move straight down 
    availablePosition.concat( this.generateAvailablePosition(x,y,0,-1) )

    //move left 
    availablePosition.concat( this.generateAvailablePosition(x,y,-1,0) )
    //move right
    availablePosition.concat( this.generateAvailablePosition(x,y,1,0) )

  return availablePosition;
}
Position QueenChess::generateAvailablePosition(x,y,dx,dy){
  availPosition = [];
  while( !isSpaceOccupied(x + dx , y + dy)) 
    availPosition.add( position(x + dx ,y + dy) );
    x += dx;
    y += dy;
  endWhile
  return availPosition;
   }
于 2013-06-04T19:04:27.547 回答