【问题标题】:Othello Minimax Algorithm Doesn't Work In React.js奥赛罗极小极大算法在 React.js 中不起作用
【发布时间】:2021-08-02 23:35:39
【问题描述】:

我遇到了一个问题,即 AI 玩家总是先玩它遇到的第一个可用动作。我试图让 AI 与 Minimax 算法一起工作。你觉得我能怎么解决?

这是代码(带解释):

展示代码前的一点见解:

  • 我试图以这样一种方式进行设置,即 AI 播放器(无论它是什么颜色)是最大化播放器。所以如果玩家回合是白人,但白人是AI玩家,那么isMaximizing = true。
  • 我想指出这一点,这样玩家在颜色方面的回合和玩家在最大化方面的回合不会混淆。

我最初使用它的地方:

// AI Hard - Return Minimax
function minimaxAI(){
    // Here's where the minimax function is initially called

    const AI = (ourPlayer === "white") ? "black" : "white"
    const squares = history[history.length - 1].slice()

    // AI is the maximizing player so we get -Infinity, it'll try to increase the value of bestScore
    let bestScore = -Infinity
    let bestMove = null
    // Perform minimax algorithm for each valid move and pick the best score
    
    // Get the valid moves for the AI player
    const AIValidMoves = checkSquaresForSides()[(AI === "black") ? 0 : 1 ]
    for (var AIMove=0;AIMove<AIValidMoves.length;AIMove++){

        // Play the AI moves and generate the new board
        const crds = AIValidMoves[AIMove].turned
        crds.unshift(AIValidMoves[AIMove].coordinates)
        const newBoard = handleMove(crds, squares,AI,false,false)

        // Check minimax for the new board
        const score = minimax(newBoard,4,false)
        
        // If score is higher than the current one, replace the highest score and the according coordinates
        if (score > bestScore || AIMove === 0) {
            bestScore = score
            bestMove = crds
        }
    }
    console.log('Final Value:')
    console.log(bestScore)

    // Play the move(this time on the real board)
    const upcomingAI = handleMove(bestMove)

    // Set it as the latest of the collection of squares
    setHistory(upcomingAI)

    // Useless to know for this issue
    setStepNumber(upcomingAI.length - 1)
}

极小极大算法本身:

