|
||
ВЗЯТИЯ И ПРЕВРАЩЕНИЯ ПЕШЕК Приведем фрагмент структуры доски для того, что бы дальше было понятно, откуда мы берем переменные. unsigned __int64 all_piece; все фигуры unsigned __int64 all_white_piece; все белые фигуры unsigned __int64 all_black_piece; все черные фигуры битовые списки черных и белых фигур для каждого класса фигур своя битовая доска. unsigned __int64 white_piece[NAME]; unsigned __int64 black_piece[NAME]; NAME - это имя фигуры и оно принимает следующие значения: 0 - нет фигуры 1 - пешка 2 - конь 3 - слон 4 - ладья 5 - ферзь 6 - король имя фигуры от координаты : int white_name_from_coord[COORDINATE]; int black_name_from_coord[COORDINATE]; по ним можно моментально узнать, какая фигура на данном поле стоит. Взятия короля и коня реализуются проще всего Рассмотрим взятия белого короля. // маска расположения белого короля from_mask = bitboard->white_piece[6]; // находим координату фигуры (самый первый (для нашего отображения правый) установленный бит) from = first_one(from_mask); // находим маску взятий короля captures_mask = (bitboard->all_black_piece & king_moves_masks[from]); while (captures_mask !=0){ // Заполняем список ходов to = first_one(captures_mask); list_capture_white(6,2,from,to,p,bitboard,list_surplus_moves); captures_mask = captures_mask & (captures_mask-1); }//while (captures !=0){ Вначале нам нужно определить координату короля. Для этого используем битовые списки белых фигур. Прежде всего, выделяем нужную маску (король у нас находится под цифрой 6) from_mask = bitboard->white_piece[6]; дальше находим координату фигуры (самый первый (для нашего отображения правый) установленный бит) from = first_one(from_mask); Для понимания нужно знать, что делает функция first_one(). Эта функция возвращает самый правый установленный бит. Например: first_one(...00001) = 0 first_one(...00010) = 1 first_one(10000...) = 63 полное описание данной функции приведу в отдельном пункте. Далее находим маску взятий короля: captures_mask = (bitboard->all_black_piece & king_moves_masks[from]); У нас есть доска, в которой все черные фигуры представлены взведенными битами, и есть множество возможных ходов короля, которые тоже представлены взведенными битами. В результате применения операции логического и у нас остаются только совпадающие биты, т.е. это фигуры, которые может побить король. Следует сказать, что _moves_masks[from] - это массив ходов короля в зависимости от расположения. Этот массив рассчитывается заранее. Такие массивы посчитаны для всех фигур. Я говорил об этом в разделе битовое представление доски. Для иллюстрации принципа приведу очень упрощенный пример. Допустим, король у нас в 1 бите, и может ходить в 0 и 2 бит: 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 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 k 1 0 0 0 0 0 а вражеские фигуры находятся в 2 и 3 бите: 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 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 0 0 1 1 0 0 0 0 5 = 0..0101 - ходы белого короля. 12 = 0..1100 - все черные фигуры. & - используем операцию битового и которое оставляет 1, только если 1 присутствуют в обоих битах. 4 = 0..0100 - взятия белого короля в итоге получили маску взятий белого короля (в примере это число 4) 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 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 0 0 1 0 0 0 0 0 дальше посредством функции first_one мы определим, что это 2 бит. Т.е. король ходит с 1 бита на 2. Заполняем список ходов. while (captures_mask !=0){ to = first_one(captures_mask); list_capture_white(6,2,from,to,p,bitboard,list_surplus_moves); captures_mask = captures_mask & (captures_mask-1); }//while (captures !=0){ Для коня все аналогично. Взятия слайдеров, т.е слона, ладьи, ферзя Для слайдеров дела обстоят несколько сложнее. Причина в том, что на их пути к врагу могут стоять свои или вражеские фигуры. Этот момент надо учитывать. Рассмотрим генерацию взятий для ладьи. from_mask = bitboard->white_piece[4]; while(from_mask != 0){ from = first_one(from_mask); from_mask = from_mask & (from_mask-1); // смотрим в четырех различных направлениях // Генерируем ходы // луч вверх blocking_square_mask = (bitboard->all_piece) & rook_up_masks[from]; if(blocking_square_mask !=0){ blocking_coordinate = first_one(blocking_square_mask); captures_mask = bitboard->all_black_piece & move_masks[blocking_coordinate]; // Заполняем список ходов if(captures_mask!=0)list_record_w(4,2,p,from,captures_mask,bitboard,list_surplus_moves); }//if(blocking_square_mask !=0){ ...... Проиллюстрируем работу приведенного кода на примере. Допустим, ладья находится во 2 бите, т.е. на поле C1 тогда всевозможные ходы вверх это массив вида 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 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 r 0 0 0 0 0 или число rook_up_masks[2] = 289360691352306688 допустим так же, что слон находится на поле С4 - это 26 бит, а конь на С6 - это 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 n 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 b 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 r 0 0 0 0 0 первое что мы делаем - это находим маску, содержащую блокированные биты: blocking_square_mask = (bitboard->all_piece) & rook_up_masks[from]; 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 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 0 0 0 0 0 0 Проверяем, и если она нулевая то взятий нет. if(blocking_square_mask !=0){ … } Иначе, находим номер первого блокированного бита и генерируем маску ходов вверх: blocking_coordinate = first_one(blocking_square_mask); captures_mask = bitboard->all_black_piece & move_masks[blocking_coordinate]; Пишем взятие, только если есть взятия. Ведь блокировка может быть своей фигурой, тогда блок есть, а взятия нет. (captures_mask!=0) list_record_w(4,2,p,from,captures_mask,bitboard,list_surplus_moves); Тут есть важный момент, если это луч вверх, то используем first_one, а если луч вниз, то last_one Дело в том, что блокированных битов может быть несколько, а нам нужен первый по лучу от фигуры В итоге маска взятий такова. 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 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 0 0 лучи вниз (last_one), влево (last_one) и вправо (first_one) аналогично. Так же считаются взятия для слона: лучи вверх – вправо (first_one), вверх – влево (first_one), вниз – вправо (last_one), вниз – влево (last_one). Взятия и превращения пешки from_mask = bitboard->white_piece[1]; while(from_mask != 0){ from = first_one(from_mask); //выделяем одну пешку one_pawn_mask = from_mask & move_masks[from]; from_mask = from_mask & (from_mask-1); Если у нас пешка на предпоследней горизонтали, то считаем ходы и взятия с превращением иначе простые взятия пешкой. if ( from / 8 == 6 ){ // считаем ходы с превращением и взятия с превращением // 1 находим простые ходы белой пешки на одно поле вперед move_conversion_mask = (one_pawn_mask << 8) & (~(bitboard->all_piece)); // взятия с превращением captures_conversion_mask = pawn_capture_white[from] & bitboard->all_black_piece; Пишем ходы с превращением. while (move_conversion_mask != 0){ to = first_one(move_conversion_mask); move_conversion_mask = move_conversion_mask & (move_conversion_mask-1); // превращение пешки // превращение в ферзь 15,превращение в ладью 14,превращение в слона 13,превращение в коня 12 list_record_os(1,15,p,from,to,0,list_surplus_moves); list_record_os(1,14,p,from,to,0,list_surplus_moves); list_record_os(1,13,p,from,to,0,list_surplus_moves); list_record_os(1,12,p,from,to,0,list_surplus_moves); #if KONTROL_B KONTROL_move(&from,&to,&p); #endif//KONTROL_B }//while (move_conversion_mask != 0){ //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Пишем взятия с превращением. while (captures_conversion_mask != 0){ to = first_one(captures_conversion_mask); captures_conversion_mask = captures_conversion_mask & (captures_conversion_mask-1); // превращение пешки с взятием // превращение в ферзь 25,превращение в ладью 24,превращение в слона 23,превращение в коня 22 list_record_os(1,25,p,from,to,bitboard->black_name_from_coord[to],list_surplus_moves); list_record_os(1,24,p,from,to,bitboard->black_name_from_coord[to],list_surplus_moves); list_record_os(1,23,p,from,to,bitboard->black_name_from_coord[to],list_surplus_moves); list_record_os(1,22,p,from,to,bitboard->black_name_from_coord[to],list_surplus_moves); #if KONTROL_B KONTROL_captures_W(bitboard,&from,&to,&p,120); #endif//KONTROL_B }//while (captures_conversion_mask != 0){ }else { // считаем взятия пешкой captures_mask = pawn_capture_white[from] & bitboard->all_black_piece; Пишем простые взятия. while (captures_mask != 0){ to = first_one(captures_mask); list_capture_white(1,2,from,to,p,bitboard,list_surplus_moves); captures_mask = captures_mask & (captures_mask-1); }//while (captures_mask != 0){ }//if ( from / 8 == 6 ){ //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // 4 находим взятия на проходе if(bitboard->taking_passage_yes == 1){ if( from / 8 == 4 ){ to = 8 * bitboard->taking_passage_y + bitboard->taking_passage_x; if( ((from - to) == -9) && (from % 8 != 7) ){ list_record_os(1,5,p,from,to,bitboard->black_name_from_coord[to - 8],list_surplus_moves); #if KONTROL_B to = to - 8; KONTROL_captures_W(bitboard,&from,&to,&p,130); #endif//KONTROL_B }//if ( ((from - to) == -9) && (from % 8 != 7) ){ if( ((from - to) == -7) && (from % 8 != 0) ){ list_record_os(1,5,p,from,to,bitboard->black_name_from_coord[to - 8],list_surplus_moves); #if KONTROL_B to = to - 8; KONTROL_captures_W(bitboard,&from,&to,&p,130); #endif//KONTROL_B }//if ( ((from - to) == -7) && (from % 8 != 0) ){ }//if ( from / 8 == 4 ){ }//if(bitboard->taking_passage_yes == 1){ }//while(from_mask != 0){ |