这是我的井字游戏算法的Javascript代码:
function minimax(newGrid, depth, Player){
//debugger
const gameState = checkWin(newGrid,true);
if (gameState == false){
values = [];
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
const boardCopy = _.cloneDeep(newGrid);
if (boardCopy[i][j] !== '') continue;
boardCopy[i][j] = Player;
console.log(boardCopy);
const value = minimax(boardCopy, depth + 1, (Player === PLYR_TOKEN) ? COMP_TOKEN : PLYR_TOKEN);
values.push({
cost: value,
cell: {
i: i,
j: j
}
});
}
}
//debugger
if (Player === COMP_TOKEN){
const max = _.maxBy(values, (v) => {
return v.cost;
});
if( depth === 0 ){
return max.cell;
} else {
return max.cost;
}
}else{
const min = _.minBy(values, (v) => {
return v.cost;
});
if( depth === 0 ){
return min.cell;
} else {
return min.cost;
}
}
} else if (gameState === null){
return 0;
} else if (gameState === PLYR_TOKEN){
return depth - 10;
} else if (gameState === COMP_TOKEN){
return 10 - depth;
}
}
这种“算法”,“代码”的问题很简单:它不会阻碍我的行动。让我们想象一下这种情况:
X->播放器 O-> MM算法
X - O
- X -
- - -
通常,理想的MiniMax算法应该采用这种选择来阻止我获胜(小写字母o是新举动):
X - O
- X -
- - o
问题是我的代码使用了此命令(小写的o是新动作):
X - O
- X o
- - -
为什么?我不知道,但是我认为它必须赢了很多,无视我的举动,无视我离获胜还有一步之遥。 老实说,我不太了解这种算法的工作原理。
其他信息:主板是一个二维数组,并且minimax函数的结果是一个对象,该对象具有两个属性(i,j),它们表示主板上的坐标。
const board = [
['','',''],
['','',''],
['','','']
];
答案 0 :(得分:1)
因此,如有疑问,请发表评论!我一步一步地做到了,当我被卡住时不会停下来,而是每次我了解更多东西时都会来回走动。
//newGrid : the board
//depth : keep track of the "guessing level"
//Player : keep track of who's turn it would be
function minimax(newGrid, depth, Player){
//checking if the game ended
const gameState = checkWin(newGrid,true);
//if not
if (gameState == false){
values = [];
//for each cell in the grid
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
//we make a deep copy of the board
const boardCopy = _.cloneDeep(newGrid);
//if the cell isn't empty, we jump to the next one
if (boardCopy[i][j] !== '') continue;
//here we assign the Player to the cell (simulating a move from this player to this cell)
boardCopy[i][j] = Player;
//debugging
console.log(boardCopy);
//here go some recursivity, so we're putting our deepcopy with the simulated move, adding a depth level, and switching player
const value = minimax(boardCopy, depth + 1, (Player === PLYR_TOKEN) ? COMP_TOKEN : PLYR_TOKEN);
//since it was a recursive thing, please do imagine we get here at max depth BEFORE lesser depths, and then we'll climb back when each depth return its value to the previous one
//so here the first "value" going in "values" will be the first cell where we did not go through "if (gameState == false){" : first cell where the game ended (with its associated cost, more on that later)
values.push({
cost: value,
cell: {
i: i,
j: j
}
});
}
}
//when the loop ended
//if we're simulating a computer turn
if (Player === COMP_TOKEN){
//getting the "value" with max cost out of "values"
const max = _.maxBy(values, (v) => {
return v.cost;
});
//if we endend our recursivity (we climbed all the way back to depth 0) == we are on the actual grid with no simulation
if( depth === 0 ){
return max.cell; //return the cell (computer will play this cell)
} else {
return max.cost; //else return the cost (to put in the "values" list)
}
}else{ //if we're simulating a player turn, same thing but with the min
const min = _.minBy(values, (v) => {
return v.cost;
});
if( depth === 0 ){ //may not be useful if you always call minimax at depth 0 on computer turn
return min.cell;
} else {
return min.cost;
}
}
} else if (gameState === null){ //so, here we're simulating our endgame, a draw have a value of 0
return 0;
} else if (gameState === PLYR_TOKEN){ //a player win have a value of "depth-10" (the quicker he win, the lesser the result)
return depth - 10;
} else if (gameState === COMP_TOKEN){ //a computer win have a value of "10-depth" (the quicker he win, the greater the result)
return 10 - depth;
}
}
完成此操作后,我们对代码的工作方式以及为什么代码无法按预期的方式有更好的了解。 确实,在计算机转向时,它仅检查获胜最快的方法。我不确定我的解决方案是否100%可靠,但是您可以尝试以这种方式解决它:
if (Player === COMP_TOKEN){
//[...]
//if we endend our recursivity (we climbed all the way back to depth 0) == we are on the actual grid with no simulation
if( depth === 0 ){
const min = _.minBy(values, (v) => {
return v.cost;
});
if(min.cost>=-9)return min.cell; //if player win in the next turn, prevent it instead
else return max.cell; //else return the best cell for computer
}
//[...]
这还远非完美(例如,您可以通过两步获胜让他获得胜利),并且尚未经过测试,但我希望现在对您来说更加清晰。 当您完成代码并按需工作时,请随时将其发布在codereview.stackexchange上以获得优化建议。