function minimax(board, depth, isMaximizing) {
    const AI = (ourPlayer === "white") ? "black" : "white"
    const stones = setStoneCount(board)
    
    // If there's a winner, return Infinity, 0 or -Infinity depending on whether AI won, it is a tie or our player won
    if (stones[0] === 0 || stones[1] === 0 || stones[0] + stones[1] === 64) {
        // Return the score of the move if the game is over
        let score = (stones[(AI === "black") ? 1 : 0] === 0) ? Infinity : (stones[stones[0] === stones[1]]) ? 0 : -Infinity
        return score
    } else if (depth === 0) {
        // If the maximal depth is reached, then evaluate the current board by counting all the stones for the both sides and subtracting one from the other appropriately
        const squares = setStoneCount(board)
        let score = 0
        if (AI === "black") {
            score = squares[0] - squares[1]
        } else if (AI === "white") {
            score = squares[1] - squares[0]
        }
        return score
    }
    if (isMaximizing) {
        // Perform minimax if depth !== 0 and isMaximizing, just like we did initially
        let bestScore = -Infinity
        
        // Get the valid moves for the maximizing player
        const AIValidMoves = checkSquaresForSides()[(AI === "black") ? 0 : 1 ] // AI Moves because isMaximizing = true
        for (var AIMove=0;AIMove<AIValidMoves.length;AIMove++){
            const crds = AIValidMoves[AIMove].turned
            crds.unshift(AIValidMoves[AIMove].coordinates)
            const newBoard = handleMove(crds,board,AI,false,false)
            // check for isMaximizing
            let maximizingNext = !isMaximizing
            
            // If there's no move to make for the one side, player turn might stay the same. Here, I check for those situations
            const available = checkSquaresForSides(newBoard)
            if (available[0].length === 0) {
                if (AI === "white") {
                    maximizingNext = true
                } else {
                    maximizingNext = false
                }
            } else if (available[1].length === 0){
                if (AI === "black") {
                    maximizingNext = true
                } else {
                    maximizingNext = false
                }
            }
            
            // Perform minimax for the new board(depth is one less)
            const score = minimax(newBoard,depth - 1,maximizingNext)
            
            // If current score is higher than the highest one caught yet, it should be replaced
            if (score > bestScore) {
                bestScore = score
            }
        }
        return bestScore
    } else {
        // Perform minimax if depth !== 0 and isMaximizing
        let bestScore = Infinity

        // Get the valid squares for the minimizing player
        const PlayerValidMoves = checkSquaresForSides()[(AI === "black") ? 1 : 0 ] // Player Moves because isMaximizing = false

        // Play each move one-by-one for the minimizing player
        for (var playerMove=0;playerMove<PlayerValidMoves.length;playerMove++) {
            const crds = PlayerValidMoves[playerMove].turned
            crds.unshift(PlayerValidMoves[playerMove].coordinates)
            const newBoard = handleMove(crds,board,ourPlayer,false,false)
            // check for isMaximizing
            let maximizingNext = !isMaximizing
            
            // Just like the previous one, check whether there's an exceptional situation to be considered with the player turn
            const available = checkSquaresForSides(newBoard)
            if (available[0].length === 0) {
                if (AI === "white") {
                    maximizingNext = true
                } else {
                    maximizingNext = false
                }
            } else if (available[1].length === 0){
                if (AI === "black") {
                    maximizingNext = true
                } else {
                    maximizingNext = false
                }
            }
            
            // Perform minimax for the new board
            const score = minimax(newBoard,depth - 1,maximizingNext)

            // If the current score is lower than the lowest one obtained so far, it should be replaced
            if (score < bestScore) {
                bestScore = score
            }
        }
        return bestScore
    }
}

它最初确实有效,但是当我对达到深度时评估板的方式不满意时。我改了,现在不行了。我不想回到前一个,因为它不准确。但是,为什么 AI 玩家会先走它遇到的第一步,但没有正确评估呢?

【问题讨论】:

    标签: javascript reactjs minimax


    【解决方案1】:

    我解决了,只是将板子评估部分改成这样:

    // The initial score
    let score = 0
    // value board for each square
    const sq_val = [
        [160, -20,  20,   5,   5,  20, -20, 160],    
        [-20, -40,  -5,  -5,  -5,  -5, -40, -20],
        [20,  -5,  15,   3,   3,  15,  -5,  20],
        [5,  -5,   3,   3,   3,   3,  -5,   5],
        [5,  -5,   3,   3,   3,   3,  -5,   5],
        [20,  -5,  15,   3,   3,  15,  -5,  20], 
        [-20, -40,  -5,  -5,  -5,  -5, -40, -20],   
        [160, -20,  20,   5,   5,  20, -20, 160]
    ]
    
    // Our stones' positions are looked for and the according values are added or substracted depending on whether the player found is the maximizing player or the minimizing player(AI Player: Maximizing, Our Player: Minimizing)
    for (var row=0;row<8;row++)
        for (var col=0;col<8;col++) {
            if (board[row][col] === AI) {
                score += sq_val[row][col]
            } else if (board[row][col] === ourPlayer) {
                score -= sq_val[row][col]
            }
        }
    }
    // The score is returned
    return score
    

    顺便说一下,算法现在已经设置好了,但它不是那么准确,因为我需要增加它的深度;而且要做到这一点,我必须首先实现 alpha-beta 修剪,这样我们的计算机就不会过热。另外,使函数异步在性能方面会更好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-06
      • 2016-01-24
      • 2019-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多