我正在为国际象棋游戏制作人工智能。
到目前为止,我已经成功实现了Alpha-Beta Pruning Minimax算法,该算法看起来像这样(来自维基百科):
(* Initial call *)
alphabeta(origin, depth, -∞, +∞, TRUE)
function alphabeta(node, depth, α, β, maximizingPlayer)
if depth = 0 or node is a terminal node
return the heuristic value of node
if maximizingPlayer
for each child of node
α := max(α, alphabeta(child, depth - 1, α, β, FALSE))
if β ≤ α
break (* β cut-off *)
return α
else
for each child of node
β := min(β, alphabeta(child, depth - 1, α, β, TRUE))
if β ≤ α
break (* α cut-off *)
return β
由于这会花费太多时间复杂度(逐个遍历所有树),因此我遇到了一个名为"History Heuristic"的内容。
原始论文中的算法:
int AlphaBeta(pos, d, alpha, beta)
{
if (d=0 || game is over)
return Eval (pos); // evaluate leaf position from current player’s standpoint
score = - INFINITY; // preset return value
moves = Generate(pos); // generate successor moves
for i=1 to sizeof(moves) do // rating all moves
rating[i] = HistoryTable[ moves[i] ];
Sort( moves, rating ); // sorting moves according to their history scores
for i =1 to sizeof(moves) do { // look over all moves
Make(moves[i]); // execute current move
cur = - AlphaBeta(pos, d-1, -beta, -alpha); //call other player
if (cur > score) {
score = cur;
bestMove = moves[i]; // update best move if necessary
}
if (score > alpha) alpha = score; //adjust the search window
Undo(moves[i]); // retract current move
if (alpha >= beta) goto done; // cut off
}
done:
// update history score
HistoryTable[bestMove] = HistoryTable[bestMove] + Weight(d);
return score;
}
所以基本上,我们的想法是为之前的“移动”跟踪Hashtable或Dictionary。
现在我很困惑这个“移动”在这里意味着什么。 我不确定它是在字面上指的是每次移动后的单个移动还是整体状态。
在国际象棋中,例如,这个哈希表的“关键”应该是什么?
个别动作如(女王到位置(0,1))或(骑士到位置(5,5))?
或者个人移动后棋盘的整体状态?
如果是1,我猜在将“移动”记录到我的历史记录表中时,不会考虑其他部分的位置吗?
答案 0 :(得分:1)
我认为原始论文(历史启发式和实践中的Alpha-Beta搜索增强功能,Jonathan Schaeffer)在线可以清楚地回答这个问题。在论文中,作者将棋盘定义为棋盘上的2个索引(从square和to),使用64x64表(实际上,我认为他使用了位移和单个索引数组)来包含移动历史。 / p>
作者比较了所有可用的移动顺序,并确定hh是最好的。如果当前的最佳实践已经建立了一种改进的移动排序形式(除了hh +换位表),我也想知道它是什么。
答案 1 :(得分:0)
您可以使用转置表,以避免多次评估同一块电路板。换位意味着您可以通过以不同顺序执行移动来达到相同的电路板状态。天真的例子:
1. e4 e5 2. Nf3 Nc6
1. e4 Nc6 2. Nf3 e5
这些游戏会产生相同的位置,但达到的目的不同。
http://en.wikipedia.org/wiki/Transposition_table
一种常见的方法叫做Zobrist散列来散列国际象棋的位置:
答案 2 :(得分:0)
根据我的经验,与其他技术相比,历史启发式产生的效益可以忽略不计,并且对于基本搜索例程而言是不值得的。 不与使用转置表相同。如果后者是你想要实现的,我仍然会反对它。还有许多其他技术可以用更少的努力产生好的结果。实际上,高效且正确的换位表是在国际象棋引擎中编码最困难的部分之一。
首先尝试修剪并移动排序启发式算法,其中大多数是一行到几行代码。我在this post中详细介绍了这些技术,它还可以估算出您可以期待的性能提升。
答案 3 :(得分:0)
在国际象棋中,例如,"键"这个哈希表是吗?
- 个人动作如(女王到位置(0,1))或(骑士到位置(5,5))?
- 或个人移动后棋盘的整体状态?
键是个人移动,并且在录制"移动"时不考虑其他部分的位置。进入历史表。
历史表的传统形式(也称为蝴蝶板)类似于:
score history_table[side_to_move][from_square][to_square];
例如,如果移动e2-e4
产生一个截止,则元素为:
history_table[white][e2][e4]
以某种方式递增(与移动的位置无关)。
与示例代码中一样,历史启发式使用这些计数器进行移动排序。其他启发式方法可以利用历史表(例如延迟移动减少)。
考虑一下:
history_table[piece][to_square]
)。