2016-05-09 12 views
1

このTicTacToe AIの実装に問題があります。I found hereです。私はjavascriptには比較的新しいので、私は変数のスコープに何か間違っていると確信しています。TicTacToe minimax AI in Javascript

コード文句を言わない私が破壊されると考えていること、特定のコードをスニペットに実行しますがheres my codepen

choices = { 
 
    0: '#ul', 
 
    1: '#um', 
 
    2: '#ur', 
 
    3: '#ml', 
 
    4: '#mm', 
 
    5: '#mr', 
 
    6: '#ll', 
 
    7: '#lm', 
 
    8: '#lr' 
 
} 
 

 
function getGrid() { 
 
    var divs = [] 
 
    for (var i = 0; i < 9; i++) { 
 
    divs.push($(choices[i]).html()) 
 
    } 
 
    return divs 
 
} 
 

 
function getGame() { 
 
    var divs = [] 
 
    for (var i = 0; i < 9; i++) { 
 
    divs.push([$(choices[i]).html(), i]) 
 
    } 
 
    return divs 
 
} 
 

 
function convertGameToGrid(game) { 
 
    var divs = [] 
 
    for (var i = 0; i < game.length; i++) { 
 
    divs.push(game[i][0]) 
 
    } 
 
    return divs 
 
} 
 

 
function checkGrid(divs) { 
 
    var options = [ 
 
    [divs[0], divs[1], divs[2]], 
 
    [divs[3], divs[4], divs[5]], 
 
    [divs[6], divs[7], divs[8]], 
 
    [divs[0], divs[3], divs[6]], 
 
    [divs[1], divs[4], divs[7]], 
 
    [divs[2], divs[5], divs[8]], 
 
    [divs[0], divs[4], divs[8]], 
 
    [divs[2], divs[4], divs[6]] 
 
    ] 
 

 
    for (var i = 0; i < options.length; i++) { 
 
    if (options[i][0] == 'X' && options[i][1] == 'X' && options[i][2] == 'X') { 
 
     return 'X' 
 
    } else if (options[i][0] == 'O' && options[i][1] == 'O' && options[i][2] == 'O') { 
 
     return 'O' 
 
    } 
 
    } 
 
    for (var i = 0; i < 9; i++) { 
 
    if (divs[i] == '') { 
 
     return false //still moves 
 
    } 
 
    } 
 
    return 'Tie' //no winner and no moves 
 
} 
 

 
var player = 'O' 
 
