2015-12-10 3 views
2

Прелюдии: Я понимаю, этот вопрос может быть слишком широким для такого переполнения стека. Хотя, я пытался добавить столько информации, сколько мог придумать.Транспонирование таблицы приводит к движку потерять

Я кодирую шахматный движок в C++ с нуля, и это в основном сделано (за исключением того, что у него слабая функция оценки). Странно, однако, что двигатель теряет каждый раз, когда таблица транспонирования включена, удивительно очевидной ошибкой (отдает королеву, а иногда и королеву и другую часть). Когда зондирование транспозиционного стола отключено, двигатель легко побеждает против Nero и TSCP.

Я использую очень простую реализацию таблицы транспозиций с помощью схемы всегда замены, взятой с сайта Брюса Морланда here.

Вот реализация зонда:

bool probe_table(TranspositionTable& t_table, unsigned int ply, 
uint64 hash_key, unsigned int depth, unsigned int& pv_move, int& score, 
int alpha, int beta) 
{ 
    unsigned int index = hash_key % t_table.num_entries; 

    assert(index < t_table.num_entries); 

    if(t_table.t_entry[index].hash_key == hash_key) 
    { 
     pv_move = t_table.t_entry[index].move; 

     if(t_table.t_entry[index].depth >= depth) 
     { 
      score = t_table.t_entry[index].score; 

      if(score > IS_MATE) score -= ply; 
      else if(score < -IS_MATE) score += ply; 

      switch(t_table.t_entry[index].flag) 
      { 
       case TFALPHA: 
       { 
        if(score <= alpha) 
        { 
         score = alpha; 
         return 1; 
        } 
       } 
       case TFBETA: 
       { 
        if(score >= beta) 
        { 
         score = beta; 
         return 1; 
        } 
       } 
       case TFEXACT: 
       { 
        return 1; 
       } 
       default: assert(false); // At least one flag must be set. 
      } 
     } 
    } 

    return 0; 
} 

Во время альфа-бета поиска, таблица зондировании (записи хранятся правильно, а также, в зависимости от обрезаний и такие):

if(probe_table(board.t_table, board.ply, board.hash_key, depth, pv_move, 
    score, alpha, beta)) 
{ 
    return score; 
} 

Редактирование: Для полноты здесь приведен фрагмент важных бит кода хранения в поиске:

if(score > alpha) // Alpha cutoff. 
{ 
    if(score >= beta) // Beta cutoff. 
    { 
     ... 

     store_entry(board.t_table, board.ply, board.hash_key, best_move, 
        beta, depth, TFBETA); 

     return beta; 
    } 

    alpha = score; 

    ... 
    } 
} 

... 

assert(alpha >= old_alpha); 

if(alpha != old_alpha) 
{ 
    store_entry(board.t_table, board.ply, board.hash_key, best_move, 
     best_score, depth, TFEXACT); 
} 
else 
{ 
    store_entry(board.t_table, board.ply, board.hash_key, best_move, 
     alpha, depth, TFALPHA); 
} 

Я рассмотрел и исследовал сбои в любой другой части двигателя, и никто не происходит. Прекрасно отключает тестирование (но все еще использует таблицу для хранения PV-линий).

Я также подумал, влияет ли мое наивное осуществление размышлений на вещи. Чтобы поразмыслить, я просто распечатываю bestmove xxxx ponder xxxx в UCI всякий раз, когда доступно движение размышлений. Если размышление включено, графический интерфейс позволяет движку задуматься (что является обычным поиском, но позже будет использоваться с помощью таблицы транспонирования).

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

И здесь мне нужен кто-то, кто столкнулся с этим прежде, чем указал мне в общем направлении.

Редактировать: Как спросил grek40, вот пример, который просто проходил (двигатель белый, белый двигаться):

Position

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

Анализируя эту позицию с за столом транспозиции заполненных во время игры:

info score cp -425 depth 1 nodes 1 time 0 pv e1d1 
info score cp -425 depth 2 nodes 2 time 0 pv e1d1 
info score cp -425 depth 3 nodes 3 time 0 pv e1d1 
info score cp -425 depth 4 nodes 4 time 0 pv e1d1 
info score cp -425 depth 5 nodes 5 time 0 pv e1d1 
info score cp -425 depth 6 nodes 6 time 0 pv e1d1 
info score cp -425 depth 7 nodes 7 time 0 pv e1d1 
info score cp -425 depth 8 nodes 8 time 0 pv e1d1 
info score cp -425 depth 9 nodes 9 time 0 pv e1d1 
info score cp -425 depth 10 nodes 10 time 0 pv e1d1 
info score cp -440 depth 11 nodes 10285162 time 3673 pv f5f8 e7f8 b2b3 b7b5 e1c1 d6d5 a3a4 
info score cp -440 depth 12 nodes 29407669 time 10845 pv f5f8 e7f8 e1f1 f8e7 f1f5 d6d5 
bestmove f5f8 ponder e7f8 

