Search code examples
javascriptreactjsminimax

I'm working on a Minimax Algorithm for my reversi game(w/ react.js) but it gives RangeError


I'm working on a Minimax Algorithm for my reversi game so that I'll have a strong AI opponent facing the player. But I bumped into this error: "RangeError: Maximum call stack size exceeded"

What can I do to fix it?

Here's the code(I won't explain it with pseudo code since my question is not about the function not working):

AI Algorithm:

function minimaxAI(){
        const squares = history[history.length - 1].slice()
        let bestScore = -Infinity
        let bestMove = null
        // Perform minimax algorithm for each valid move and pick the best score
        const AI = (ourPlayer === "white") ? "black" : "white"
        const AIValidMoves = checkSquaresForSides()[(AI === "black") ? 0 : 1 ]
        console.log(AIValidMoves)
        for (var AIMove=0;AIMove<AIValidMoves.length;AIMove++){
            const crds = AIValidMoves[AIMove].turned
            crds.unshift(AIValidMoves[AIMove].coordinates)
            const newBoard = handleMove(crds, squares)
            const score = minimax(newBoard,5,(AI === "black") ? false : true)
            if (score > bestScore) {
                bestScore = score
                bestMove = crds
            }
        }
        const handling = bestMove
        const upcomingAI = handleMove(handling)
        setHistory(upcomingAI)
        setStepNumber(upcomingAI.length - 1)
    }

Minimax Algorithm:

function minimax(board, depth, isMaximizing){
        const AI = (ourPlayer === "white") ? "BLACK" : "WHITE"
        let result = setWinnerAndTurn(true)
        // Retrieve the filled squares of the current board
        let currentStones = 0
        history[history.length - 1].slice().map((row, y) =>
            row.map((square, x) =>
                currentStones += (square) ? 1 : 0
            )
        );
        let newStones = 0
        // Retrieve the filled squares of the updated board
        board.map((row, y) =>
            row.map((square, x) =>
                newStones += (square) ? 1 : 0
            )
        );
        console.log(currentStones)
        console.log(newStones)
        if (result.winner || newStones - currentStones === depth) {
            // Get the evaluated version of the last move
            let score = (result.winner === AI) ? Infinity : -Infinity
            return score
        }
        if (isMaximizing){
            // Play the optimal move for the opposing player
            let bestScore = -Infinity
            let bestMove = null
            const selection = (AI === "BLACK") ? 0 : 1 
            const AIValidMoves = checkSquaresForSides()[selection]
            console.log(AIValidMoves)
            for (var AIMove=0;AIMove<AIValidMoves.length;AIMove++){
                const crds = AIValidMoves[AIMove].turned
                crds.unshift(AIValidMoves[AIMove].coordinates)
                const newBoard = handleMove(crds, board)
                const score = minimax(newBoard,10,(selection === 0) ? false : true)
                if (score > bestScore) {
                    bestScore = score
                    bestMove = crds
                }
            }
            return bestScore
        } else {
            // Play the optimal move for the maximizing player
            let bestScore = Infinity
            let bestMove = null
            const selection = (AI === "BLACK") ? 1 : 0 
            const AIValidMoves = checkSquaresForSides()[selection]
            console.log(AIValidMoves)
            for (var AIMove=0;AIMove<AIValidMoves.length;AIMove++){
                const crds = AIValidMoves[AIMove].turned
                crds.unshift(AIValidMoves[AIMove].coordinates)
                const newBoard = handleMove(crds, board)
                const score = minimax(newBoard,10,(selection === 1) ? false : true)
                if (score < bestScore) {
                    bestScore = score
                    bestMove = crds
                }
            }
            return bestScore
        }
    }

Solution

  • By the way, I solved the problem 1h ago(posting it now). I forgot to reduce the depth value with every step taken towards the root of the tree. Just changed these parts:

    1st:

    if (result.winner) {
        // Get the evaluated version of the last move
        let score = (result.winner === AI) ? Infinity : -Infinity
        return score
    } else if (depth === 0) {
        return 0
    }
    

    2nd:

    const score = minimax(newBoard,depth - 1,(selection === 1) ? false : true)
    

    Otherwise the function would be stuck in an infinitive move

    And also, you can remove this:

    let currentStones = 0
    history[history.length - 1].slice().map((row, y) =>
        row.map((square, x) =>
        currentStones += (square) ? 1 : 0
        )
    );
    let newStones = 0
    // Retrieve the filled squares of the updated board
    board.map((row, y) =>
        row.map((square, x) =>
            newStones += (square) ? 1 : 0
        )
    );