在Javascript中实现极小化极大算法

4
作为个人练习,我正在尝试实现一个基于极小化极大算法的井字游戏。我一直在研究我在网上找到的各种语言的示例。我的实现似乎已经可以工作,但在某些边缘情况下,AI会输掉。您可以在这里玩我的版本。
如果您选择3个角落然后选择中心,您将获胜。否则它似乎表现正确。我可以手动运行我的minmax()函数,并使用不同的游戏状态进行测试,发现AI的第一步得分不正确。我担心我实施算法的方式存在根本性问题。
以下是我的代码:
// Board state 'object'
function State(old) {
  // Prior board states can be loaded in during minmax recursion
  if (typeof old !== 'undefined') {
    this.board = old.board.slice(0);
  } else {
  // Otherwise start with empty board
    this.board = ['E','E','E','E','E','E','E','E','E'];  
  }
  // Terminal game flag
  this.result = 'active';
  // Current player flag
  this.turn = "X";
  // Label to identify move that results in this state during recursion
  this.element = "";
  // Function to switch active player for minmax scoring
  this.advanceTurn = function() {
    this.turn = this.turn === "X" ? "O" : "X";
  }
  // Function to determine if game is complete
  this.isTerminal = function() {
    const lines = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6],
    ];
    for (let i = 0; i < lines.length; i++) {
      const [a, b, c] = lines[i];
      if (this.board[a] !== 'E' && this.board[a] === this.board[b] && this.board[a] === this.board[c]) {
        this.result = this.board[a];
        return true;
      } 
    } 
    if (this.moves().length < 1) {
        this.result = 'DRAW';
        return true;
    }
    return false;
  }
  // Function to find all possible moves
  this.moves = function() {
    arr = this.board.reduce(function(array,el,index){
      if (el === 'E') {
        array.push(index);
      }
      return array;
    },[]);
    return arr;
  }
}

// Recursive minmax function
function minmax(state) {
  // 1) If the state is terminal, return the score from O's perspective
  if (state.isTerminal() === true) {
    if (state.result === 'X') {
      return  -10; 
    } else if (state.result === 'O') {  
      return 10;
    } else {
      return 0;
    }
  } 

  // Generate list of possible new game states (moves) 
  newStatesSet = state.moves().map(function (el) {
    var newState = new State(state);
    newState.board[el] = state.turn.slice(0);
    newState.advanceTurn();
    newState.element = el;
    return newState;
  });  

  // Array to hold all child scores
  var newStateScores = [];

  // For each of these states, add the minmax score of 
  // that state to the scoreList
  newStatesSet.forEach(function(newState) {
    var newStateScore = minmax(newState);
    newStateScores.push(newStateScore);
  });
  stateScore = Math.min(...newStateScores);
  return stateScore;
}

function aiMove(state) {
  var possibleScores = [];
  var possibleMoves = [];
  var possibleStates = state.moves().map(function(el) {
    var newState = new State(state);
    possibleMoves.push(el);
    newState.board[el] = 'O';
    possibleScores.push(minmax(newState));
    return newState;
  });
  if (possibleMoves.length < 1) {
    return -1;
  }
  console.log(possibleStates);
  console.log(possibleScores);
  function indexOfMax(arr) {
    var max = arr.reduce(function(a,b) {
      return b > a ? b : a;   
    });
    return arr.indexOf(max);
  }
  return possibleMoves[indexOfMax(possibleScores)];
}  

var game = new State();
game.board = ['E','E','E',
              'O','E','E',
              'X','E','X']
game.turn = 'O';
//console.log(minmax(game));
console.log(aiMove(game));

我不确定,但这可能对你有所帮助 https://mostafa-samir.github.io/Tic-Tac-Toe-AI/ - Mahesh Gareja
2个回答

2

MiniMini算法

我认为你的递归minmax函数存在问题:

stateScore = Math.min(...newStateScores);

这总是计算最小值,所以实际上您正在运行一种递归算法,在其中X和O合作使X获胜!(在您的顶层中确实使用了max,但不在递归中。)您可以通过根据状态.turn选择是max还是min来修复此问题。

感谢查看代码!在递归中,我在函数上运行了所有当前回合的子代分数集合上的Math.min。我这样做是为了让AI避免可能输掉的状态。这是一个简化的解决方案,在纸上经过了很多情况后,我意识到它是错误的。我添加了一个条件来检查当前回合和最小值或最大值,具体取决于谁的回合。它仍然给出奇怪的答案,但我注意到我在将当前回合分配给从旧状态生成的新状态的方式中存在错误。我修复了这个问题,现在它可以正常工作了! - Solomon Bothwell

0

也许这个TypeScript实现可以帮助你。它使用BoardMiniMaxItem,像这样:

interface Board {
    gameOver: boolean;
    score: number;
    playerX: boolean;
    val: GridEntry[];
}
interface  MinMaxItem {
    score: number;
    board: Board;
}

MiniMaxItem 是递归元素,其中保存了状态及其递归分数。要在 JavaScript 中实现,您需要将 BoardManager 类的方法更改为函数。


"...你必须通过函数更改BoardManager类的方法"是什么意思? - Zac
我修改了代码,请再次检查代码库,你就会明白。 - botika
1
链接已经失效。您能否澄清如何使用这些数据结构实现极小化极大算法? - aleph_one

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接