Minimax函数未返回正确结果

时间:2020-10-21 21:07:01

标签: javascript tic-tac-toe minimax

我制作了一个tic tac toe discord bot,与(对)我玩tic tac toe。该算法效果很好,但是即使我尝试实现它,也似乎几乎不会阻止我或取得胜利。我已经尝试了很多调试,但是我真的不认为有任何错误,我只是觉得我的逻辑有些偏离。这是我的MiniMax函数。

function MINIMAX(Board, Depth, MaximizingScore, CTS) {
        var value = 0;
        var score = EvaluateBoard(Board);
        if (score == 10) {
           return score; 
        }

            // If Minimizer has won the game  
            // return his/her evaluated score 
            if (score == -10) {
            return score; 
        }
        if(IsTerminalState(Board) && CTS) {
        return 0;
        }

        var PossibleMoves = FindPossibleMoves(Board);
        if(MaximizingScore) {
        //When AI is calculated
        var BestVal = -1000;
        for(var i = 0; i < PossibleMoves.length; i++) {
            var Ii = PossibleMoves[i];
            Board[Ii] = "X";
            BestVal = Math.max(BestVal, MINIMAX(Board, Depth + 1, !MaximizingScore, true));
            Board[Ii] = "N"
        }
        return BestVal;
        } else {
        //When Player is calculated
        var BestVal = 1000;
        for(var i = 0; i < PossibleMoves.length; i++) {
            var Ii = PossibleMoves[i];
            Board[Ii] = "O";
            BestVal = Math.min(BestVal, MINIMAX(Board, Depth + 1, !MaximizingScore, true));
            Board[Ii] = "N"
        }
        return BestVal;
        }
}

我使用类似的功能来找到最佳的移动方式。在这里:

function FindBestMove(Board) {
        var bestVal = -1000;
        var bestMove = 0;
        var PossibleMoves = FindPossibleMoves(Board);
        for(var i = 0; i < PossibleMoves.length; i++) {
        var Ii = PossibleMoves[i];
        Board[Ii] = "X";
        moveVal = MINIMAX(Board, 0, false, false);
        console.log(moveVal);
        Board[Ii] = "N"
        if(moveVal > bestVal) {
            bestMove = Ii;
            bestVal = moveVal;
        }
        }
        return bestMove;
    }

我看过多个网站,它们似乎都使用相同的算法。我不知道我的逻辑有什么问题,但是我做了一些调试,发现Minimax几乎总是返回-10。我认为问题在于,它为每个值发送-10,它只是选择它可以放入的第一个空格(因为由于-10!> -10而不能将最大值设置得更高),但是我需要一点帮助的是为什么它认为阻止或赢得与失去一样有价值。这是返回值的屏幕截图。

enter image description here

它偶尔会返回0,但不是很频繁。

我并不是完全在寻找代码,我只是想看看我的算法出了什么问题。抱歉,如果这篇文章有点不好,我只是不确定到底出了什么问题。我已经调试了很长时间,以至于我不知道该怎么办。谢谢=)

哦,这是我遇到的问题的代码片段。如果将AI置于几乎任何这种情况下,我们都会得到相同的结果。