var ai = 'X' 
 

 
$(document).ready(function() { 
 

 
    function playerTurn(i) { 
 
    return function() { 
 
     var g = getGrid() 
 
     var cG = checkGrid(g) 
 
     if (!cG) { 
 
     if ($(choices[i]).html() == '') { 
 
      $(choices[i]).html(player) 
 
      var g = getGrid() 
 
      var cG = checkGrid(g) 
 
      if (cG == player) { 
 
      console.log('You win') 
 
      } else if (cG == 'Tie') { 
 
      console.log('Tie') 
 
      } else { 
 
      aiTurn() 
 
      } 
 
     } 
 
     } 
 
    } 
 
    } 
 

 
    for (var i = 0; i < 9; i++) { 
 
    $(choices[i]).on('click', playerTurn(i)); 
 
    } 
 

 
    function score(g, depth) { 
 
    var cG = checkGrid(g) 
 
    console.log(cG, g) 
 
    if (cG == ai) { 
 
     return 10 - depth 
 
    } else if (cG == player) { 
 
     return depth - 10 
 
    } else { 
 
     return 0 
 
    } 
 
    } 
 

 
    function minimax(game, depth) { 
 
    var g = convertGameToGrid(game) 
 
    if (checkGrid(g)) { 
 
     return score(g, depth) 
 
    } 
 
    depth += 1 
 
    var scores = [] 
 
    var moves = [] 
 

 
    var availMoves = getAvailMoves(game) 
 
    console.log('moves', availMoves) 
 
    for (var i = 0; i < availMoves.length; i++) { 
 
     var possibleGame = game 
 
     if (depth % 2 == 0) { 
 
     possibleGame[availMoves[i]][0] = ai 
 
     } else { 
 
     possibleGame[availMoves[i]][0] = player 
 
     } 
 
     var m = minimax(possibleGame, depth) 
 
     scores.push(m) 
 
     console.log('mm: ', depth, i, scores) 
 
     moves.push(availMoves[i]) 
 
    } 
 

 
    //even depths are ai, odd are player 
 
    if (depth % 2 == 0) { 
 
     var max_score_index = 0 
 
     var max_score = -100000000 
 
     for (var i = 0; i < scores.length; i++) { 
 
     if (scores[i] > max_score) { 
 
      max_score_index = i 
 
      max_score = scores[i] 
 
     } 
 
     } 
 
     if (depth == 0) { //we need the best move 
 
     return moves[max_score_index] 
 
     } else { //otherwise this function needs scores 
 
     return scores[max_score_index] 
 
     } 
 
    } else { 
 
     var min_score_index = 0 
 
     var min_score = 100000000 
 
     for (var i = 0; i < scores.length; i++) { 
 
     if (scores[i] < min_score) { 
 
      min_score_index = i 
 
      min_score = scores[i] 
 
     } 
 
     } 
 
     return scores[max_score_index] 
 
    } 
 
    } 
 

 
    function getAvailMoves(game) { 
 
    var moves = [] 
 
    for (var i = 0; i < game.length; i++) { 
 
     if (game[i][0] == '') { 
 
     moves.push(game[i][1]) 
 
     } 
 
    } 
 
    return moves 
 
    } 
 

 
    function aiTurn() { 
 
    //Dumb ai 
 
    // c = Math.floor(Math.random()*9) 
 
    // while ($(choices[c]).html()) { 
 
    // c = Math.floor(Math.random()*9) 
 
    // } 
 

 
    //new strategy taken from http://neverstopbuilding.com/minimax 
 
    console.log('ai') 
 
    var c; 
 
    game = getGame() 
 
    c = minimax(game, -1) 
 

 
    $(choices[c]).html('X') 
 
    var g = getGrid() 
 
    var cG = checkGrid(g) 
 
    if (cG == ai) { 
 
     console.log('You lose') 
 
    } else if (cG == 'Tie') { 
 
     console.log('Tie') 
 
    } 
 
    } 
 

 

 
})
#ttt-box { 
 
    position: relative; 
 
    height: 304px; 
 
    width: 304px; 
 
    margin: 30px auto; 
 
    background-color: #bbb; 
 
    border: solid #000 4px; 
 
    border-radius: 20%; 
 
} 
 
#l1, 
 
#l2, 
 
#l3, 
 
#l4 { 
 
    position: absolute; 
 
    background-color: #000; 
 
} 
 
#l1 { 
 
    left: 99px; 
 
    width: 3px; 
 
    height: 296px; 
 
} 
 
#l2 { 
 
    left: 199px; 
 
    width: 3px; 
 
    height: 296px; 
 
} 
 
#l3 { 
 
    top: 99px; 
 
    width: 296px; 
 
    height: 3px; 
 
} 
 
#l4 { 
 
    top: 199px; 
 
    width: 296px; 
 
    height: 3px; 
 
} 
 
#ul, 
 
#um, 
 
#ur, 
 
#ml, 
 
#mm, 
 
#mr, 
 
#ll, 
 
#lm, 
 
#lr { 
 
    cursor: pointer; 
 
    position: absolute; 
 
    width: 99px; 
 
    height: 99px; 
 
    font-size: 70px; 
 
    text-align: center; 
 
} 
 
#ul { 
 
    top: 0; 
 
    left: 0; 
 
} 
 
#um { 
 
    top: 0; 
 
    left: 101px; 
 
} 
 
#ur { 
 
    top: 0; 
 
    left: 201px; 
 
} 
 
