首先,您可以检查我多年前编写的跳棋 AI 的这段代码。有趣的部分是最后一个函数 ( alphabeta
)。(它在 python 中,但我认为你可以像伪代码一样看待它)。
显然我不能教你所有的阿尔法/贝塔理论,因为它可能有点棘手,但也许我可以给你一些实用的技巧。
评价功能
这是良好的最小/最大 alpha/beta 算法(以及任何其他知情搜索算法)的关键点之一。编写一个好的启发式函数是 AI 开发的艺术部分。您必须非常了解游戏,与专家级玩家交谈以了解哪些棋盘特征对回答以下问题很重要:玩家 X 这个位置有多好?
您已经指出了一些不错的功能,例如机动性、稳定性和自由角。但是请注意,评估函数必须快速,因为它会被调用很多次。
一个基本的评价函数是
H = f1 * w1 + f2 * w2 + ... + fn * wn
其中f
是一个特征分数(例如自由角的数量),w
是一个相应的权重,表示特征 f 在总分中的重要性。
只有一种方法可以找到权重值:经验和实验。;)
基本算法
现在您可以从算法开始。第一步是了解游戏树导航。在我的 AI 中,我只是将主板用作黑板,AI 可以在其中尝试移动。
例如,我们从某个配置B1中的板开始。
第 1 步:获取所有可用的招式。您必须为给定玩家找到所有适用于 B1 的移动。在我的代码中,这是由self.board.all_move(player)
. 它返回一个移动列表。
第 2 步:应用移动并开始递归。假设函数返回了三个动作(M1、M2、M3)。
- 采取第一步 M1 并应用它以获得新的板配置 B11。
- 在新配置上递归应用算法(找到 B11 中适用的所有移动,应用它们,递归结果,......)
- 撤消移动以恢复 B1 配置。
- 采取下一步行动 M2 并应用它以获得新的板配置 B12。
- 等等。
注意:步骤 3 只能在所有移动都可逆的情况下完成。否则,您必须找到另一种解决方案,例如为每个动作分配一个新棋盘。
在代码中:
for mov in moves :
self.board.apply_action(mov)
v = max(v, self.alphabeta(alpha, beta, level - 1, self._switch_player(player), weights))
self.board.undo_last()
第三步:停止递归。这三个非常深,因此您必须对算法进行搜索限制。n
一个简单的方法是在关卡之后停止迭代。例如,我从B1和.max_level=2
current_level=max_level
- 例如,我从 B1 (current_level 2) 应用 M1 移动以获得 B11。
- 从 B11 (current_level 1) I apple,例如,M2 移动获得 B112。
- B122 是“current_level 0”板配置,所以我停止递归。我返回应用于 B122 的评估函数值,然后返回到级别 1。
在代码中:
if level == 0 :
value = self.board.board_score(weights)
return value
现在...标准算法伪代码返回最佳叶子值的值。但是我想知道哪个动作能把我带到最好的叶子!为此,您必须找到一种将叶值映射到移动的方法。例如,您可以保存移动序列:从 B1 开始,序列 (M1 M2 M3) 将玩家带入棋盘 B123,值为 -1;序列(M1 M2 M2)将玩家带入棋盘 B122,值为 2;等等...然后您可以简单地选择将 AI 带到最佳位置的动作。
我希望这会有所帮助。
编辑:关于alpha-beta 的一些注释。没有图形示例很难解释 Alpha-Beta 算法。出于这个原因,我想链接我发现的最详细的 alpha-beta 修剪解释之一:this one。我想我真的不能做得比这更好。:)
关键点是:Alpha-beta 剪枝为节点添加了 MIN-MAX 两个边界。此边界可用于决定是否应扩展子树。
这个界限是:
- Alpha:可能解决方案的最大下限。
- Beta:可能解决方案的最小上限。
如果在计算过程中,我们发现Beta < Alpha
可以停止对该子树的计算。
显然检查上一个链接以了解它是如何工作的。;)