|
||
БИТОВОЕ ПРЕДСТАВЛЕНИЕ ДОСКИ Одномерное представление доски Доску мы отображаем на 64- битное число. Это означает что, каждая клетка представлена одним битом. Для каждой клетки возможно два состояния - 1 или 0. Она имеет вид: (MSB) 0010…010001 (LSB) LSB – least significant bit младший бит MSB – most significant bit старший бит Мы знаем, что разряды - это степени числа 2 0001 это 2 **0 0010 это 2 **1 0100 это 2 **2 Таким образом: LSB это 2**0 MSB это 2**63 Но вот как это отображается в развертке? Возможно, 1 у нас запишется как 0000..001, а возможно и 1 у нас запишется как 1000..000 Сдвиг влево это << При его использовании самые старшие биты уходят в небытие. Значит, если опираться на графику этой операции, развертка у нас должна выглядеть так: 1 у нас запишется как 0000..001 Если еще вспомнить, что шестнадцатеричное представление у нас задается в виде: 1 = 0x1 16 = 0x10, то окончательно останавливаемся на развертке вида: 1 как 0000..001 Отображение доски на одномерный массив Доска это матрица 8 на 8. Или по-другому - это двухмерный массив. Его можно огромным количеством способов отобразить на одномерный массив. Для однозначности мы должны задать формулы отображения доски напрямую и обратно. Отображение доски напрямую задаем формулой: Line_index = 8 * Rank_index + File_index Обратное отображение прямой на доску: File_index = Line_index modulo 8 = Line_index % 8 = Line_index & 7 Rank_index = Line_index div 8 = Line_index / 8 = Line_index >> 3 Здесь у нас: Line_index – координата одномерной доски (от 0 до 63) Rank_index – координата доски по вертикали (от 0 до 7) File_index – координата доски по горизонтали (от 0 до 7) Таким образом, доску мы представляем в виде: A8(56) B8(57) C8(58) D8(59) E8(60) F8(61) G8(62) H8(63) A7(48) B7(49) C7(50) D7(51) E7(52) F7(53) G7(54) H7(55) A6(40) B6(41) C6(42) D6(43) E6(44) F6(45) G6(46) H6(47) A5(32) B5(33) C5(34) D5(35) E5(36) F5(37) G5(38) H5(39) A4(24) B4(25) C4(26) D4(27) E4(28) F4(29) G4(30) H4(31) A3(16) B3(17) C3(18) D3(19) E3(20) F3(21) G3(22) H3(23) A2(8) B2(9) C2(10) D2(11) E2(12) F2(13) G2(14) H2(15) A1(0) B1(1) C1(2) D1(3) E1(4) F1(5) G1(6) H1(7) т.е. битовая развертка выглядит следующим образом: H8(63) G8(62) F8(61) . . . . . .C1(2) B1(1) A1(0) A1(LSB)- это нулевой бит H8(MSB)-это 63 бит Мы не только развернули доску в линию, но и развернули эту линию. Структура доски Так как бит может принимать только два значения 1 или 0, то на битовой доске мы можем представить только факт присутствия или отсутствия фигуры. Конечно, нам этого недостаточно нам нужно знать, что это за фигура и какого она цвета. Для полного описания доски я использовал следующую структуру // битовая доска struct T_Bitboard { 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]; по ним можно моментально узнать какая фигура на данном поле стоит. //------------------------------------------------- // ВСПОМОГАТЕЛЬНАЯ ИНФОРМАЦИЯ // цвет хода 0 - черные 1 - белые int colour_move; // разрешение бития на проходе 1/0 int taking_passage_yes; // х координата битого поля(конвертируем из буквы) // так а - 0 , h - 7 таким образом по х отображение прямое int taking_passage_x; // у координата битого поля // так '1' - 0 , '8' - 7 таким образом отображение тоже прямое только сдвинуто на 1 int taking_passage_y; // рокировка белых в длинную сторону 1/0 int castling_Q; // рокировка белых в короткую сторону 1/0 int castling_K; // рокировка черных в длинную сторону 1/0 int castling_q; // рокировка черных в короткую сторону 1/0 int castling_k; // 64 битный хеш ключ позиции unsigned __int64 hash_key ; // оценка позиции int eval ; };//struct T_Bitboard { Расположение фигуры на доске Допустим, единственная фигура стоит на поле А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 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 или если ее развернуть то так: 0000000000000000000000000000000000000000000000000000000000000001 это у нас число 1 если фигура стоит на поле Н8 то: 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 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 или если ее развернуть то так: 1000000000000000000000000000000000000000000000000000000000000000 это число 9223372036854775808 В результате любому положению фигуры на доске мы можем сопоставить 64 битное число в диапазоне от 1 до 9223372036854775808 Зная что: 2**0 это 00..001 2**1 это 00..010 2**2 это 00..100 т.е. просто бежим по степеням 2. Мы можем рассчитать массив констант для всевозможного расположения взведенного бита на доске. В программе это массив: const unsigned __int64 move_masks[] ={ // в примечании пишем какой бит установлен в 1 1, // 0 bit т.е. ....0001 2, // 1 bit т.е. ....0010 4, // 2 bit т.е. ....0100 8, // 3 bit и т.д. 16, // 4 bit …. } Таким образом, если у нас есть 64 битное число all_piece, то добавить фигуру на поле А1 можно следующим образом: all_piece = all_piece | 1 | - or операция логического «или» работает следующим образом - если хоть одна единица, то результат единица. в программе это будет: all_piece = all_piece | move_masks[0] (очевидно что | move_masks[0] = 1) Стереть фигуру с позиции можно следующим образом: all_piece = all_piece ^ 1 ^ - xor операция исключающего или работает следующим образом, если хоть одна единица, то результат единица, но если обе единицы то результат ноль. В программе это будет: all_piece = all_piece ^ move_masks[0] Всевозможные ходы фигуры Следуя соглашению об отображении доски на одномерный массив и используя move_masks[], можно рассчитать массив ходов фигуры от ее расположения. Например, для короля на А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 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 0 0 0 1 0 0 0 0 0 0 Или если ее развернуть то так: 0000000000000000000000000000000000000000000000000000000110000001 это будет число 770 Массив всевозможных расположений и ходов для них короля в программе const unsigned __int64 king_moves_masks[] ={ // в примечании пишем, где находится король, например from0 - король в A1(0) //====================== 770, // from0 //====================== 1797, // from1 … } Аналогично для всех фигур. Массивы возможных ходов это все ходы, которые может сделать фигура, если она находится одна на доске. Следует понимать, что отображение доски и массивы возможных ходов фигуры завязаны друг на друга. Поэтому о массивах всевозможных ходов было сказано в главе о структуре доски. |