Я пытаюсь реализовать минимакс с альфа-бета-обрезкой для игры с шашками в Java. Мой минимаксный алгоритм работает отлично. Мой код работает с кодом alpha-beta на месте. К сожалению, когда я играю 1000 игр против стандартного алгоритма минимакса, алгоритм альфа-бета всегда выходит за 50 игр или около того.
Так как обрезка альфа-бета не должна уменьшать качество ходов, просто время, необходимое для их достижения, что-то должно быть неправильным. Тем не менее, я вынул ручку и бумагу и нарисовал гипотетические значения листьев node и использовал свой алгоритм для прогнозирования, будет ли он рассчитывать правильный лучший ход, и, похоже, не существует логических ошибок. Я использовал дерево из этого видео: Alpha-Beta Обрезка для отслеживания моего алгоритма. Он логически должен принимать все одинаковые варианты и, следовательно, быть функциональной реализацией.
Я также поставил операторы печати в код (они были удалены, чтобы уменьшить беспорядок), и значения возвращаются правильно, и появляется обрезка. Несмотря на все мои усилия, я не смог найти, где лежит логическая ошибка. Это моя третья по-разному попытка реализовать это, и у всех из них была одна и та же проблема.
Я не могу опубликовать полный код здесь, это слишком долго, поэтому я включил методы, имеющие отношение к ошибке. Я не уверен, но я подозреваю, что проблема может быть в методе нерекурсивного перемещения(), хотя я не могу найти в нем логическую ошибку, поэтому я просто буду обманывать ее больше, возможно, делая вещи хуже, чем лучше, не имея рифмы или разума.
Есть ли трюк для восстановления нескольких целых значений из рекурсивных вызовов в цикле for? Он отлично работает с моими минимаксными и негамаксными реализациями, но обрезание альфа-бета, похоже, приводит к некоторым странным результатам.
@Override
public GameState move(GameState state)
{
int alpha = -INFINITY;
int beta = INFINITY;
int bestScore = -Integer.MAX_VALUE;
GameTreeNode gameTreeRoot = new GameTreeNode(state);
GameState bestMove = null;
for(GameTreeNode child: gameTreeRoot.getChildren())
{
if(bestMove == null)
{
bestMove = child.getState();
}
alpha = Math.max(alpha, miniMax(child, plyDepth - 1, alpha, beta));
if(alpha > bestScore)
{
bestMove = child.getState();
bestScore = alpha;
}
}
return bestMove;
}
private int miniMax(GameTreeNode currentNode, int depth, int alpha, int beta)
{
if(depth <= 0 || terminalNode(currentNode.getState()))
{
return getHeuristic(currentNode.getState());
}
if(currentNode.getState().getCurrentPlayer().equals(selfColor))
{
for(GameTreeNode child: currentNode.getChildren())
{
alpha = Math.max(alpha, miniMax(child, depth - 1, alpha, beta));
if(alpha >= beta)
{
return beta;
}
}
return alpha;
}
else
{
for(GameTreeNode child: currentNode.getChildren())
{
beta = Math.min(beta, miniMax(child, depth - 1, alpha, beta));
if(alpha >= beta)
{
return alpha;
}
}
return beta;
}
}
//Checks to see if the node is terminal
private boolean terminalNode(GameState state)
{
if(state.getStatus().equals(win) || state.getStatus().equals(lose) || state.getStatus().equals(draw))
{
return true;
}
else
{
return false;
}
}