【问题标题】:Minimax Algorithm JS implementingMinimax算法JS实现
【发布时间】:2016-09-19 20:03:57
【问题描述】:

在尝试编写井字游戏游戏时,我有一些 javascript 代码。
所以AI(人工智能)播放“X”,而“Human”播放器分别是“O”;
为了测试,我把一块板子放在
['e', 'e', 'o',
'x', 'o', 'e',
'e', 'e', 'e']

轮到AI移动了。所以很明显,AI(人工智能)的最佳举措是
['e', 'e', 'o',
'x', 'o', 'e',
'x'、'e'、'e']。

但它返回我
['x', 'e', 'o',
'x', 'o', 'e',
'e', 'e', 'e']
变体。
如果有好的提示,我将不胜感激,这将以正确的方式重定向我。
是的,我已经阅读了一周关于 Minimax 的文章。我个人将本教程用作原型http://blog.circuitsofimagination.com/2014/06/29/MiniMax-and-Tic-Tac-Toe.html
所以请看一下我的代码:

var board = ['e', 'e', 'o', 'x', 'o', 'e', 'e', 'e', 'e'];
var signPlayer = 'o';
var signAI = (signPlayer === 'x') ? 'o' : 'x';

//Circuits Of Imagination

game = {
    over: function(board) {
        for (var i = 0; i < board.length; i += 3) {
            if (board[i] === board[i + 1] && board[i + 1] === board[i + 2]) {
                return board[i] !== 'e' ? board[i] : false;
            }
        }
        for (var j = 0; j < board.length; j++) {
            if (board[j] === board[j + 3] && board[j + 3] === board[j + 6]) {
                return board[j] !== 'e' ? board[j] : false;
            }
        }
        if ((board[4] === board[0] && board[4] === board[8]) || 
        (board[4] === board[2] && board[4] === board[6])) {
            return board[4] !== 'e' ? board[4] : false;
        }
        var element;
        if (board.every(function(element) {
            return element !== 'e';
        })) {
            return true;
        }
    },
    winner: function(board) {
        return game.over(board);
    },
    possible_moves: function(board, sign) {
        var testBoard = [], 
        nextBoard;
        for (var i = 0; i < board.length; i++) {
            nextBoard = board.slice();
            if (nextBoard[i] === 'e') {
                nextBoard[i] = sign;
                testBoard.push(nextBoard);
            }
        }
        return testBoard;
    }
}

function score(board) {
    if (game.winner(board) === signPlayer) {
        return -10;
    } else if (game.winner(board) === signAI) {
        return +10;
    } else {
        return 0;
        //Game is a draw
    }
}

function max(board) {

    if (game.over(board)) {
        return score(board);
    }
    var newGame = [];
    var best_score = -10;
    var movesArray = game.possible_moves(board, signAI);

    for (var i = 0; i < movesArray.length; i++) {
        newGame = movesArray[i].slice();
        score = min(newGame);
        if (score > best_score) {
            best_score = score;
        }
        console.log('maxnewGame', newGame);
        return best_score;
    }
}

function min(board) {

    if (game.over(board)) {
        return score(board);
    }
    var newGame = [];
    var worst_score = 10;
    var movesArray = game.possible_moves(board, signPlayer);

    for (var i = 0; i < movesArray.length; i++) {
        newGame = movesArray[i].slice();
        score = max(newGame);
        if (score < worst_score) {
            worst_score = score;
        }
        console.log('minnewGame', newGame);
        return worst_score;
    }
}
max(board);

【问题讨论】:

  • 好的,这绝对是代码...您有问题吗?代码是否有效但不是您想要的方式?代码不起作用吗?控制台有错误吗?

标签: javascript tic-tac-toe minimax


【解决方案1】:

您的代码中有一些错误:

  1. 你在 for 循环中有return best/worst_score,它提前终止了搜索。让它脱离循环。
  2. score 在循环中被重新定义为一个数字,而它应该是一个函数。将其重命名为moveScore
  3. max/minnewGame 设置不正确。
  4. best/worse_score 初始化为 +/-10,这意味着有时找不到最大值。

固定代码如下。 但是请注意,X 仍然没有选择“明显”的移动。这实际上是按照您的极小极大算法的预期工作。这是由于算法假设对手处于最佳状态。在您给定的棋盘状态下,最佳对手将无法以 X 获胜。因此,算法“放弃”并选择第一个可能的动作。

