tic-tac-toe minimax的递归问题

时间:2017-02-16 13:23:41

标签: javascript

我正在尝试使用minimax,但它已经卡住,任何人都可以放弃一些光线吗?该方法在HTML内容的上方computerBestMove

当我放入一个bugger时,它会导致堆叠太深。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>

    <style>
      #game_board {
        border-style: solid
      }
      th {
        border-style: inset;
        font-size: 20px;
        text-align: center;
        width: 50px;
        height: 50px;
      }
    </style>

    <script>
      var gameOver = false;
      var state = [0,0,0,0,0,0,0,0,0];
      var human = false
      var computer = true
      var humanValue = -1;
      var compValue = 1;
      var winCombo = [
        [0,1,2],
        [3,4,5],
        [6,7,8],
        [0,3,6],
        [1,4,7],
        [2,5,8],
        [0,4,8],
        [2,4,6]
      ]
      var squares = document.getElementsByClassName("square");


      function markSquare(square) {
        if(gameOver === false ) {
          for(var x = 0; x < 9; x++ ) {
            if(squares[x] == square && state[x] == 0) {
              set(x, humanValue);
              computerMove();
            }
          }
        } else {
          console.log("game is over");
        }
      }

      function set(index, player) {
        if(player == humanValue) {
          squares[index].innerText = humanValue;
          state[index] = humanValue;
        } else {
          squares[index].innerText = compValue;
          state[index] = compValue;
        }
      }

      function checkWinner(board, player) {
        var value = player === human ? humanValue : compValue;
        //8 differnt way of winning
        winCombo.forEach(function(combo) {
          if (value === state[combo[0]] &&
            state[combo[0]] === state[combo[1]] &&
            state[combo[1]] === state[combo[2]]) {
              gameOver = true;
              console.log(value + " wins!");
            }
        })
        return gameOver;
        // for(var x = 0; x < 8; x++) {
        //   var win = true;
        //   for(var y = 0; y < 3; y++){
        //     if(board[winCombo[x][y]] != value) {
        //       win = false;
        //       break;
        //     }
        //   }
        //   if(win) {
        //     return true;
        //   }
        // }
        // return false;
      }

      function returnWinner() {
        var winner = null;
        winCombo.forEach(function(combo) {
          if (state[combo[0]] === state[combo[1]] && state[combo[1]] === state[combo[2]]) {
            (combo[0] === -1 ) ?  (winner = "player") : (winner = "computer");
          }
        })
        return winner;
      }

      function noMoreMove(board) {
        for (var i = 0; i < 9; i++) {
          if (board[i] === 0) {
            return false;
          } else
          return true;
        }
      }

      function computerMove() {
        computerBestMove(state, 0, computer)
      }

      function computerBestMove(board, depth, player) {
        //if player wins ... = -10

        if(checkWinner(board, !player) === true) {
          return -10 + depth;
        }else if(noMoreMove(board) === true) {
          return 0;
        }

        var value = player === human ? humanValue : compValue;
        var max = -Infinity;
        var index = 0;

        for(var i = 0; i < 9; i++){
          if(board[i] === 0) {
            var newBoard = board.slice();
            newBoard[i] = value;

            var moveValue = -computerBestMove(newBoard, depth+1, !player);

            if(moveValue > max) {
              max = moveValue;
              index = i;
            }
          }
        }
        if(depth === 0){
          set(index, computer)
        }
        return max
      }

    </script>
  </head>
  <body>

    <p id="message">A the message area.</p>

    <button type="button" name = "choice" value = "two players" checked>Two players </button>
    <button type="button" name = "choice" value = "vs AI">Unbeatable AI </button>

    <table id='game_board'>
      <tr>
        <th id="square0" class = 'square' onclick="markSquare(this);"></th>
        <th id="square1" class = 'square' onclick="markSquare(this);"></th>
        <th id="square2" class = 'square' onclick="markSquare(this);"></th>
      </tr>
      <tr>
        <th id="square3" class = 'square' onclick="markSquare(this);"></th>
        <th id="square4" class = 'square' onclick="markSquare(this);"></th>
        <th id="square5" class = 'square' onclick="markSquare(this);"></th>
      </tr>
      <tr>
        <th id="square6" class = 'square' onclick="markSquare(this);"></th>
        <th id="square7" class = 'square' onclick="markSquare(this);"></th>
        <th id="square8" class = 'square' onclick="markSquare(this);"></th>
      </tr>
    </table>

  </body>
</html>

1 个答案:

答案 0 :(得分:0)

这段代码不正确。如果从函数返回,循环将自动停止。所以这只会检查每次第一个单元格。 一旦用适当的循环替换它来检查所有单元格,就会出现堆栈溢出,如你所说。

  function noMoreMove(board) {
    for (var i = 0; i < 9; i++) {
      if (board[i] === 0) {
        return false;
      } else
      return true;
    }
  }

堆栈溢出问题是由这行代码引起的:

var moveValue = -computerBestMove(newBoard, depth+1, !player);

这只是错误的逻辑。唯一一次computerBestMove()不会创建一个无限循环,就是当一个玩家赢了,这不可能是前3个移动。或者当没有更多的空单元格时,修复后的noMoreMove函数也不会发生前8次移动。两者都在开始时进行检查。

所以你总是会触发这个无限循环。

您必须重写computerBestMove()函数,以便检查哪个移动最佳的部分和分配结果的部分。您希望循环检查最佳位置,但是一旦检查了所有组合,该循环必须停止。