Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/442.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript中的Minimax无法正常工作_Javascript_Jquery_Tic Tac Toe_Minimax_Minmax - Fatal编程技术网

Javascript中的Minimax无法正常工作

Javascript中的Minimax无法正常工作,javascript,jquery,tic-tac-toe,minimax,minmax,Javascript,Jquery,Tic Tac Toe,Minimax,Minmax,作为一个实践项目,我在JSFiddle上做了一个Tic-Tac-Toe游戏(因为还不够,对吧?),然后我加入了一个无与伦比的人工智能。在大多数情况下,它是有效的,但有一些组合(例如,将X设置为字段5、9、3或字段3、7、9)会导致计算机无法正确计算最佳移动 JSFIDLE上的项目: 以及从第63行开始的相关函数: function evaluateMove(move, player, depth) { var gameStatus = evaluateGameStatus(move); //ge

作为一个实践项目,我在JSFiddle上做了一个Tic-Tac-Toe游戏(因为还不够,对吧?),然后我加入了一个无与伦比的人工智能。在大多数情况下,它是有效的,但有一些组合(例如,将X设置为字段5、9、3或字段3、7、9)会导致计算机无法正确计算最佳移动

JSFIDLE上的项目:

以及从第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
}
功能评估移动(移动、玩家、深度){
var gameStatus=evaluateGameStatus(move);//获取当前板的状态
如果(游戏状态<2&&player)
return-1;//如果人类赢了,则返回-1
如果(游戏状态<2&!玩家)
返回1;//如果人员丢失,则返回1
var returnValue=0//稍后返回的值
对于(var z=0;z<3;z++){//行循环
对于(var s=0;s<3;s++){//column循环
if(move[z][s])//如果当前插槽具有x或o,
继续;//跳过它
var nextMove=cloneGameStatus(move);//使用当前网格的基创建临时数组
nextMove[z][s]=!player?:x:“o”;//为第一个自由字段指定适当的符号
var value=evaluateMove(nextMove,!player,depth+1);//递归,但使用切换的player,以便在之后添加正确的图标
如果((值>返回值)&&player)
返回值=值;
如果((值<返回值)&&!玩家)
返回值=值;
}
}
returnValue;//当前模拟的返回值
}
我认为最后两个if子句导致了这些问题,因为计算机确实计算了正确的值(在调试器中可以观察到),但它们有时会被覆盖,但我不确定这是否真的是问题的根源。任何帮助或提示都将不胜感激


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

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

var returnValue = 0 //value to be returned later
这是不正确的。除了缺少分号之外,正确的代码应该是:

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

您希望最大玩家的默认值为负值,以便他采取最好的移动,而最小玩家的默认值为正值,以便他采取最坏的移动。按照您的方式,如果最大化玩家只面临值为-1的选项,因为-1小于0,并且returnValue初始化为0,那么将返回0,尽管要返回的正确值是-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
}
功能评估移动(移动、玩家、深度){
var gameStatus=evaluateGameStatus(move);//获取当前板的状态
如果(游戏状态!=2)
return gameStatus;//如果游戏不再运行,则返回结果
var returnValue;//以后要返回的值
对于(var z=0;z<3;z++){//行循环
对于(var s=0;s<3;s++){//column循环
if(move[z][s])//如果当前插槽具有x或o,
继续;//跳过它
var nextMove=cloneGameStatus(move);//使用当前网格的基创建临时数组
nextMove[z][s]=!player?:x:“o”;//为第一个自由字段指定适当的符号
var value=evaluateMove(nextMove,!player,depth+1);//递归,但使用切换的player,以便在之后添加正确的图标
if((value>returnValue | | returnValue==null)&&player)
返回值=值;
if((value
现在默认值为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
}
}
函数evaluateGameStatus(gameStatus){//一堆获胜的组合
如果(
X支票
)
return-1;//有一个x的成功组合
否则如果(
O支票
)
return 1;//有一个成功的o的组合
否则{
对于(var z=0;z<3;z++){
对于(var s=0;s<3;s++){
如果(!gameStatus[z][s])
return 2;//如果有一个双方都没有赢得的空场,继续玩
}
}
返回0;//没有成功的组合,并且已达到最大移动量。这是平局
}
}
显然,我必须对checkGameEnd函数进行同样的调整。
你会注意到我也更改了提款支票。这是因为,出于某种原因,旧的count==maxMoves检查不再起作用,所以我改为一个循环,它只检查是否有空字段,如果有,则返回2,如果没有,则返回0(在这里它返回0,因为此时它已经完成了所有检查:X没有赢,O没有赢,并且没有剩余的空位,因此游戏必须是平局)

现在可以在此处找到工作项目: