У меня вопрос о TTalphabeta. В статье MTD-f (https://people.csail.mit.edu/plaat/mtdf.html) реализация альфа-бета сильно отличается от других, которые я обнаружил (https://homepages.cwi.nl/~paulk/theses/Carolus.pdf)
В чем разница между ними?
Моя эвристика возвращает int, поэтому нет десятичных знаков, следует ли мне проявлять особую осторожность?
Моя реализация знает:
template <class node, class transposition_table>
bound_and_action<node> alpha_beta_with_memory(node& root, depth depth,
bound alpha, bound beta, transposition_table& table)
{
auto value_in_hash = table.find(root);
if (value_in_hash != table.end()
&& value_in_hash->second._depth > depth) { // Transposition table lookup
auto bound_in_hash = value_in_hash->second;
if ( bound_in_hash.lower_bound >= beta )
return { bound_in_hash.lower_bound, root.get_action() };
if ( bound_in_hash.upper_bound <= alpha )
return { bound_in_hash.upper_bound, root.get_action() };
alpha = std::max(alpha, bound_in_hash.lower_bound);
beta = std::min(beta, bound_in_hash.upper_bound);
}
bound_and_action<node> ret;
if ( depth == 0 || root.is_terminal() ) { // Leaf Node
ret._bound = root.get_heuristic_value();
ret._action = root.get_action();
} else {
list<node> children;
root.get_children(children);
if (root.is_max_node()) {
ret._bound = INT_MIN;
bound a = alpha; //Save original alpha
for (auto child : children) {
bound_and_action<node> possible_ret = alpha_beta_with_memory(child, depth - 1,
a, beta, table);
if (possible_ret._bound == 1000) {
return {1000, root.get_action()};
}
if (possible_ret._bound > ret._bound ) {
ret._bound = possible_ret._bound;
ret._action = child.get_action();
}
a = std::max(a, ret._bound);
if ( beta <= ret._bound ) {
break; // beta cut-off
}
}
} else { // if root is a min node.
ret._bound = INT_MAX;
bound b = beta; //Save original beta
for (auto child : children) {
bound_and_action <node> possible_ret = alpha_beta_with_memory(child, depth - 1,
alpha, b, table);
if (possible_ret._bound == 1000) {
return {1000, root.get_action()};
}
if (possible_ret._bound < ret._bound) {
ret._bound = possible_ret._bound;
ret._action = child.get_action();
}
b = std::min(b, ret._bound);
if ( ret._bound <= alpha ) {
break; // alpha cut-off
}
}
}
}
//
// ----- Transposition table storing of bounds.
hash_struct& hash_value = table[root];
if (hash_value._depth < depth) {
// Fail low result implies an upper bound.
if (ret._bound <= alpha) {
hash_value.upper_bound = ret._bound;
}
// Found an accurate minimax value - will not occur if called with zero window.
if ( ret._bound > alpha && ret._bound < beta){
hash_value.lower_bound = ret._bound;
hash_value.upper_bound = ret._bound;
}
// Fail high result implies a lower bound.
if (ret._bound >= beta ) {
hash_value.lower_bound = ret._bound;
}
hash_value._depth = depth;
hash_value._action = ret._action;
}
return ret;
}
Но это работает не так хорошо, как должно. Все в порядке, если TT не используется, поэтому что-то в его использовании должно быть неправильно.
При использовании нулевого окна значение никогда не находится между альфа и бета, поэтому мне не нужны обе границы, это то, что говорится в исходной статье. / * Найдено точное минимаксное значение - не произойдет при вызове с нулевым окном * / если g> alpha и g <beta, то n.lowerbound: = g; n. верхний предел: = g; хранить n.lowerbound, n.upperbound;
Но ты делаешь. Нулевые окна будут все время перемещаться вперед и назад, поэтому, если у вас нет двух границ в TT, многие поиски не будут полезны.
Понятно, спасибо за ответ. Значит, реализация во второй ссылке неверна?
Я правда не знаю. Если вам интересно, вы можете проверить публикации Дона Дэйли. Он провел некоторое исследование адаптированных версий mtd-f.
Я уже изменил алгоритм, чтобы иметь 2 границы, если моя эвристика дискретна, нужно ли мне изменять какое-либо сравнение, такое как то, которое я сказал ранее?
Я экспериментировал с MTD (f), когда диссертация Аске была новой, около 20 (!) Лет назад. Я действительно не помню подробностей на этом уровне. :-)





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