Я выполнил все правила игры в шахматы и проверил все соответствующие функции, такие как генерация хода и определение состояния терминала ie: dr aws, и контр-маты. Однако после того, как я написал функцию минимакса, с базовым c упорядочением ходов и альфа-бета-отсечкой, он всегда будет возвращать первый ход из поколения ходов. Кроме того, при тестировании с матом в 1 позиции, с небольшим количеством кусков, с большим количеством кусков, кажется, что это займет вечность (может быть cra sh или бесконечное l oop). Этого не могло произойти из-за проблем со скоростью, так как, когда я тестировал производительность всех функций, я мог вызывать их от 60 до 200 тысяч раз в секунду). Вот код:
let board = [
["C", "H", "B", "Q", "K", "B", "H", "C"],
["P", "P", "P", "P", "P", "P", "P", "P"],
[" ", " ", " ", " ", " ", " ", " ", " "],
[" ", " ", " ", " ", " ", " ", " ", " "],
[" ", " ", " ", " ", " ", " ", " ", " "],
[" ", " ", " ", " ", " ", " ", " ", " "],
["p", "p", "p", "p", "p", "p", "p", "p"],
["c", "h", "b", "q", "k", "b", "h", "c"]
];
function updateMovedCastlesAndKings(movedCastlesAndKings, board) {
return {
hasWKMoved: board[7][4] == " " || movedCastlesAndKings.hasWKMoved,
hasWKSCMoved: board[7][7] == " " || movedCastlesAndKings.hasWKSCMoved,
hasWQSCMoved: board[7][0] == " " || movedCastlesAndKings.hasWQSCMoved,
hasBKMoved: board[0][4] == " " || movedCastlesAndKings.hasBKMoved,
hasBKSCMoved: board[0][7] == " " || movedCastlesAndKings.hasBKSCMoved,
hasBQSCMoved: board[0][0] == " " || movedCastlesAndKings.hasBQSCMoved
};
}
function alphaBeta(board,
depth,
alpha,
beta,
color,
movedCastlesAndKings,
castlingPerms,
enpassantSquare,
halfMoveClock,
boardHistory,
isNullOK) {
if (color == 1) {
var type = "w";
var oppositeType = "b";
var indexInHistoryTable = 1;
} else {
var type = "b";
var oppositeType = "w";
var indexInHistoryTable = 0;
}
let kingPosition = findKing(board, type);
let moves = generateLegalMoves(enpassantSquare, castlingPerms, kingPosition, board, type);
let amountOfMoves = moves.length;
let isSideInCheck = isSquareUnderAttack(kingPosition, board, type);
let isTerminal = isTerminalNode(boardHistory, halfMoveClock, amountOfMoves, isSideInCheck, board);
let isEndgameState = isEndgame(board);
if (isTerminal || depth == 0) {
if (isTerminal) {
if (type == "w" && isCheckmate(amountOfMoves, isSideInCheck)) {
return {
score: color * -2000
};
}
if (type == "b" && isCheckmate(amountOfMoves, isSideInCheck)) {
return {
score: color * 2000
};
}
return {
score: 0
};
} else {
return {
score: color * evaluate(isEndgameState, board)
};
}
}
if (isNullOK && depth >= 3 && !isEndgameState && !isSideInCheck) {
color *= -1;
let value = -alphaBeta(board,
depth - 3,
-beta,
10 - beta,
color,
movedCastlesAndKings,
castlingPerms,
undefined,
halfMoveClock,
boardHistory,
false).score;
color *= -1;
if (value >= beta) {
return {
score: value
};
}
}
let bestValue = -Infinity;
let bestMove;
for (moveIndex = 0; moveIndex < amountOfMoves; moveIndex++) {
let moveItem = moves[moveIndex];
let nextNode = moveItem.node;
let isPawnMove = moveItem.isPawnMove;
let isCapture = moveItem.targetSQ;
let doublePawnMove = moveItem.doublePawnMove;
let updatedMovedCastlesAndKings = updateMovedCastlesAndKings(movedCastlesAndKings, nextNode);
let nextEnpassantSquare = getEnpassantSquare(doublePawnMove, nextNode, oppositeType);
let nextCastlingPerms = castlingPermissions(updatedMovedCastlesAndKings, nextNode);
let hashIndex = makeHashTableIndex(nextCastlingPerms, nextEnpassantSquare, nextNode, oppositeType);
let boardHistoryCopy = clone1DArray(boardHistory);
let nextHalfMoveClock = halfMoveClock;
boardHistoryCopy.push(hashIndex);
isPawnMove || isCapture ? nextHalfMoveClock = 0 : nextHalfMoveClock += 1;
let value = -alphaBeta(nextNode,
depth - 1,
-beta,
-alpha,
-color,
updatedMovedCastlesAndKings,
nextCastlingPerms,
nextEnpassantSquare,
nextHalfMoveClock,
boardHistoryCopy,
true).score;
if (value > bestValue) {
bestValue = value;
bestMove = moveItem;
}
alpha = Math.max(alpha, bestValue);
if (alpha >= beta) break;
}
if (!bestMove.targetSQ) {
let from = bestMove.from;
let to = bestMove.to;
historyTable[indexInHistoryTable][from.y][from.x][to.y][to.x] += depth ** 2;
}
return {
score: bestValue,
move: bestMove
};
}
Если вам нужны какие-либо разъяснения для улучшения вопроса, не стесняйтесь отвечать. (примечание: алгоритм поиска использует вариант минимакса, называемый negamax, с отсечкой нулевого хода)