2013-06-04 3 views
7

Это вопрос относительно большой картины того, как проверять перемещение кулака в шахматы с помощью магических битов. Чтобы уточнить, я не спрашиваю , как магические биты работают внутри.Сдвижное поколение поколений с использованием магической доски

Теперь, более подробную информацию о запросе. Я пишу представление шахматной доски с помощью битовой платы, и я хочу проверить подвижные куски с использованием магических битов. Может ли кто-нибудь изложить основные шаги по достижению этого? В качестве примера рассмотрим следующую позицию платы:

White to move. Validate a given move for the rook on g3

Предположим, у нас есть все магические функции bitboard и структуры данных инициализированы и готовы к использованию. Таким образом, используя только сигнатуры функций для магических битов, вы можете перечислить шаги (псевдо-код или любой язык) для проверки данного шага для белой ладьи на g3?

ответ

4

Это хорошо, что мы можем предположить, магические функции bitboard доступны, но в целом bitboard функции генерации шаг может принимать любую технику, которая производит bitboard что дает возможные квадраты для перемещения. Скажем 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; 
+0

Большое спасибо Zong Zheng Li, который отвечает на мой вопрос. Хотя на данный момент я использую более простой подход (http://chessprogramming.wikispaces.com/Classical+Approach), я вернусь к магическим советам, как только приступлю к общему процессу, включая поиск и оценку. – bytefire

+0

@bytefire Да, в моем собственном движке я также использую классический подход, поскольку его было легко реализовать как временный компонент. Я обнаружил, что он достаточно быстр ('perft (6)' через 2 секунды), что выигрыш от перехода к магическому поколению в значительной степени незначителен, поэтому я просто сохранил его. – Zong

+0

Это очень полезно, поскольку оно добавляет больше значения тому, что я сейчас реализую. На стороне примечания, когда я ударил дальнейшие блокпосты, я буду размещать вопросы на SO с тегом «шахматы». Ждем больше помощи :) – bytefire

-6

Хаха никогда не слышал о «волшебной доске». Google это, и это именно то, что я ожидал. Хотя я не вижу в этом никакой магии. В любом случае, чтобы ответить на ваш вопрос, вам нужно создать доступную позицию битов перемещения выбранной вами части. Не уверен, что еще нужно.

Что касается псевдо кода что-то вроде, так что я предполагаю:

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 
    } 

Я имею в виду не что иное, трудно об этом, вы просто должны убедиться, что вы получаете и установить позицию таким образом, совместимым с внутренней структурой вы используются.

EDIT:

пример королевы:

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; 
    } 
+0

Спасибо за ваш ответ. Это хорошо для короля, но как насчет ладьи? Это не просто для скользящих частей, т. Е. Ладьи, епископа и королевы. Блокатор блокирует квадрат, который он занимает, а также любой квадрат после этого в направлении движения. – bytefire

+0

@bytefire См. Edit – dchhetri

+1

Нет, магические биты генерируют карты атаки только один раз перед движением поколений. Когда дело доходит до поколения, это просто просмотр таблицы. Это наивная реализация, которая вообще не использует преимущества для билтов. – Zong

14

Проще говоря, малые биты - это эффективный способ занять позицию и получить правовые движения для скользящей части.

Во-первых, вам нужно найти магические числа. Некоторые из кода, который вы пишете на , находите магические числа также будут повторно использоваться, когда вы используете магические числа.

Для начала вам необходимо написать 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); 

Таким образом, вы можете спросить себя, да 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;)).

Плата перехода является результирующими доступными ходами для вашей части для данной блокирующей доски. Это включает в себя возможность захвата вашей пьесы. Обратите внимание, что это также включает в себя захват ваших собственных произведений (но вы можете просто И это с НЕ из ваших мест кусочков, чтобы удалить их).

Итак, в основном вам нужно создать маску блокировщика на всех квадратах, как для ладьи, так и для слона. И вам также нужно создать все возможные блокираторы на каждом квадрате, как для ладьи, так и для слона. Когда вы создаете доски блокаторов, вы также должны генерировать результирующие платы перемещения. Храните все это в массивах для последующего использования.

Теперь, когда вы сделали это, для каждой комбинации квадратов/частей вы производите случайные 64-битные номера и видите, являются ли они волшебными. Вы узнаете, являются ли они волшебными, используя магическую формулу, return ((blockerboard*magic) >> (64-bits));, которая создаст магический индекс из 0..2^бит (0..1024 в случае ладьи e4). Для определенного куска/квадрата, если две блокирующие доски когда-либо генерируют один и тот же магический индекс , но, эти две платы-доски имеют разные доски для перемещения, тогда это номер магглов, и вы должны попробовать новый.

Как только вы получите это, у вас будет 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]; 
} 
+1

Это был фантастический ответ, конечно, сделал многое для меня намного более ясным. Единственное, с чем я сейчас сталкиваюсь, это то, что на самом деле является индексом, и почему он правильно указывает на нужное место в массиве перемещений. У вас есть куда я могу пойти для более ясного понимания этого? Я проверил все обычные места, то есть шахматы, прокручивая wiki, различные сообщения в блогах и т. Д. – abarax

+2

Это волшебство, человек. Серьезно, хотя, я все это понял, когда написал ответ прошлым летом, и теперь я изо всех сил пытаюсь вспомнить. Таким образом, индекс, возвращаемый магической формулой, всегда является целым числом меньше, чем общее количество блокировщиков (BB). Каждый ВВ имеет соответствующую плату (МБ). При поиске магии вы вычисляете каждый МБ медленным алгоритмом и сохраняете MB по индексу, рассчитанному магическим числом кандидата. При запуске двигателя МБ пересчитываются медленным алгоритмом, а затем они просто сохраняются в индексе, рассчитанном вашими запрограммированными хорошими магами. – paulwal222

+2

Кроме того, хотя у вас может быть 1024 BB, каждый из которых имеет соответствующий MB, вы можете в конечном итоге хранить менее 1024 МБ. Например, для двух разных ББ, имеющих один и тот же МБ, магия могла вычислять два разных показателя или могла рассчитывать одинаковый индекс для обоих. Пока МБ одинаковы, нормально, чтобы индекс столкнулся. Поэтому, чтобы напрямую ответить на ваш вопрос, просто удача/вероятность, что мы нашли число, которое происходит для расчета уникальных индексов для уникальных МБ. Затем мы целенаправленно храним MB с правильными индексами при запуске двигателя, используя магическую формулу и нашу известную магию. – paulwal222