【问题标题】:Minimax function not returning correct resultsMinimax 函数没有返回正确的结果
【发布时间】:2020-10-21 21:07:01
【问题描述】:

我制作了一个 tic tac toe discord 机器人来和(对抗)我玩 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。)但是什么我需要一点帮助是为什么它认为阻止或获胜与失败一样值得。这是它返回的值的屏幕截图。

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

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

哦,这是我遇到的问题的代码 sn-p。如果你把这个 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;
    }

【问题讨论】:

  • 请做一个可运行的例子,否则很难帮你找到你的逻辑缺陷
  • @IvanD 这是一个不和谐的机器人,我不知道如何制作一个可运行的示例,因为它只是发送消息和东西。我会尝试举个例子,但可能需要一点时间 =)
  • 例如模拟输出到console.logs :)
  • @IvanD 完成了 sn-p。

标签: javascript tic-tac-toe minimax


【解决方案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 次移动,现在由机器人根据给定标准确定下一个最佳移动

【讨论】:

  • 哇!这很好用。我没有做这样的事情的唯一原因是因为我的机器人(如果逻辑有效)会尝试建立一个获胜的局面,因为它会查看所有可能的动作,而不仅仅是当前的棋盘。这是绝对可行的,我只是希望 AI 在没有它可以获胜的情况下不要放置“随机”动作。
  • @Glitch752 你总是可以先使用它,如果你可以基于防止损失之外的逻辑做出更明智的举动,让它在最后返回 false
  • 是的,确实如此。我会尽力做到这一点。当然,这可能会弄乱机器人试图做的事情,但它肯定会比我目前的 AI 工作得更好;)
  • @Glitch752 - 如果答案对您有帮助,请务必点赞,如果真的有帮助,请接受 :) 我确实喜欢这个小练习,绝对让我的创意源源不断
猜你喜欢
  • 2021-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-14
  • 2014-09-13
  • 2012-08-01
  • 2014-05-10
  • 1970-01-01
相关资源
最近更新 更多