console.log(FindBestMove(["O", "X", "N", "X", "X", "O", "N", "O", "X"]));//2 would be the 3nd element in the array. This would not be ideal because we would want to block X in this case, so this results in a loose.
function calculateWinX(gamearr) {
            //HORIZONTAL
            if(gamearr[0]=="X" && gamearr[1]=="X" && gamearr[2]=="X") {return true}
            if(gamearr[3]=="X" && gamearr[4]=="X" && gamearr[5]=="X") {return true}
            if(gamearr[6]=="X" && gamearr[7]=="X" && gamearr[8]=="X") {return true}
            
            //VERTICAL
            if(gamearr[2]=="X" && gamearr[5]=="X" && gamearr[8]=="X") {return true}
            if(gamearr[1]=="X" && gamearr[4]=="X" && gamearr[7]=="X") {return true}
            if(gamearr[0]=="X" && gamearr[3]=="X" && gamearr[6]=="X") {return true}
            
            //DIAGONAL
            if(gamearr[0]=="X" && gamearr[4]=="X" && gamearr[8]=="X") {return true}
            if(gamearr[6]=="X" && gamearr[4]=="X" && gamearr[2]=="X") {return true}
            return false;
        }
        function calculateWinO(gamearr) {
            //HORIZONTAL
            if(gamearr[0]=="O" && gamearr[1]=="O" && gamearr[2]=="O") {return true}
            if(gamearr[3]=="O" && gamearr[4]=="O" && gamearr[5]=="O") {return true}
            if(gamearr[6]=="O" && gamearr[7]=="O" && gamearr[8]=="O") {return true}
            
            //VERTICAL
            if(gamearr[2]=="O" && gamearr[5]=="O" && gamearr[8]=="O") {return true}
            if(gamearr[1]=="O" && gamearr[4]=="O" && gamearr[7]=="O") {return true}
            if(gamearr[0]=="O" && gamearr[3]=="O" && gamearr[6]=="O") {return true}
            
            //DIAGONAL
            if(gamearr[0]=="O" && gamearr[4]=="O" && gamearr[8]=="O") {return true}
            if(gamearr[6]=="O" && gamearr[4]=="O" && gamearr[2]=="O") {return true}
            return false;
        }
    function IsTerminalState(gamearr) {
        return !gamearr.includes("N");
    }
    function FindPossibleMoves(gamearr) {
        var PossibleMoves = [];
        for(var i = 0; i < 9; i++) {
            if(gamearr[i]==="N") {
                PossibleMoves.push(i);
            }
        }
        return PossibleMoves;
    }
    function EvaluateBoard(gamearr) {
        if(calculateWinO(gamearr)) {
        return 10;
        }
        if(calculateWinX(gamearr)) {
        return -10;
        }
    }
        function MINIMAX(Board, Depth, MaximizingScore, CTS) {
        var value = 0;
        var score = EvaluateBoard(Board);
        if (score == 10) {
           return score; 
        }

            // If Minimizer has won the game  
            // return his/her evaluated score 
            if (score == -10) {
            return score; 
        }
        if(IsTerminalState(Board) && CTS) {
        return 0;
        }

        var PossibleMoves = FindPossibleMoves(Board);
        if(MaximizingScore) {
        //When AI is calculated
        var BestVal = -1000;
        for(var i = 0; i < PossibleMoves.length; i++) {
            var Ii = PossibleMoves[i];
            Board[Ii] = "X";
            BestVal = Math.max(BestVal, MINIMAX(Board, Depth + 1, !MaximizingScore, true));
            Board[Ii] = "N"
        }
        return BestVal;
        } else {
        //When Player is calculated
        var BestVal = 1000;
        for(var i = 0; i < PossibleMoves.length; i++) {
            var Ii = PossibleMoves[i];
            Board[Ii] = "O";
            BestVal = Math.min(BestVal, MINIMAX(Board, Depth + 1, !MaximizingScore, true));
            Board[Ii] = "N"
        }
        return BestVal;
        }
        }
    function FindBestMove(Board) {
        var bestVal = -1000;
        var bestMove = 0;
        var PossibleMoves = FindPossibleMoves(Board);
        for(var i = 0; i < PossibleMoves.length; i++) {
        var Ii = PossibleMoves[i];
        Board[Ii] = "X";
        moveVal = MINIMAX(Board, 0, false, false);
        console.log(moveVal);
        Board[Ii] = "N"
        if(moveVal > bestVal) {
            bestMove = Ii;
            bestVal = moveVal;
        }
        }
        return bestMove;
    }

1 个答案:

答案 0 :(得分:1)

我创建了一种简单但有效的方法来计算机器人要执行的下一个最佳动作:

const checkBlank = (vals) => {
  const v = (vals.filter(val => !val));
  
  if (v.length !== 0) {
    return vals.indexOf(v[0]);
  }
  
  return v;
}


const winningConditions = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6]
];


const board = [
  '', 'O', '',
  '', 'X', '',
  'X', '', '',
];


const previousMove = 6;
const botCharacter = 'O';
const playerCharacter = 'X';


function bestNextMove() {
  let randomBlank = 0;
  
  for (let i = 0; i < winningConditions.length; i++) {
    const winningCondition = winningConditions[i];
    
    if (! winningCondition.includes(previousMove)) {
      continue;
    }
    
    let counter = 0;
    
    const [firstPos, secondPos, thirdPos] = winningCondition;
    
    const boardPos1 = board[firstPos], 
          boardPos2 = board[secondPos], 
          boardPos3 = board[thirdPos];
    
    const blankCheck = checkBlank([boardPos1, boardPos2, boardPos3]);
    
    randomBlank = blankCheck !== false ? winningCondition[blankCheck] : randomBlank;
    
    const str = boardPos1 + boardPos2 + boardPos3;
    
    if (str === playerCharacter + playerCharacter && blankCheck !== false) {
      return randomBlank;
    }
  }
  
  return randomBlank;
}

console.log(bestNextMove());

说明

我正在检查是否存在获胜条件,玩家可以在下一回合中获胜,然后返回阻止该获胜举动所需的适当坐标。

bestNextMove也将返回有效的起始举动,如果尚未进行任何举动,或者不存在玩家获胜的条件。

我的示例包含一个示例板,其中已经进行了3次动作,现在可以根据给定的标准在机器人上确定下一个最佳动作