我有一个国际象棋变体引擎,可以与普通国际象棋一起下自杀棋和失败者棋。随着时间的推移,我可能会为我的引擎添加更多变体。该引擎完全用 C++ 实现,并正确使用了 OOP。我的问题与这种变体引擎的设计有关。
最初,该项目作为一个仅用于自杀的引擎开始,随着时间的推移我添加了其他口味。为了添加新的变体,我首先尝试在 C++ 中使用多态性。例如,一个MoveGenerator
抽象类有两个子类SuicideMoveGenerator
,NormalMoveGenerator
并且根据用户选择的游戏类型,工厂将实例化正确的子类。但我发现这要慢得多——显然是因为实例化包含虚函数的类和在紧密循环中调用虚函数都非常低效。
但后来我想到使用具有模板专业化的 C++ 模板来分离不同变体的逻辑,最大限度地重用代码。这似乎也很合乎逻辑,因为动态链接在上下文中并不是真正必要的,因为一旦您选择了游戏类型,您基本上会坚持到游戏结束。C++ 模板专业化正是提供了这一点——静态多态性。模板参数是SUICIDE
orLOSERS
或NORMAL
。
enum GameType { LOSERS, NORMAL, SUICIDE };
因此,一旦用户选择了游戏类型,就会实例化适当的游戏对象,并且从那里调用的所有内容都将被适当地模板化。例如,如果用户选择自杀棋,让我们说:
ComputerPlayer<SUICIDE>
对象被实例化,并且该实例化基本上与整个控制流静态链接。中的函数ComputerPlayer<SUICIDE>
将与MoveGenerator<SUICIDE>
,Board<SUICIDE>
等一起使用,而相应的函数NORMAL
将适当地工作。
总的来说,这让我在一开始就实例化了正确的模板化专业类,并且在任何地方都没有任何其他if
条件,整个事情都很完美。最好的事情是根本没有性能损失!
然而,这种方法的主要缺点是使用模板会使您的代码更难阅读。如果处理不当,模板专业化也会导致重大错误。
我想知道其他变体引擎作者通常会为逻辑分离做什么(良好的代码重用)?我发现 C++ 模板编程非常合适,但如果有更好的东西,我很乐意接受。特别是,我检查了 HG Muller 博士的 Fairymax 引擎,但它使用配置文件来定义游戏规则。我不想这样做,因为我的许多变体都有不同的扩展名,并且通过使其通用到配置文件的级别,引擎可能不会变得强大。另一种流行的引擎 Sjeng 到处都是if
条件,我个人觉得这不是一个好的设计。
任何新的设计见解都会非常有用。