这是 minmax 算法的最大特性之一:该算法只能做出与其对手一样好的决策。如果对手被最佳模拟,算法不会考虑错误的可能性而放弃。为了使算法能够选择你认为的明显动作,你必须让算法也考虑到直到输掉的回合,所以它会尝试在游戏后期输掉可能的(这可以通过给出失败的动作-10+turns 来完成,其中转数是失去的转数)。另一种方法是根据可能的游戏胜过状态来计算分数,并支持 X 获胜的更多可能状态的移动。

var board = ['e', 'e', 'o', 'x', 'o', 'e', 'e', 'e', 'e'];
var signPlayer = 'o';
var signAI = (signPlayer === 'x') ? 'o' : 'x';

//Circuits Of Imagination

game = {
  over: function(board) {
    for (var i = 0; i < board.length; i += 3) {
      if (board[i] === board[i + 1] && board[i + 1] === board[i + 2]) {
        return board[i] !== 'e' ? board[i] : false;
      }
    }
    for (var j = 0; j < board.length; j++) {
      if (board[j] === board[j + 3] && board[j + 3] === board[j + 6]) {
        return board[j] !== 'e' ? board[j] : false;
      }
    }
    if ((board[4] === board[0] && board[4] === board[8]) ||
      (board[4] === board[2] && board[4] === board[6])) {
      return board[4] !== 'e' ? board[4] : false;
    }
    var element;
    if (board.every(function(element) {
      return element !== 'e';
    })) {
      return true;
    }
  },
  winner: function(board) {
    return game.over(board);
  },
  possible_moves: function(board, sign) {
    var testBoard = [],
      nextBoard;
    for (var i = 0; i < board.length; i++) {
      nextBoard = board.slice();
      if (nextBoard[i] === 'e') {
        nextBoard[i] = sign;
        testBoard.push(nextBoard);
      }
    }
    return testBoard;
  }
}

function score(board) {
  if (game.winner(board) === signPlayer) {
    return -10;
  } else if (game.winner(board) === signAI) {
    return +10;
  } else {
    return 0;
    //Game is a draw
  }
}

function max(board) {

  if (game.over(board)) {
    return score(board);
  }
  var newGame = [];
  var moveScore, maxnewGame;
  var best_score = -Infinity;
  var movesArray = game.possible_moves(board, signAI);

  for (var i = 0; i < movesArray.length; i++) {
    newGame = movesArray[i].slice();
    moveScore = min(newGame);
    if (moveScore > best_score) {
      best_score = moveScore;
      maxnewGame = newGame;
    }
  }
  console.log('maxnewGame', maxnewGame);
  return best_score;
}

function min(board) {

  if (game.over(board)) {
    return score(board);
  }
  var newGame = [];
  var moveScore, minnewGame;
  var worst_score = Infinity;
  var movesArray = game.possible_moves(board, signPlayer);

  for (var i = 0; i < movesArray.length; i++) {
    newGame = movesArray[i].slice();
    moveScore = max(newGame);
    if (moveScore < worst_score) {
      worst_score = moveScore;
      minnewGame = newGame;
    }
  }
  console.log('minnewGame', minnewGame);
  return worst_score;
}
console.log(max(board));

【讨论】:

  • 感谢您提供如此详细的答案,但还有一件事:我如何以最佳移动返回棋盘?
  • @TarasYaremkiv 只需将棋盘连同分数一起归还。例如,return [worse_score, minnewGame]'。然后在调用函数时,做result = min(board); moveScore = result[0]; moveBoard = result[1].
  • 我的算法肯定有问题(实现。在上面的 sn-p 中,AI 最佳移动总是插入到第一个空单元格中。
  • @TarasYaremkiv 那是因为它做出了最好的举动,被定义为“与完美对手对抗的成功机会最高的举动”。
  • 这是我一一输入棋盘值的顺序。在这段代码中gist.github.com/Y-Taras/e2e7cf74a5b262d211a8ba25cf460d6b 所以最终玩家赢了,AI 输了 ['e', 'e', 'e', 'e', 'o', 'e', 'e' , 'e', 'e']; ['x', 'e', 'e', 'e', 'o', 'e', 'e', 'e', 'e' ]; ['x','o','e','e','o','e','e','e','e' ]; ['x', 'o', 'x', 'e', 'o', 'e', 'e', 'e', 'e' ]; ['x', 'o', 'x', 'e', 'o', 'e', 'e', 'o', 'e'];
猜你喜欢
  • 2022-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多