如何为游戏创建一个好的评估函数?

22

我有时编写程序来玩一些棋盘游戏变体。基本策略是标准的alpha-beta修剪或类似的搜索,有时会加入对结局或开局的通常方法。我大多数时间都在尝试下棋变种,所以当我需要选择评估函数时,我使用基本的国际象棋评估函数。

然而,现在我正在编写一个完全新的棋盘游戏程序。如何选择一个好的甚至是像样的评估函数呢?

主要的挑战在于同样的棋子总是在棋盘上,因此通常的材料函数不会根据位置改变,而且这个游戏还没被玩过一千次左右,所以人类不能通过足够的经验来提供洞察力。(PS.我考虑过MoGo方法,但随机游戏不太可能终止。)

游戏详细信息:该游戏在一个10x10的棋盘上进行,每方固定六个棋子。这些棋子有特定的移动规则和相互作用方式,但没有任何棋子被捕获。游戏的目标是使你的棋子足够多地位于棋盘上的某些特殊格子中。电脑程序的目标是提供一个与当前人类玩家相媲美或更优秀的选手。

8个回答

16
我将从一些基础知识开始,逐渐转向更难的内容。
基本代理和测试框架
无论采取什么方法,您都需要从非常简单和愚蠢的地方开始。对于愚蠢的代理来说,最好的方法是随机选择一个(生成所有可能的移动,随机选择一个)。这将作为比较所有其他代理的起点。您需要一个强大的框架进行比较。某些东西可以接受各种代理,允许在它们之间玩一些游戏,并返回性能矩阵。根据结果,您计算每个代理的适应度。例如,您的函数将在每对代理之间(第一/第二次)播放500场比赛,并返回类似以下内容的结果:
  x         -0.01       -1.484   |  -1.485
0.01          x         -1.29    |  -1.483
1.484       1.29          x      |  2.774

例如,我在这里使用2分制胜利、1分制平局的得分函数,最后将所有得分相加以找到适应度。该表格立即告诉我agent3是最好的,而agent1agent2并没有真正的区别。

因此,一旦设置了这两个重要的事项,您就准备好尝试评估函数了。


让我们从选择功能开始

  1. 首先需要创建一个"不差劲"的评估函数。这意味着该函数应正确识别三个重要方面(胜利/平局/失败)。虽然听起来显而易见,但我看到过很多机器人,其创建者无法正确设置这三个方面。

  2. 接着,您需要发挥人类智慧,找出游戏状态的一些特征。第一件事是与游戏专家交谈,询问他如何评估位置。

  3. 如果你没有专家,或者你只是五分钟前刚创造了游戏规则,请不要低估人类寻找模式的能力。即使玩几局游戏后,聪明的人也可以给你提出他应该如何进行的想法(这并不意味着他能够实现这些想法)。将这些想法用作特征。

  4. 此时,您并不真正需要知道这些特征会如何影响游戏。例如特征:棋子价值、棋子的移动范围、控制重要位置、安全性、总可能走步数、接近终点等。

  5. 在编写这些特征并单独使用它们以查看哪个效果最佳之后(不要急于丢弃单独执行效果不理想的特征,它们可能会在与其他特征相结合时有所帮助),您准备好尝试组合了。

通过组合和加权简单特征来构建更好的评估。有几种标准方法。
  1. 基于各种特征的组合创建一个超级函数。它可以是线性的 eval = f_1 * a_1 + ... f_n * a_nf_i 特征,a_i 系数),但它可以是任何形式。然后使用遗传算法实例化许多代理程序,其权重绝对随机地分配给此评估函数,并使它们相互对抗。使用测试框架比较结果,放弃一些明显的失败者并改变一些获胜者。继续同样的过程。(这是一个粗略的概述,请阅读更多关于 GA 的内容)

  2. 使用神经网络中的反向传播思想,从游戏的末尾向后传播误差以更新网络的权重。您可以阅读有关 backgammon 如何完成此操作的更多信息(我没有编写类似的内容,因此很抱歉短暂的说明)。

您可以不使用评估函数! 对于只听说过极小化/Alpha-Beta的人来说,这可能听起来很疯狂,但有些方法根本不需要评估。其中之一称为蒙特卡罗树搜索,正如名称中的蒙特卡罗所示,它使用了大量随机(它不应该是随机的,它可以使用您以前的良好代理)游戏玩法来生成树。这本身就是一个巨大的话题,因此我将给出我的高水平解释。您从根开始,创建您要尝试扩展的前沿。一旦您扩展了某些内容,您只需随机转到叶子。从叶子获取结果后,您会向上回溯结果。重复进行此操作多次,并收集有关当前前沿的每个子级的统计信息。选择最好的一个。其中有重要的理论涉及如何在探索和利用之间平衡,阅读的好东西是UCT(上置信区间算法)。

11

挑选几个候选的评估函数,例如移动力(可能的移动次数)减去对手的移动力,然后尝试找到每个度量标准的最佳权重。在评估函数中优化权重,遗传算法似乎效果很好。

使用随机权重创建一组群体,让它们相互对抗,限定深度和回合数,将失败者替换为获胜者的随机组合,洗牌并重复进行,每代结束后打印出群体平均值。直到你对结果满意或者看到需要调整某些指标的范围并重新尝试时,运行此过程。如果一个指标的最佳值可能超出初始范围,则需要进行调整。

最新补充: 我之前不知道的一种更为接受、研究、理解的方法是称为"Differential Evolution"。通过三个父代创建后代,避免了向平均值过早收敛的问题。


