这是有关使用神奇位板验证国际象棋滑动棋子移动的大局观问题。为了澄清,我并不是在问神奇位板的内部工作原理。
现在,关于问题的更多细节。我正在使用位板编写国际象棋棋盘表示,并且希望使用神奇位板来验证滑动棋子的移动。能否有人列出实现这一点的主要步骤? 以以下位置为例:
假设我们已经初始化并准备好使用所有神奇位板函数和数据结构。因此,仅使用神奇位板的函数签名,您能否列出验证g3白色战车的特定移动的步骤(伪代码或任何语言)?
简单来说,魔幻位棋盘是一种有效的方法,可以对一个位置进行操作并获得滑动棋子的合法移动。
首先,你需要找到一些魔数。你编写代码以查找魔数时,一些代码也会在你使用魔数时被重复使用。
首先,你需要编写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);
所以你可能会问:什么是阻挡器面罩、移动板和阻拦板?好吧,这些术语是我刚刚编出来的,但是我是这样理解它们的:
/* 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
*/
blockers = occupancy & blockermask;
))。return ((blockerboard*magic) >> (64-bits));
,您将从0..2^bits(在e4 rook的情况下是0..1024)创建一个神奇的索引。对于某个特定的棋子/方格,如果两个阻挡板产生相同的神奇索引,但这两个阻挡板具有不同的移动板,则这是一个麻瓜数字,您应该尝试一个新的数字。/* 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];
}
很好,我们可以假设魔术位棋盘函数可用,但是通常位棋盘移动生成函数可以接受任何产生给出可移动方格的位棋盘的技术。例如RookMoves
是这样一个函数,然后您将按以下方式填充移动列表:
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);
}
}
Bit.Pop(ref x)
会返回 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;
perft(6)
只需2秒),切换到魔术生成所获得的收益几乎可以忽略不计,所以我就保留了它。 - ZongPositions 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;
}