2017-04-05 12 views
0

Tic-Tac-Toe 게임을 쓰려고하는데 MiniMax 알고리즘을 사용하기로 결정했지만 구현하는 데 문제가 있습니다. 예를 들어 : Tic-Tac-Toe의 Minimax가 올바른 값을 반환하지 않음

board = [ 
      "E", "E", "X", 
      "E", "E", "X", 
      "E", "O", "O" 
      ]; 

에 그것은 AI의 차례이고 함수는 최고의 이동으로 AiMove { score: -10, coordinates: 0 } 반환합니다. 나는 지금 꽤 오랫동안 디버깅을 해왔지만 기능의 재귀 적 성격과 가능한 게임 나무의 양, 특히 초기 게임 상태는 따라 잡고 디버깅하기가 어렵다.

누군가가 손을 빌려줄 수 있습니까?

https://jsfiddle.net/LdLqk1z8/4/

var factions = { 
 
    AIplayer: "X", 
 
    humanPlayer: "O" 
 
}; 
 

 
var gameResults = { 
 
    winner: "" 
 
}; 
 

 
var emptyCells = function(board) { //check for empty cells and return an array with the number of empty cells 
 
    var indices = []; 
 
    for (var itr = 0; itr < 9; itr++) { 
 
    if (board[itr] === "E") { 
 
     indices.push(itr); 
 
    } 
 
    } 
 
    return indices; 
 
}; 
 

 
var isGameOver = function(board) { 
 
    var tile = board; 
 

 
    //check for victory conditions 
 
    for (var i = 0; i <= 6; i = i + 3) { 
 
    if (tile[i] !== "E" && tile[i] === tile[i + 1] && tile[i + 1] === tile[i + 2]) { 
 
     if (factions.AIplayer === tile[i]) { 
 
     gameResults.winner = "AIplayer"; 
 
     } else if (tile[i] === factions.humanPlayer) { 
 
     gameResults.winner = "humanPlayer"; 
 
     } 
 
     return true; 
 
    } 
 
    } 
 

 
    for (var i = 0; i <= 2; i++) { 
 
    if (tile[i] !== "E" && tile[i] === tile[i + 3] && tile[i + 3] === tile[i + 6]) { 
 
     if (factions.AIplayer === tile[i]) { 
 
     gameResults.winner = "AIplayer"; 
 
     } else if (tile[i] === factions.humanPlayer) { 
 
     gameResults.winner = "humanPlayer"; 
 
     } 
 
     return true; 
 
    } 
 
    } 
 

 
    for (var i = 0, j = 4; i <= 2; i = i + 2, j = j - 2) { 
 
    if (tile[i] !== "E" && tile[i] === tile[i + j] && tile[i + j] === tile[i + 2 * j]) { 
 
     if (factions.AIplayer === tile[i]) { 
 
     gameResults.winner = "AIplayer"; 
 
     } else if (tile[i] === factions.humanPlayer) { 
 
     gameResults.winner = "humanPlayer"; 
 
     } 
 
     return true; 
 
    } 
 
    } 
 

 
    var check = emptyCells(board); //check if the game ended with a draw 
 
    if (check.length === 0) { 
 
    gameResults.winner = "draw"; 
 
    return true; 
 
    } else { 
 
    return false; //if no condition is matched the game has not concluded 
 
    } 
 
}; 
 

 
var getBestMove = function(board, player) { 
 

 
    // return an AiMove object initialized to 10 if the AI player wins, -10 if the human player wins and 0 if the game is a draw 
 
    if (isGameOver(board)) { 
 
    if (gameResults.winner === "AIplayer") { 
 
     return new AiMove(10); 
 
    } else if (gameResults.winner === "humanPlayer") { 
 
     return new AiMove(-10); 
 
    } else if (gameResults.winner === "draw") { 
 
     return new AiMove(0); 
 
    } 
 
    } 
 

 
    var moves = []; //array to store all moves 
 
    var currentPlayer = player; 
 
    for (var i = 0, l = board.length; i < l; i++) { //iterate over the board 
 
    if (board[i] == "E") { //if the tile is empty 
 
     var move = new AiMove; //create new AiMove object and assign a coordinate 
 
     move.coordinates = i; 
 
     board[i] = currentPlayer; //update board 
 
     //call getBestMove recursively with the next player 
 
     if (currentPlayer === factions.AIplayer) { 
 
     move.score = getBestMove(board, factions.humanPlayer).score; 
 
     } else if (currentPlayer === factions.humanPlayer) { 
 
     move.score = getBestMove(board, factions.AIplayer).score; 
 
     } 
 
     moves.push(move); 
 

 
     board[i] = "E"; //clear tile after move is pushed in to the moves array 
 
    } 
 
    } 
 

 
    //if it's the AI player's turn select biggest value from the moves array, if it's the human player's turn select the smallest value 
 
    if (currentPlayer === factions.AIplayer) { 
 
    var bestMove = 0; 
 
    var bestScore = -10000; 
 
    for (var i = 0; i < moves.length; i++) { 
 
     if (moves[i].score > bestScore) { 
 
     bestScore = moves[i].score; 
 
     bestMove = i; 
 
     } 
 
    } 
 
    } else if (currentPlayer === factions.humanPlayer) { 
 
    var bestMove = 0; 
 
    var bestScore = 10000; 
 
    for (var i = 0; i < moves.length; i++) { 
 
     if (moves[i].score < bestScore) { 
 
     bestMove = i; 
 
     bestScore = moves[i].score; 
 
     } 
 
    } 
 
    } 
 
    return moves[bestMove]; //return best move 
 
}; 
 

 
var board = [ 
 
       "E", "E", "X", 
 
       "E", "E", "X", 
 
       "E", "O", "O" 
 
       ]; 
 

 