这对我来说听起来是一个不错的方法。+1(未注册 :() - Thomas Vultura
@ThomasVultura,请解释一下这个:“用赢家的随机组合替换输家”。你是如何进行繁殖的?你只是平均权重吗?我在这里发布了一个后续问题:https://stackoverflow.com/questions/45201979/genetic-algorithm-for-optimization-in-game-playing-agent-heuristic-evaluation-fu - Ryan J. Shrott

3
我会考虑使用监督式机器学习算法,例如强化学习。请查看棋盘游戏中的强化学习,这将为您提供一些好的研究方向。
此外,请查看基于强化学习的奥赛罗策略获取(PDF链接),在给定游戏规则的情况下,可以学习到良好的“回报函数”。这与TD-Gammon密切相关...

在训练期间,神经网络本身用于为双方选择移动...令人惊讶的发现是,即使在使用原始棋盘编码的零初始知识实验中,实际上也进行了大量学习。


2
如果没有人理解游戏,你就无法得到一个合适的评估函数。不要告诉我标准的alpha-beta算法加上物质计数对于国际象棋或其变种很好或甚至还可以(也许失败者棋是个例外)。
你可以尝试神经网络反馈或类似的机器学习算法,但它们通常需要大量的训练,而在这种情况下可能不可用。即使它们不差,你也不能从中获得知识。
我认为除了尽力了解游戏,并在开始时将未知因素作为评估函数的随机因素(或者直到未知因素变得更加明确),没有其他方法。
当然,如果你分享更多关于游戏的信息,你可以从社区获得更好的想法。

2
据我了解,您想要一个良好的静态评估函数,用于在min-max树的叶子节点上使用。如果是这样,最好记住这个静态评估函数的目的是为计算机玩家提供一个评分,以评估该棋局对计算机玩家而言有多么好。因此,如果f(board1)> f(board2),那么board1对计算机更好(它更有可能最终获胜)比board2。当然,没有任何静态函数能完全正确地适用于所有棋盘。
所以,您说“游戏的目标是在棋盘上的某些特殊方格中有足够数量的棋子”,因此,对于f(board),第一步就是简单地计算计算机在这些特殊方格上的棋子数量。然后您可以进一步完善它。
如果不知道游戏规则,就无法给出更好的猜测。如果您给出了游戏规则,我相信stackoverflow用户会能够为这样的函数提出大量原创性的想法。

1
谢谢您的评论。关于您最后的观点,我不想给出规则,因为我对创建或发现评估函数的一般方法感兴趣。实际上,我有多个完全不同的游戏需要编程。 - A. Rex

2

虽然你可以使用各种机器学习方法来得出一个评估函数(例如在gnubackgammon等项目中使用的TD-Learning就是一个例子),但结果肯定取决于游戏本身。对于双陆棋,它非常有效,因为游戏的随机性(掷骰子)迫使学习者探索可能不想探索的领域。如果没有这样一个关键的组成部分,你可能会得到一个只对自己有效而不对其他人有效的评估函数。

由于物质差异可能不适用,移动性的概念是否重要——即您有多少可能的移动?控制棋盘上的某个区域通常比不控制好吗?与玩游戏的人交流,找出一些线索。

虽然最好拥有尽可能好的评估函数,但您还需要调整搜索算法,以便能够尽可能地进行深入搜索。有时,这实际上更加重要,因为具有平庸评估函数的深度搜索器可以击败具有良好评估函数的浅层搜索器。这完全取决于领域。(例如gnubackgammon使用1层搜索进行专家游戏)

有其他技巧可以用来提高搜索的质量,最重要的是,使用置换表缓存搜索结果以进行良好的前向剪枝。
我强烈建议查看这些幻灯片。

1

你在选择算法时也需小心谨慎。如果你的算法与实际价值没有明显的关联,标准的人工智能函数将无法正常工作。为了有效,你的评估函数或者启发式函数必须要始终与实际价值相同或者低于实际价值,否则它会以一种奇怪的方式来引导你的决策。(对于棋类游戏,尽管我认为标准点数足够好),仍然可以争论这一点。

通常我会找出其能做到什么和需要什么。对于像推箱子这样的游戏,我使用的是将一个箱子(单独)从当前位置移动到任何目标位置所需的最少步数。虽然这不是所需步数的精确答案,但我认为它是一个相当好的启发式函数,因为它永远不会高估,并且可以预先计算整个游戏板上的值。在对局面进行得分时,只需将每个当前箱子位置的值累加即可。

在我编写的人工生命模拟中,旨在进化群体狩猎和防御能力。我使用的评分系统仅用于指导进化而非执行任何修剪。我为每个生物出生时赋予一个点数。对于它们在生命中消耗的每一点能量,我都会再给它们一个额外的点数。然后,我使用它们这一代的点数总和来确定每个生物繁殖的可能性。在我的情况下,我只是使用了它们所获得的这一代总点数的比例。如果我想进化出擅长逃避的生物,我会因被吃掉的点数而扣分。
你还应该注意,你的函数不要太难达成目标。如果你想进化出某些东西,你需要确保解决方案空间有一个合理的斜率。你需要引导进化朝着一个方向发展,而不仅仅是在它偶然命中时宣布胜利。
如果你提供更多信息,我很乐意尝试提供更多见解。此外,关于这个主题有很多优秀的书籍可供参考。
雅各布

2
因为您使用了“启发式”这个术语,我认为您的第一段是试图描述可接受性,这是单智能体搜索(例如解决谜题)而非双人游戏所面临的问题。 - Shaggy Frog
+1 很好的观点。谢谢。我同意你的看法。我在描述可接受性,并且稍后还提到了单人游戏。 - TheJacobTaylor

1
请记住,一个合适的评估函数是否存在并不一定是真实的。对于这个陈述,我假设评估函数必须具有较低的复杂度(P)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接