国际象棋:高分支因子

时间:2013-05-11 19:08:48

标签: chess alpha-beta-pruning iterative-deepening

我正在尝试开发一个简单的国际象棋引擎,但我正在努力实现其性能。我已经使用alpha-beta修剪和迭代加深实现了Negamax(没有任何额外的启发式),但是我无法获得超过3-4层的合理搜索时间。以下是从游戏开始我的程序日志的摘录:

2013-05-11 18:22:06,835 [9] INFO  CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Searching at depth 1
2013-05-11 18:22:06,835 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Leaves searched: 28
2013-05-11 18:22:06,835 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Nodes searched: 28
2013-05-11 18:22:06,835 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Found PV: A4->A6 
2013-05-11 18:22:06,835 [9] INFO  CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Searching at depth 2
2013-05-11 18:22:06,897 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Leaves searched: 90
2013-05-11 18:22:06,897 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Nodes searched: 118
2013-05-11 18:22:06,897 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Found PV: A2->A3 B7->B6 
2013-05-11 18:22:06,897 [9] INFO  CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Searching at depth 3
2013-05-11 18:22:08,005 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Leaves searched: 6027
2013-05-11 18:22:08,005 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Nodes searched: 6414
2013-05-11 18:22:08,005 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Found PV: A2->A3 A6->B8 A4->A7 
2013-05-11 18:22:08,005 [9] INFO  CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Searching at depth 4
2013-05-11 18:22:10,485 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Leaves searched: 5629
2013-05-11 18:22:10,485 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Nodes searched: 6880
2013-05-11 18:22:10,485 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Found PV: D2->D4 A6->B8 C4->C5 A7->A6 
2013-05-11 18:22:10,485 [9] INFO  CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Searching at depth 5
2013-05-11 18:22:34,353 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Leaves searched: 120758
2013-05-11 18:22:34,353 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Nodes searched: 129538
2013-05-11 18:22:34,353 [9] DEBUG CoevolutionaryChess.Engine.MoveSearchers.NegamaxMoveSearcher [(null)] - Found PV: D2->D4 A6->B8 C4->C5 A7->A6 A4->A6 

它显示分支因子大约为10.我已经读过,通过适当的移动顺序,我应该得到6左右的东西,所以我怀疑我的排序是错误的。它目前以这种方式工作:

  1. 游戏树节点有一个孩子的链表;最初,捕获和促销活动放在安静的行动之前
  2. 在搜索过程中,增加alpha或导致截止的孩子会被放在列表的开头
  3. 在下一次加深PV的迭代中应首先搜索
  4. 订购动作是否合适?我得到的分支因素是预期的?目前我正在使用一个简单的静态评估函数,只考虑位置的材料差异 - 它是否是低截止率的原因(如果还考虑了数字的移动性,我会得到类似的结果)?诸如null移动减少或杀手启发式等技术是否会有显着帮助(不是10-15%,而是一个数量级)?我不希望我的引擎变强,但我希望分支因子大约为6。

2 个答案:

答案 0 :(得分:31)

我也在C#中开发了一个国际象棋引擎,它的分支因子大约为2.5。绝对可以通过多个数量级的数量来改进您的发动机。如今,一般的策略是基于良好的移动顺序使用非常积极的移动修剪。你为了能够看到一些深刻的战术线而牺牲了一些正确性。

以下是我发现最有效的技术概述。请注意,某些组件是补充组件而其他组件是替代组件,因此我给出的结果是一般准则。如果你没有坚实的基础,那么列表末尾的巨大收益是不可能的。

  1. negamax只需alpha-beta pruning:3秒内深度为4。

  2. 添加iterative deepeningnull move heuristic:深度5.迭代深化在这一点上并没有真正帮助,但它很容易实现。 null 移动包括跳过你的转弯,看看你是否还能得到 一个带有浅层搜索的beta截止值。如果可以,那很可能 修剪树是安全的,因为它几乎总是有利的 移动。

  3. Killer heuristic:深度6.这涉及存储移动 导致beta截止并且如果下次合法则先尝试它们 你是在同一个深度。你似乎在做类似的事情 已经。

  4. MVV/LVA ordering:深度8.基本上,你想要捕获它 在移动的顶部有很多潜在的物质净收益 名单。因此,如果一个pawn捕获了一个女王,你应该首先搜索它。

  5. Bitboard representation:深度10.这不会改善分支 因素,但这是我达到这一点时所做的。放弃了 数组,改为使用UInt64,并使用make / unmake而不是copy-make。如果你觉得困难,你不需要使用魔术位板;有更简单的方法仍然非常快。位板大大提高了性能 并且可以轻松编写评估组件。我去了 perft(6)需要几分钟才需要3秒钟。 (顺便说一下,编写一个perft函数是确保移动生成正确性的好方法)

  6. Transposition table:深度13.这提供了很大的收益,但也是 很难做对。绝对肯定你的位置哈希 在实现表之前是正确的。大多数好处来自 订购桌子的惊人举动给你。始终存储最好的 进入桌子,每当你得到一个匹配的位置,试试吧 第一。

  7. Late move reductions:深度16.这极大地增加了您的搜索深度,但强度增加更多 人工比其他技术。基本上你的动作订购 现在很好,你只需要完全搜索前几个 在节点中移动,您可以通过浅搜索检查其他节点。

  8. Futility pruning:深度17.通过跳过移动来修剪叶节点 在查看潜力时,提高节点价值的可能性很小 物质利益。如果移动的净潜在增益+位置的静态评估低于位置的当前值,则跳过 对此举的评价。

  9. 还有其他各种组件也有帮助,但大多数都是次要的,有些是专有的。 :D然而,并不是所有关于高搜索深度和低分支因素。像quiescence search这样的东西会恶化搜索深度,但对于任何引擎来说都是必需的。没有它,您的引擎将遭受巨大的战术错误。您可能还需要考虑check extensionssingle reply extensions。我还建议至少在评估函数中引入piece-square tables。这是一种非常简单的方法,可以大大提高程序的位置知识;你可能会看到你的引擎玩更常见的开口。国际象棋编程是一个有趣的爱好,我希望信息量不会让你气馁!

答案 1 :(得分:2)

您可以使用多种启发式方法来降低分支因子。

首先,您应该使用transposition table (TT)来存储位置结果,深度和最佳移动。在搜索移动之前,首先要检查是否已经在深度> =搜索到您要搜索的深度。如果是,您只需使用表中的结果即可。如果不是,您仍然可以使用表格中的移动作为第一次搜索。

如果TT中的某个位置(搜索内)没有匹配项,则可以使用Iterative Deepening (ID)。您不必搜索N的深度,而是首先搜索N-2的深度。这将非常快,并且会让您在深度N处首先进行搜索。

还有Null Move Pruning。与Alpha-Beta结合使用(Negamax是Alpha-Beta的变体)会大大降低您的分支因子。想法是在搜索位置之前,尝试空移动(不播放)并进行缩小搜索(N-2或N-3)。减少搜索将非常快。如果空移动搜索的结果仍然高于beta,则意味着位置非常糟糕,您不再需要搜索它(并非总是如此,但大部分时间都是如此)。

当然,您可以使用多种其他启发式方法来改善您的move ordering,这将改善您的分支因素。