Javascript中的Minimax无法正常工作

时间:2017-09-26 13:19:24

标签: javascript jquery tic-tac-toe minimax minmax

作为一个练习项目,我在JSFiddle上制作了一个Tic-Tac-Toe游戏(因为它已经不够了,对吗?)并且我逐渐增加了一个无与伦比的AI。在大多数情况下它可以工作,但有一些组合(例如将X设置为字段5,9,3或字段3,7,9)导致计算机无法正确计算最佳移动。

JSFiddle上的项目:https://jsfiddle.net/jd8x0vjz/

相关功能从第63行开始:

function evaluateMove(move, player, depth) {
var gameStatus = evaluateGameStatus(move); //get status of current board
if (gameStatus < 2 && player)
    return -1; //if human won, return -1
if (gameStatus < 2 && !player)
    return 1; //if human lost, return 1

var returnValue = 0 //value to be returned later

for (var z = 0; z < 3; z++) { //loop for row
    for (var s = 0; s < 3; s++) { //loop for column
        if (move[z][s]) //if current slot has an x or o,
            continue; //skip it     
        var nextMove = cloneGameStatus(move); //create temporary array with base of current grid
        nextMove[z][s] = !player ? "x" : "o"; //assign first free field  the appropriate symbol
        var value = evaluateMove(nextMove, !player, depth+1);   //recursion but with switched player, to add the correct icon afterwards
        if ((value > returnValue) && player)
            returnValue = value;            
        if ((value < returnValue) && !player)
            returnValue = value;                
    }
}
return returnValue; //return value of current simulation
}

我认为最后两个if子句导致了这些问题,因为计算机确实计算了正确的值(在调试器中可观察到),但它们有时会被覆盖,但我不确定这是否真的是问题的根源。任何帮助或提示将不胜感激!

编辑:问题解决了!如果不是第一个,请在下面查找我的答案。

2 个答案:

答案 0 :(得分:1)

我不能肯定这是问题的根源,但是你的代码中肯定存在一个会产生奇怪结果的错误。这一行:

var returnValue = 0 //value to be returned later

不正确。除了缺少分号外,正确的代码应该是:

var returnValue = -1;
if(!player){
  returnValue = 1;
}

你希望最大玩家的默认值为负值,这样他才能获得最佳移动,并且最大限度地减少玩家积极因素,这样他就会采取最差的行动。你这样做的方式,如果最大化玩家只面对值为-1的选项,因为-1小于0,并且returnValue初始化为0,虽然要返回的正确值为-1,但仍会返回0。

答案 1 :(得分:1)

returnValue默认值错误的想法肯定让我走上了正确的道路;它并没有让所有东西神奇地起作用(如果它真的那么太好了),但它确实给了我正确的推动力。由于我们不想在没有计算任何值的情况下返回任何值,因此我将evaluateMove函数调整如下:

function evaluateMove(move, player, depth) {
var gameStatus = evaluateGameStatus(move); //get status of current board
if (gameStatus != 2)
    return gameStatus; //if the game is not running anymore, return result

var returnValue; //value to be returned later

for (var z = 0; z < 3; z++) { //loop for row
    for (var s = 0; s < 3; s++) { //loop for column
        if (move[z][s]) //if current slot has an x or o,
            continue; //skip it     
        var nextMove = cloneGameStatus(move); //create temporary array with base of current grid
        nextMove[z][s] = !player ? "x" : "o"; //assign first free field  the appropriate symbol
        var value = evaluateMove(nextMove, !player, depth+1);   //recursion but with switched player, to add the correct icon afterwards
        if ((value > returnValue || returnValue == null) && player)
            returnValue = value;            
        if ((value < returnValue || returnValue == null) && !player)
            returnValue = value;                
    }
}
return returnValue; //return value of current simulation
}

现在默认值为null,因此不应该关闭计算。然而它甩掉的是第一块支票,所以我调整它只是为了在游戏结束时返回当前状态,而不是进行任何精心检查。但是 放弃了结果,因为我在两种方法中使用了反转的默认值,所以我也必须调整evaluateGameStatus。现在,如果人类获胜,则返回-1而不是1,如果计算机获胜,则返回1而不是-1:

function evaluateGameStatus(gameStatus) { //a clusterfuck of winning combinations
if(
X Checks
)
return -1; //there's a successful combination of x's

else if(
O Checks
)
return 1; //there's a successful combination of o's

else {
for (var z = 0; z < 3; z++) {
    for (var s = 0; s < 3; s++) {
        if (!gameStatus[z][s])
            return 2; //if there is an empty field neither has won, continue playing
        }
    }

return 0; //there's no successful combination and max moves have been reached. it's a draw
}
}

显然,我必须对checkGameEnd函数进行相同的调整。
你会注意到我也改变了抽签检查。这是因为,由于某种原因,旧的检查count == maxMoves不再起作用,所以我改为循环,它只检查是否有空字段,如果有则返回2,如果有,则返回0没有(它在这里返回0,因为此时它已经完成了所有的检查:X没有赢,O没赢,而且没有剩下的开放位置,所以游戏必须是平局)。 / p>

现在可以在这里找到工作项目:
https://jsfiddle.net/h5zwzkm7/