Алгоритм шахматного минимакса всегда возвращает первый ход - PullRequest
0 голосов
/ 27 апреля 2020

Я выполнил все правила игры в шахматы и проверил все соответствующие функции, такие как генерация хода и определение состояния терминала 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, с отсечкой нулевого хода)

...