function AiMove(score) { 
 
    this.coordinates, 
 
    this.score = score; 
 
} 
 

 
console.log(getBestMove(board, factions.AIplayer))

편집 : 이사회는이 이길 수없는 설정하고, AI, 그것은 "제공 업"숙명이기 때문에 그것은, 그렇게 될 수 있을까요? "깊이"라는 개념을 구현하면이 문제를 해결할 수 있을까요?

+2

글쎄,'isGameOver'는 게임 오버라고 생각합니다 - 그리고'O'가 침팬지를 내미는 똥이 아니라면'X'는 이길 수 없으며'O' ** **는 반드시 이겨야합니다 –

+0

주세요 조금 더 많은 컨텍스트, 점수는 무엇입니까? -10 의미? –

+0

@JaromandaX 그래, 인공 지능은 치명적이며 플레이어가 똥을 내고 튀어 나오지 않고 게임을 꺼낼 수 없게 만드는 을 완벽하게 수행한다고 가정합니다. 따라서 수건을 던지십시오. 깊이 구현 - 모든 재귀 호출에서 음수 스코어를 증가 시키므로 궁극적으로 사망으로 이어지더라도 AI가 상대 이동을 막도록 강요합니다 -이를 해결해야합니까? – Anon

답변

2

깊이를 소개하는 아이디어는 실제로 올바른 것입니다.

moves.push(move)는 다음 줄을 추가하기 전에 :

move.score = Math.sign(move.score) * (Math.abs(move.score) - 1); 

이 (-10, ... (10)가 9가된다 등 -9된다) 점수 1 점 이하 무덤을 만들 것입니다. 그것은 손실이 멀수록 덜 무덤 적이며 승리가 가까울수록 더 좋습니다.

제공된 샘플 보드에서 플레이어의 즉각적인 승리를 막는 최상의 이동 수단으로 6을 반환합니다.

. . X  . . X  . . X  X . X  X O X 
. . X → . . X → . O X → . O X → . O X 
. O O  X O O  X O O  X O O  X O O 

더 나은 디버깅 가능성을 위해, 나는 콘솔에 보드를 인쇄하는 기능을 작성합니다 게임이 이런 식으로 계속 같이 물론, AI는 여전히 최선의 플레이에 대해 잃게됩니다. 예를 들어 :

function printBoard(board) { 
    console.log(board.slice(0, 3).join (' ').replace(/E/g, '.')); 
    console.log(board.slice(3, 6).join (' ').replace(/E/g, '.')); 
    console.log(board.slice(6, 9).join (' ').replace(/E/g, '.')); 
    console.log(''); 
} 

은 또한 코드를 더 객체는 게임 객체 메소드, ... 등을 작성, 지향 만들기로 볼 수 있었다.

+0

제안 해 주셔서 감사합니다. 그것은 깊이를 구현하는 꽤 우아한 방법입니다. 사실, 저는 이미 간단한 GUI와 게임 로직을 작성했습니다. 다른 관련이없는 문제를 격리하기 위해 jsfiddle에 AI 코드를 다시 작성했습니다. 다음은 작성 당시의 전체 코드입니다. https : // jsfiddle.net/LdLqk1z8/4/ – Anon

+1

모든 코드를'humanPlayerTurn'에서'generateBoard'로 옮깁니다. https://fiddle.jshell.net/w1cyxwhr/3/에서 약간의 다른 것들과 그 업데이트를 확인하십시오. 실제로, 당신은 "질문하기"버튼을 통해 새로운 질문을해야합니다 : P – trincot

+0

나는 당신이 대답 할 때 바로 문제를 발견했습니다. 'AiMove()'는'humanPlayerTurn()'을 호출하지 않았습니다. 따라서 AI가 처음으로 나온 게임에서 게임은 멈췄습니다. 그러나 당신의 해결책은 더욱 의미가 있습니다. 각 AI 턴 다음에'humanPlayerTurn()'을 계속 호출 할 이유가 없습니다. 게임 리셋시'.off()'함수가 있어도 이벤트를 여러 번 붙이면됩니다. 모든 클릭 이벤트). 어쨌든 모든 도움을 주셔서 감사합니다. – Anon