#ml { 
 
    top: 101px; 
 
    left: 0; 
 
} 
 
#mm { 
 
    top: 101px; 
 
    left: 101px; 
 
} 
 
#mr { 
 
    top: 101px; 
 
    left: 201px; 
 
} 
 
#ll { 
 
    top: 201px; 
 
    left: 0; 
 
} 
 
#lm { 
 
    top: 201px; 
 
    left: 101px; 
 
} 
 
#lr { 
 
    top: 201px; 
 
    left: 201px; 
 
}
<body> 
 
    <div class="container"> 
 
    <div id="content"> 
 
     <div id="ttt-box"> 
 
     <div id="l1"></div> 
 
     <div id="l2"></div> 
 
     <div id="l3"></div> 
 
     <div id="l4"></div> 
 

 
     <div id="boxes"> 
 
      <div id="ul"></div> 
 
      <div id="um"></div> 
 
      <div id="ur"></div> 
 
      <div id="ml"></div> 
 
      <div id="mm"></div> 
 
      <div id="mr"></div> 
 
      <div id="ll"></div> 
 
      <div id="lm"></div> 
 
      <div id="lr"></div> 
 
     </div> 
 

 
     </div> 
 
    </div> 
 
    </div> 
 
</body>

以下のセクションです。最初のプレイヤーの移動後、私はconsole.logが8を出力するはずだと思います!なぜなら、それは単一の経路をたどったかのように8回しか印刷しないからです。

var availMoves = getAvailMoves(game) console.log('moves',availMoves) for (var i=0;i<availMoves.length;i++) { var possibleGame = game if (depth%2==0) { possibleGame[availMoves[i]][0] = ai } else { possibleGame[availMoves[i]][0] = player } var m = minimax(possibleGame,depth) scores.push(m) console.log('mm: ', depth,i, scores) moves.push(availMoves[i]) }

編集:私は気づいてることは時々ミニマックス再帰はundefinedを返していることです。私はそれがなぜ(私のcodepenを見て)見つけることを試みたが、私は失敗した。

Edit2:これらの再帰を完全にスキップしているため、未定義に戻っているようです。私はまだこれを解決する方法を見つけることができません。

答えて

2

最初にお勧めします。まず始めに、デバッガの使い方を学びましょう。

あなたの問題については、すべてのコードをトレースしていませんが、私はあなたのminmax関数で問題を引き起こしている可能性があることに気がつきました。あなたは(それを割り当てずに)elseブロックでそれを使用しても宣言しifブロックでmax_score_indexを割り当てるされますが、

//even depths are ai, odd are player 
if (depth % 2 == 0) { 
    var max_score_index = 0 
    // snip... 
} else { 
    var min_score_index = 0 
    // snip... 
    return scores[max_score_index] 
} 

注:その関数の底部に向かって、あなたはこのコードを持っています。それはelseブロックから未定義に戻されます。

+0

ありがとうございます!それは間違いなく問題でした。私の「未定義」問題は解消されましたが、それでもまだ正しく機能していないので、後で詳しく説明します。デバッガでは、コンソールを経由するよりも優れたものが組み込まれていますか?私のコードはエラーを投げているわけではないので、私がコンソールでトレースするものは何もありませんが、JSをデバッグする最良の方法についてもっとアドバイスがあれば、喜んでそれを取るでしょう –

+1

あなたはコンソール、開発ツールを介して。 IEおよびFireFox開発者ツールで、[デバッガ]タブを選択します。 Chromeデベロッパーツールで[ソース]タブを選択します。いったんそこにブレークポイントを設定し、変数値を調べ、コードをステップ実行することができます。問題を解決するためにコードを実際に見ているようなものはありません。 –

+1

あなたのお手伝いをしてくれてありがとう、ジャック、私はすべてがうまくいきました。私の他の問題は深刻なコピーエラーに終わったので、デバッガを使う必要はありませんでしたが、 –