еще раз Анализируя без транспозиции таблицы (на самом деле, с таблицей, но очищается):

info score cp -415 depth 1 nodes 82 time 0 pv f5f8 
info score cp -415 depth 2 nodes 200 time 0 pv f5f8 e7f8 
info score cp -405 depth 3 nodes 900 time 0 pv f5f8 e7f8 b2b3 
info score cp -425 depth 4 nodes 2936 time 1 pv f5f8 e7f8 b2b3 f8e7 
info score cp -415 depth 5 nodes 10988 time 4 pv f5f8 e7f8 b2b3 b7b5 e1d1 
info score cp -425 depth 6 nodes 65686 time 25 pv f5f8 e7f8 e1f1 d7e7 f1d1 b7b5 
info score cp -420 depth 7 nodes 194124 time 76 pv f5f8 e7f8 b2b3 b7b5 e1f1 f8e7 f1f7 
info score cp -425 depth 8 nodes 357753 time 141 pv f5f8 e7f8 b2b3 b7b5 e1f1 f8e7 f1f5 d7c7 
info score cp -425 depth 9 nodes 779686 time 292 pv f5f8 e7f8 e1f1 f8e7 f1f5 h4h8 
info score cp -425 depth 10 nodes 1484178 time 560 pv f5f8 e7f8 e1f1 f8e7 f1f5 h4h8 
info score cp -435 depth 11 nodes 29481132 time 11117 pv f5f8 d6d5 e1e5 
info score cp -435 depth 12 nodes 106448053 time 41083 pv f5f8 e7f8 
bestmove f5f8 ponder e7f8 

Следует отметить, что на глубине 10, оценка точна из поиска таблицы транспозиции.

Редактировать: Данная позиция была проанализирована после игры. Во время игры у двигателя не было достаточно времени для завершения поиска на более высокую глубину, в результате чего он играл e1d1, что смешно.

Зачем это произошло? Двигатель обнаруживает лучший ход с глубины 1, но из таблицы транспозиций был найден другой ход. Я также задаюсь вопросом, почему нет линии PV в поиске таблицы транспозиции.

Моя догадка бы поиск Нестабильность цитата из сайта Брюса Морланда здесь: Ключ Зобрист не принимает во внимание тот путь, чтобы добраться до узла. Не каждый путь один и тот же. Возможно, что оценка в элементе хэша может быть основана на пути, который будет содержать повторение, если он встречается в какой-либо другой точке дерева. Повторение может привести к оценке ничьей или, по крайней мере, другому результату.

Редактировать: Я попытался отключить хранение таблицы, когда нет значения TFEXACT. То есть я прекратил хранить и извлекать значения TFALPHA/TFBETA, и он прекрасно работает. Кто-нибудь знает, почему?

+0

Можете ли вы воспроизвести свою проблему с детерминированной последовательностью ходов? – grek40

+0

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

+0

@ grek40 Добавлен пример, который только что состоялся. –

ответ

0

Я заметил, что вы настройки ваш (передача по ссылке) pv_moveперед тем проверки того, выполняется ли ваш критерий глубины. Это означает, что probe_table может меняться pv_move и все еще возвращается 0 (без удара) (и не установка score). Это похоже на неудачу?

+0

Если запись хеш-таблицы существует, это линия PV, и ее первым элементом является движение PV, независимо от глубины. Движение PV оценивается выше как движение и обыскивается сначала Альфа-Бета. Если движение окажется плохим, оно просто движется нормально. Но, если запись существует и выполняется критерий глубины, соответствующий балл устанавливается с использованием флага, а затем возвращается функция probe_table(). Затем Alpha-Beta видит, что критерий глубины был удовлетворен и возвращает результат для позиции без поиска. TL; DR Это по дизайну. –

+0

ОК, спасибо. И спасибо за включение ссылки Morland и вашего кода. Сравнивая ваш код с кодом сайта, он также выглядит так, как Morland никогда не записывает хэш-код TFALPHA * внутри * цикла перемещения, только после этого, таким образом, max alpha. Кажется, вы сохраняете всякий раз, когда 'alpha == oldalpha', * внутри *. Не знаю, может ли это также иметь значение ... –

+0

TFALPHA не записывается внутри цикла перемещения. TFBETA находится внутри цикла перемещения всякий раз, когда происходит прерывание бета. Я планирую попытаться удалить записи обрезания альфа/бета и попробовать с помощью только записей TFEXACT и посмотреть, не изменилось ли это. –

Смежные вопросы