实现Minimax算法的问题



我一直在尝试为一个简单的国际象棋机器人实现Minimax算法,我觉得我理解它背后的基本原理和一般原理,但我的代码并没有真正起作用,我正在努力找出原因。

这是我生成boardScore的函数。

const boardScore = (fen) => {
// fen = rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
// caps are for white
// white is maximizing player
const pieceWorth = {
p: -1,
P: 1,
k: -3,
K: 3,
b: -3,
B: 3,
r: -5,
R: 5,
q: -3,
Q: 3,
k: -99999,
K: 99999,
};
const pieces = fen.split(" ")[0].split("");
const score = 0;
for (const piece in pieces) {
score += pieceWorth[pieces[piece]] || 0;
}
if (game.turn() === "b" && game.in_checkmate()) score += 99999999;
if (game.turn() === "w" && game.in_checkmate()) score -= 99999999;
return score;
};

这是我为被调用的根极小极大函数编写的代码。目前我只想让它为黑色碎片(轮到AI(工作

const minimaxRoot = (game, depth) => {
// checking for black - minimizing player
const minUtility = Infinity;
let bestMove = null;
const moves = game.moves();
for (let i = 0; i < moves.length; i++) {
game.move(moves[i]);
let score = minimax(game, depth - 1);
if (score < minUtility) {
minUtility = score;
bestMove = moves[i];
}
game.undo();
console.log(minUtility);
return bestMove;
}
};

这是我的极大极小算法。

// white is maximizing player
const minimax = (game, depth, white) => {
console.count();
if (depth === 0) {
return boardScore(game.fen());
}
const moves = game.moves();
if (white) {
let bestScore = -Infinity;
for (let i = 0; i < moves.length; i++) {
game.move(moves[i]);
let score = minimax(game, depth - 1, false);
bestScore = Math.max(bestScore, score);
game.undo();
}
return bestScore;
} else {
let bestScore = Infinity;
for (let i = 0; i < moves.length; i++) {
game.move(moves[i]);
let score = minimax(game, depth - 1, true);
bestScore = Math.min(bestScore, score);
game.undo();
}
return bestScore;
}
};

这就是我调用函数的方式,当我移动时会发生这种情况。

const blackMove = () => {
game.move(minimaxRoot(game, 3));
setPosition(game.fen());
};

如有任何帮助,我们将不胜感激。在这两天的大部分时间里,我一直在努力,但进展甚微。我看到的大多数例子都包括某种形式的阿尔法-贝塔修剪、转置表或移动排序,这使它变得更加复杂,这让我很难理解。

首先,不要使用minimax算法。与阿尔法-贝塔相比效率低下。为了更简单,您应该在NegaMax框架中使用Alpha Beta。

注意NegaMax:评估函数应该相对于要移动的一侧。

那么,你的评价函数仅仅基于物质平衡,不足以拥有一个像样的打法。关于评估的两个好的(简单的(页面:
https://www.chessprogramming.org/Simplified_Evaluation_Function
https://www.chessprogramming.org/PeSTO%27s_Evaluation_Function

对于更复杂/高级的搜索实现:

  • 这里对MiniMax和AlphaBeta算法进行了很好的解释

  • 这里很好地解释了换位表,这里也解释了Zobrist哈希。(简化的(想法是不要(浪费时间(搜索之前搜索过的位置,我们更喜欢存储他们的分数。

  • 移动排序是一个简单的事实,AlphaBeta的性能取决于移动的顺序:如果它首先搜索最好的移动,它会更快。因此,我们在alphaBeta中近似通过的移动顺序(例如,QxP不好,因为PxQ=>首先搜索好的捕获(。

  • 此外,要在国际象棋引擎中拥有一个像样的国际象棋,必须有一个静态搜索,以避免地平线效应。

我已经在一个(我认为(评论良好的JavaScript国际象棋引擎中实现了所有这些东西。

我认为这应该是一个以NegaMax方式为您提供的AlphaBeta算法的工作代码:

const alphaBeta = (game, depth, white, alpha=-Infinity, beta=Infinity) => {
console.count();
if (depth === 0) {
var view = white ? 1 : -1;
return view * boardScore(game.fen());
}
const moves = game.moves();
let bestScore = -Infinity;
for (let i = 0; i < moves.length; i++) {
game.move(moves[i]);
let score = -alphaBeta(game, depth - 1, !white, -beta, -alpha);
game.undo();
if( score >= beta ) {
return beta;   //  fail hard beta-cutoff
}
if( score > alpha ) {
alpha = score; // alpha acts like max in MiniMax
}
}
return alpha;

};

当它在两个移动之间交替时,通常意味着它选择了列表中的第一个移动,并且没有找到更好的移动。

这里的问题很常见,它与您的评估函数有关。你总是为黑色返回一个负值,即使是在最小值最大循环中的黑色(白色则相反(。如果是黑色转向移动,则需要返回-score,而如果是白色转向,则返回score。如果在你的极小极大循环中没有其他问题,那么这应该可以解决你的问题。

稍后你会遇到的另一个问题是,你没有在你的极小极大函数中检查将死或平局。在检查深度==0的地方,你还需要检查游戏是否以任何方式结束,然后返回,否则即使游戏结束,它也会继续计算,这将产生非常奇怪的结果。

最新更新