1. Esports World Cup 2025 13:00 | Шахматные звезды 5.0 | Дубов - Ниманн
    Тур чемпионов. Финал top!! | ЧМ рапид + блиц 25 top!!
    Последний довод короля Книга - NEW!
    Очень СКОРО переезжаем. Оставайтесь с нами!

Основные принципы генератора ходов (Bitboard и 0x88)

Discussion in 'Машинное отделение' started by Chemer, 30 Nov 2006.

  1. TopicStarter Overlay

    Chemer Максим

    • Участник
    Member Since:
    14.09.2006
    Message Count:
    1.674
    Likes Received:
    13
    Репутация:
    0
    Location:
    Запорожье
    Оффлайн
    Начал писать свой собственный движок. Подскажите пожалуста, основные принципы генератора ходов Bitboard и 0x88. И где можно нарыть инфы по сабжу. Начал писать свой собственный (представление доски в виде одномерного массива) - получается слишком медленно.
  2. WinPooh В.М.

    • Команда форума
    Member Since:
    13.02.2006
    Message Count:
    9.494
    Likes Received:
    3.127
    Репутация:
    95
    Location:
    Москва
    Оффлайн
    http://en.wikipedia.org/wiki/Board_representation - общие слова

    http://www.seanet.com/~brucemo/topics/0x88.htm - хорошее объяснение 0x88
    http://en.wikipedia.org/wiki/Bitboard - про битборды начать можно тоже с Википедии, затем почитать, например, Роберта Хьятта (есть ссылки в конце статьи)

    Разница в скорости генератора ходов между грамотными реализациями 0x88, битбордов и mailbox (одномерного массива) не столь велика, как может показаться. Среди сильных программ встречаются все три метода. Битборды позволяют делать быстрые и эффективные проверки в оценке, и с переходом на 64-битные системы будут становиться всё более популярными...
  3. TopicStarter Overlay

    Chemer Максим

    • Участник
    Member Since:
    14.09.2006
    Message Count:
    1.674
    Likes Received:
    13
    Репутация:
    0
    Location:
    Запорожье
    Оффлайн
    Спасибо! Неплохоб ещеб сишный или паскалевский исходник, где 0х88 реальзовано.
  4. WinPooh В.М.

    • Команда форума
    Member Since:
    13.02.2006
    Message Count:
    9.494
    Likes Received:
    3.127
    Репутация:
    95
    Location:
    Москва
    Оффлайн
    Фрукт. Тога. Миллион других программ.

    http://arctrix.com/nas/fruit/
  5. TopicStarter Overlay

    Chemer Максим

    • Участник
    Member Since:
    14.09.2006
    Message Count:
    1.674
    Likes Received:
    13
    Репутация:
    0
    Location:
    Запорожье
    Оффлайн
    Еще вопрос. В альфабете необходима продцедура, которая востанавливает позицию после совершения хода типа Unmove(*pos;move). Непойму, как сделать, чтобы востанавливала позицию после взятия.
  6. WildCat Коршунов Игорь

    • Команда форума
    Member Since:
    04.05.2006
    Message Count:
    3.599
    Likes Received:
    4
    Репутация:
    0
    Location:
    Гомель
    Оффлайн
    Можно запомнить какую фигуру мы сбили. И в Unmove() ставим ее на поле, куда ходили (или чуть рядом, если это было взятие на проходе).
  7. WinPooh В.М.

    • Команда форума
    Member Since:
    13.02.2006
    Message Count:
    9.494
    Likes Received:
    3.127
    Репутация:
    95
    Location:
    Москва
    Оффлайн
    Можно и вообще без unmove обойтись, просто при совершении хода копировать структуру позиции.
    Да, это будет медленно - но зато меньше шансов ошибиться. Потом всегда можно будет к этому месту вернуться.
    Главное, помнить завет Кнута/Хора про преждевременную оптимизацию.
  8. TopicStarter Overlay

    Chemer Максим

    • Участник
    Member Since:
    14.09.2006
    Message Count:
    1.674
    Likes Received:
    13
    Репутация:
    0
    Location:
    Запорожье
    Оффлайн
    Я думаю лучше прикрепить к move поле move.kill_figura и в нем передовать взятие. Чтобы при рекурсии не создавать лишних проблем
  9. WinPooh В.М.

    • Команда форума
    Member Since:
    13.02.2006
    Message Count:
    9.494
    Likes Received:
    3.127
    Репутация:
    95
    Location:
    Москва
    Оффлайн
    Можно и так. Я пробовал разные варианты. Разницы по скорости почти никакой. Не в этом суть.
    Выбирайте тот метод, который лично вам покажется наиболее удобным, простым для понимания и реализации (это главное!)

    На любительском уровне хорошо играет не самая навороченная программа, а самая свободная от ошибок.
  10. WildCat Коршунов Игорь

    • Команда форума
    Member Since:
    04.05.2006
    Message Count:
    3.599
    Likes Received:
    4
    Репутация:
    0
    Location:
    Гомель
    Оффлайн
    На профессиональном тоже :)

    ЗЫ Тема ветки как-то неадекватна. Винни, может стоит ее подправить?
    Например лучше: "Основные принципы генератора ходов (Bitboard и 0x88)"
  11. bankuss Александр

    • Заслуженный
    • Участник
    • Старожил
    Member Since:
    24.05.2006
    Message Count:
    1.084
    Likes Received:
    38
    Репутация:
    6
    Оффлайн
    о скорости на данном этапе можно не думать - главное сделать рабочий движок. :)
  12. ProstoTak Старожил

    • Ветеран
    • Старожил
    Member Since:
    12.02.2006
    Message Count:
    5.479
    Likes Received:
    123
    Репутация:
    1
    Оффлайн
    ПУХ, ты садюга. По русски нет что ли статей? Дай по русски, будь человеком, а не только медведем :)
  13. bankuss Александр

    • Заслуженный
    • Участник
    • Старожил
    Member Since:
    24.05.2006
    Message Count:
    1.084
    Likes Received:
    38
    Репутация:
    6
    Оффлайн
    ProstoTak по русски тоже есть но малооо :)
  14. TopicStarter Overlay

    Chemer Максим

    • Участник
    Member Since:
    14.09.2006
    Message Count:
    1.674
    Likes Received:
    13
    Репутация:
    0
    Location:
    Запорожье
    Оффлайн
    Мужики, так что там насчет по-русски? То по-аглицки я не очень. Можно и своими словами подробно рассказать про 0х88.
  15. WildCat Коршунов Игорь

    • Команда форума
    Member Since:
    04.05.2006
    Message Count:
    3.599
    Likes Received:
    4
    Репутация:
    0
    Location:
    Гомель
    Оффлайн
    Русских статей по этой теме я не встречал.

    Проще всего mailbox:

    Square Board[120]; // доска из 120 полей

    Номера некоторых полей:

    #define A1 91
    #define B1 92
    #define C1 93
    #define D1 94
    #define E1 95
    #define F1 96
    #define G1 97
    #define H1 98

    #define A8 21
    #define B8 22
    #define C8 23
    #define D8 24
    #define E8 25
    #define F8 26
    #define G8 27
    #define H8 28

    Те поля, которые выходят за нраницы обычной шахматной доски забиваем специальной константой.

    Думаю, что предсставление доски понятно.

    Для примера генерация ходов слонов, ладей, ферзей вдоль одного луча.
    Code:
    void GenSlideRay(MoveList *ml, int from, int inc)
    {
        int to = from + inc;
        while (IS_EMPTY(to))
        {
            ml.AddMove(from, to);
            to += inc;
        }
        if (IS_OPPONENT(to)) ml.AddCapture(from, to);
    }
    Макросы (или красивее сделать inline-функциями):
    IS_EMPTY(to) - свободно ли поле to
    IS_OPPONENT(to) - на поле to фигура противника?

    Думаю, что идея понятна. Что может быть проще?
  16. TopicStarter Overlay

    Chemer Максим

    • Участник
    Member Since:
    14.09.2006
    Message Count:
    1.674
    Likes Received:
    13
    Репутация:
    0
    Location:
    Запорожье
    Оффлайн
    Я похожее на выходных ваял - слона, ладью, ферзя, короля(без шаха). Но мне кажетяся по лучам - неочень изящно.
  17. TopicStarter Overlay

    Chemer Максим

    • Участник
    Member Since:
    14.09.2006
    Message Count:
    1.674
    Likes Received:
    13
    Репутация:
    0
    Location:
    Запорожье
    Оффлайн
    А доска 120 - здорово! по 2 поля вокруг основной доски для аутов! А то я задолбался проверки делать на выход за границы!
    Век живи - век учись - все равно дураком сдохнешь! :)
  18. WinPooh В.М.

    • Команда форума
    Member Since:
    13.02.2006
    Message Count:
    9.494
    Likes Received:
    3.127
    Репутация:
    95
    Location:
    Москва
    Оффлайн
    120 x 120 многовато будет :)

    Доски используют самых разных размеров, некоторые считают самым удачным 10x12, другие - 12x16, или 10x8... Базовая идея одна и та же - использовать факт, что в двоичном представлении для одной координаты достаточно трёх бит, а если вылезли в четвёртый - значит, аут. Другие, кроме классического 16x8, размеры используют для разных специфических ускорений в генераторе - например, быстрый поиск нужных диагоналей.

    Ещё одно полезное свойство 16x8 - то, что разность двух полей всегда зависит только от относительного вектора, соединяющего одно с другим, и не зависит от положения на доске (имеется в виду параллельный перенос пары полей куда угодно). Например, (B3 - A1) == (G6 - F4). Это существенная помощь в проверке на шахи (в данном случае, коня).
  19. TopicStarter Overlay

    Chemer Максим

    • Участник
    Member Since:
    14.09.2006
    Message Count:
    1.674
    Likes Received:
    13
    Репутация:
    0
    Location:
    Запорожье
    Оффлайн
    Насчет 120х120 - описка - конечно же просто 120!
  20. WildCat Коршунов Игорь

    • Команда форума
    Member Since:
    04.05.2006
    Message Count:
    3.599
    Likes Received:
    4
    Репутация:
    0
    Location:
    Гомель
    Оффлайн
    Другого нам не дано.
  21. NS Нефёдов Сергей

    • Заслуженный
    • Ветеран
    • Старожил
    Member Since:
    02.05.2006
    Message Count:
    6.811
    Likes Received:
    96
    Репутация:
    3
    Location:
    Санкт-Петербург
    Оффлайн
    Проверка за выход доски в 0x88 (это в десятиной системе 136)
    if (square and 136)=0 then //поле на доске
    if (square and 136)<>0 then //поле не на доске
    Вроде не очень сложно :)
  22. WildCat Коршунов Игорь

    • Команда форума
    Member Since:
    04.05.2006
    Message Count:
    3.599
    Likes Received:
    4
    Репутация:
    0
    Location:
    Гомель
    Оффлайн
    В mailbox вообще не надо проверять выход за доску.
  23. TopicStarter Overlay

    Chemer Максим

    • Участник
    Member Since:
    14.09.2006
    Message Count:
    1.674
    Likes Received:
    13
    Репутация:
    0
    Location:
    Запорожье
    Оффлайн
    Переделаю свой горе-генератор в mailbox WildCat-а вреде идея неплохая и переделывать не очень много.
  24. NS Нефёдов Сергей

    • Заслуженный
    • Ветеран
    • Старожил
    Member Since:
    02.05.2006
    Message Count:
    6.811
    Likes Received:
    96
    Репутация:
    3
    Location:
    Санкт-Петербург
    Оффлайн
    Да, но он вроде получается чуть медленней, так как вектора возможных ходов фигур храняться не в регистрах, а в хеш-памяти.
    Хотя на 1С у меня такой генератор получился более быстрым, чем генератор 10x10 - но это специфика языка.
    Или я путаю типы генераторов? И то о чем я говорю называется иначе? (список легальных ходов каждой фигуры с каждого поля)
    В генераторе WildCat-а есть проверка за выходы за поле, только в отличии от 0x88 она делается по-другому.
    Если в 0x88 не надо обращаться к памяти для проверки выхода за доску, то в его генераторе проверяется маркер на поле - то есть идет обращение к оперативной памяти. Но такой генератор получается чуть более быстрым чем 0x88
  25. WinPooh В.М.

    • Команда форума
    Member Since:
    13.02.2006
    Message Count:
    9.494
    Likes Received:
    3.127
    Репутация:
    95
    Location:
    Москва
    Оффлайн
    Оптимизировать надо прежде всего генератор взятий. Именно он вызывается в программе чаще всего, в каждом конечном узле основного поиска - один раз как минимум! А его оптимизировать можно только переходя на битборды...
  26. WildCat Коршунов Игорь

    • Команда форума
    Member Since:
    04.05.2006
    Message Count:
    3.599
    Likes Received:
    4
    Репутация:
    0
    Location:
    Гомель
    Оффлайн
    В любом случае надо проверять маркер на поле (хотя бы чтобы проверить отсутсвие своей фигуры на этом поле).
    В 0x88 эту проверку можно пропустить только для полей вне доски. Т.е. мой будет и быстрее и удобнее.

    Генератор взятий не явл. чем-то критическим. И вряд-ли из-за него стоит заморачиваться. На битборды стоит переходить только, если в ОФ есть признаки вычисляемые на битбордах намного эффективнее.
    Например, битборды очень удобны для оценки пешечной структуры. Но можно воспользоваться трюком как в Фрукте - хранить битборды только с пешками, остальные фигуры как обычно. С другой стороны оценка пешечной структуры хорошо кешируется. Значит никакой существенной прибавки мы здесь не получим.
  27. TopicStarter Overlay

    Chemer Максим

    • Участник
    Member Since:
    14.09.2006
    Message Count:
    1.674
    Likes Received:
    13
    Репутация:
    0
    Location:
    Запорожье
    Оффлайн
    Все даделал майлбокс по WildCat получилось очень красиво. Немного запары с енпасантом. Теперь думаю как написать проверялку на шахи, и генерировать легальные ходы под шахом.
  28. ProstoTak Старожил

    • Ветеран
    • Старожил
    Member Since:
    12.02.2006
    Message Count:
    5.479
    Likes Received:
    123
    Репутация:
    1
    Оффлайн
    Кстати, проверки на шах тот ещё гемор. Времени жрёт много. Надо бы совет как оптимизировать.
  29. WinPooh В.М.

    • Команда форума
    Member Since:
    13.02.2006
    Message Count:
    9.494
    Likes Received:
    3.127
    Репутация:
    95
    Location:
    Москва
    Оффлайн
    Первое правило оптимизации: НЕ ОПТИМИЗИРУЙ!
    Второе правило оптимизации (только для экспертов): если всё же без оптимизации не обойтись, ВСЁ РАВНО НЕ ОПТИМИЗИРУЙ!
  30. WildCat Коршунов Игорь

    • Команда форума
    Member Since:
    04.05.2006
    Message Count:
    3.599
    Likes Received:
    4
    Репутация:
    0
    Location:
    Гомель
    Оффлайн
    Для проверки на шах проще всего сходить от короля по всем направлениям и посмотреть нет ли шахующей фигуры.

    А можно просто следить объявил ли противник шах свом последним ходом. Здесь все просто, смотрим дает ли шах ходившая фигура, если нет, то смотрим может получился вскрытый шах. Еще надо не забыть про рокировки и взятия на проходе.
  31. NS Нефёдов Сергей

    • Заслуженный
    • Ветеран
    • Старожил
    Member Since:
    02.05.2006
    Message Count:
    6.811
    Likes Received:
    96
    Репутация:
    3
    Location:
    Санкт-Петербург
    Оффлайн
    Проверка на Шах для 0x88
    Просьба не пугаться именам переменных, и прописанных напрямую в коде значений констант.

    Code:
    function check_itW(var pos:board;otkuda:byte):boolean; inline;
      var kuda:byte;
      var chto:shortint;
      begin
        with pos do
        begin
        if ChKor[otkuda-Bking+128]>0 Then
             Begin
                Check_itW:=true;
                exit;
             end;
        // Пешка.
        if BKolPeshka>0 then
        begin
          kuda:=otkuda+17;
          if ((kuda and 136)=0) and (shortint(pole[kuda])=-1) then
          Begin
            Check_itW:=true;
            exit;
          end;
          kuda:=otkuda+15;
          if ((kuda and 136)=0) and (shortint(pole[kuda])=-1) then
          Begin
            Check_itW:=true;
            exit;
          end;
        end;
        // конь
        if BKolKon>0 then
        begin
          kuda:=otkuda+byte(33);
          if ((kuda and 136)=0) and (shortint(pole[kuda])=-2) then
          Begin
            Check_itW:=true;
            exit;
          end;
          kuda:=otkuda+byte(-33);
          if ((kuda and 136)=0) and (shortint(pole[kuda])=-2) then
          Begin
            Check_itW:=true;
            exit;
          end;
           kuda:=otkuda+byte(31);
          if ((kuda and 136)=0) and (shortint(pole[kuda])=-2) then
          Begin
            Check_itW:=true;
            exit;
          end;
          kuda:=otkuda+byte(-31);
          if ((kuda and 136)=0) and (shortint(pole[kuda])=-2) then
          Begin
            Check_itW:=true;
            exit;
          end;
             kuda:=otkuda+byte(14);
          if ((kuda and 136)=0) and (shortint(pole[kuda])=-2) then
          Begin
            Check_itW:=true;
            exit;
          end;
          kuda:=otkuda+byte(-14);
          if ((kuda and 136)=0) and (shortint(pole[kuda])=-2) then
          Begin
            Check_itW:=true;
            exit;
          end;
           kuda:=otkuda+byte(18);
          if ((kuda and 136)=0) and (shortint(pole[kuda])=-2) then
          Begin
            Check_itW:=true;
            exit;
          end;
          kuda:=otkuda+byte(-18);
          if ((kuda and 136)=0) and (shortint(pole[kuda])=-2) then
          Begin
            Check_itW:=true;
            exit;
          end;
        end;
          // слон или ферзь
          if (BKolBishop+BKolFerz)>0 then
          begin
          kuda:=otkuda+byte(-17);
          while (kuda and 136)=0 do
            begin
            chto:=shortint(pole[kuda]);
            if chto<>0 then
              begin
              if (chto=-3)or(chto=-5) then
               begin
               check_itW:=true;
               exit;
               end;
              break;
              end;
              kuda:=kuda+byte(-17);
            end;
    
            kuda:=otkuda+byte(17);
          while (kuda and 136)=0 do
            begin
            chto:=shortint(pole[kuda]);
            if chto<>0 then
              begin
              if (chto=-3)or(chto=-5) then
               begin
               check_itW:=true;
               exit;
               end;
              break;
              end;
              kuda:=kuda+byte(17);
            end;
          kuda:=otkuda+byte(-15);
          while (kuda and 136)=0 do
            begin
            chto:=shortint(pole[kuda]);
            if chto<>0 then
              begin
              if (chto=-3)or(chto=-5) then
               begin
               check_itW:=true;
               exit;
               end;
              break;
              end;
              kuda:=kuda+byte(-15);
            end;
    
          kuda:=otkuda+byte(15);
          while (kuda and 136)=0 do
            begin
            chto:=shortint(pole[kuda]);
            if chto<>0 then
              begin
              if (chto=-3)or(chto=-5) then
               begin
               check_itW:=true;
               exit;
               end;
              break;
              end;
              kuda:=kuda+byte(15);
            end;
           end;
            // Ладья или ферзь
          if (BKolRook+BKolFerz)>0 then
          begin
          kuda:=otkuda+byte(-16);
          while (kuda and 136)=0 do
            begin
            chto:=shortint(pole[kuda]);
            if chto<>0 then
              begin
              if (chto=-4)or(chto=-5) then
               begin
               check_itW:=true;
               exit;
               end;
              break;
              end;
              kuda:=kuda+byte(-16);
            end;
    
            kuda:=otkuda+byte(16);
          while (kuda and 136)=0 do
            begin
            chto:=shortint(pole[kuda]);
            if chto<>0 then
              begin
              if (chto=-4)or(chto=-5) then
               begin
               check_itW:=true;
               exit;
               end;
              break;
              end;
              kuda:=kuda+byte(16);
            end;
          kuda:=otkuda+byte(-1);
          while (kuda and 136)=0 do
            begin
            chto:=shortint(pole[kuda]);
            if chto<>0 then
              begin
              if (chto=-4)or(chto=-5) then
               begin
               check_itW:=true;
               exit;
               end;
              break;
              end;
              kuda:=kuda+byte(-1);
            end;
    
          kuda:=otkuda+byte(1);
          while (kuda and 136)=0 do
            begin
            chto:=shortint(pole[kuda]);
            if chto<>0 then
              begin
              if (chto=-4)or(chto=-5) then
               begin
               check_itW:=true;
               exit;
               end;
              break;
              end;
              kuda:=kuda+byte(1);
            end;
          end;
    
        check_itW:=false;
        end;
      end;
    Все направления прописаны напрямую в коде - чтоб не оптимизировать вызовы функций.
  32. NS Нефёдов Сергей

    • Заслуженный
    • Ветеран
    • Старожил
    Member Since:
    02.05.2006
    Message Count:
    6.811
    Likes Received:
    96
    Репутация:
    3
    Location:
    Санкт-Петербург
    Оффлайн
    Обявление структуры позиции

    type BoardPole = array[0..127] of byte;
    type board=record
    Rokir:byte; // 0000 KB DB KW DW
    pole: BoardPole;
    prohod:byte; //ноль, или координата
    hod:shortint;//больше или меньше нуля
    WKolRook:byte;//Количество Ладей
    BKolRook:byte;
    WKolBiShop:byte;//Количество Слонов
    BKolBishop:byte;
    WKolFerz:byte;//Количество Ферзей
    BKolFerz:byte;
    WKolKon:byte;//Количество коней
    BKolKon:byte;
    WKolPeshka:byte;//Количество пешек
    BKolPeshka:byte;
    WKing:Byte; // координата
    BKing:Byte;
    end;
  33. ProstoTak Старожил

    • Ветеран
    • Старожил
    Member Since:
    12.02.2006
    Message Count:
    5.479
    Likes Received:
    123
    Репутация:
    1
    Оффлайн
    Значицца так. Докладаю. После некоторой доделки мой генератор даёт 5 000 000 генераций всех (20) ходов из начальной позиции за 22 секунды. Это получается около 4,5 миллионов ходов в секунду генерит. Правда пока нет проверки на шах и превращение только в ферзя. Проц Celeron 900 MHz.
    Это много или мало?
  34. ProstoTak Старожил

    • Ветеран
    • Старожил
    Member Since:
    12.02.2006
    Message Count:
    5.479
    Likes Received:
    123
    Репутация:
    1
    Оффлайн
    А как именно медленно? Какие цифры?
  35. WinPooh В.М.

    • Команда форума
    Member Since:
    13.02.2006
    Message Count:
    9.494
    Likes Received:
    3.127
    Репутация:
    95
    Location:
    Москва
    Оффлайн
    Это нормально. Только учти, что если ты считаешь только для первого хода, то совершенно не задействована часть, отвечающая за ходы дальнобойных фигур - самая прожорливая часть во всех mailbox-генераторах.

    Реализуй как можно скорее функцию perft. Проверь корректность генератора на вот этих позициях:
    http://mypage.bluewin.ch/romanhartmann/perft.html

Share This Page