gpt4 book ai didi

c++ - 基于 C++ 的变体国际象棋引擎设计问题

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:27:05 24 4
gpt4 key购买 nike

我有一个国际象棋变体引擎可以玩 suicide chess和输家棋以及正常的国际象棋。随着时间的推移,我可能会为我的引擎添加更多变体。该引擎完全使用 C++ 实现,并正确使用了 OOP。我的问题与这种变体引擎的设计有关。

最初该项目只是一个自杀式引擎,但随着时间的推移,我添加了其他风格。为了添加新的变体,我首先尝试在 C++ 中使用多态性。例如,MoveGenerator抽象类有两个子类 SuicideMoveGeneratorNormalMoveGenerator根据用户选择的游戏类型,工厂将实例化正确的子类。但我发现这要慢得多 - 显然是因为实例化包含虚函数的类和在紧密循环中调用虚函数都非常低效。

但后来我想到使用具有模板特化的 C++ 模板来分离不同变体的逻辑,同时最大程度地重用代码。这似乎也很合乎逻辑,因为在上下文中动态链接并不是真正必要的,因为一旦您选择了游戏类型,您基本上就会坚持使用它直到游戏结束。 C++ 模板特化恰恰提供了这一点——静态多态性。模板参数是 SUICIDELOSERSNORMAL .

enum GameType { LOSERS, NORMAL, SUICIDE };

因此,一旦用户选择了游戏类型,就会实例化适当的游戏对象,并且从那里调用的所有内容都会适本地模板化。例如,如果用户选择自杀棋,可以说:

ComputerPlayer<SUICIDE>

对象被实例化,实例化基本上静态地链接到整个控制流。 ComputerPlayer<SUICIDE> 中的函数可以与 MoveGenerator<SUICIDE> 一起使用, Board<SUICIDE>依此类推同时对应NORMAL一个会适本地工作。

总的来说,这让我可以在开始时实例化正确的模板化专门类,而无需任何其他 if任何地方的条件,整个事情都完美无缺。最好的是根本没有性能损失!

然而,这种方法的主要缺点是使用模板会使您的代码更难阅读。如果处理不当,模板特化也可能导致重大错误。

我想知道其他变体引擎作者通常会做什么来分离逻辑(具有良好的代码重用性)?我发现 C++ 模板编程非常适合,但如果有更好的东西,我会很乐意接受。特别是,我检查了 HG Muller 博士的 Fairymax 引擎,但它使用配置文件来定义游戏规则。我不想这样做,因为我的许多变体都有不同的扩展名,并且通过使其通用到配置文件级别,引擎可能不会变得强大。另一个流行的引擎 Sjeng 散落着 if无处不在的条件,我个人认为这不是一个好的设计。

任何新的设计见解都会非常有用。

最佳答案

"Calling virtual functions in tight loops are inefficient"

如果这导致任何真正的膨胀,我会感到非常惊讶,如果循环的所有变量都具有相同的动态类型,那么我希望编译器从其 L1 缓存中获取相应的指令,因此不会受到太大影响。

但是有一点让我担心:

"obviously because instantiating classes containing virtual functions [is] quite inefficient"

现在……我真的很惊讶。

用虚函数实例化一个类的成本与实例化一个没有任何虚函数的类的成本几乎没有区别:它多了一个指针,仅此而已(在流行的编译器上,它对应于 _vptr).

我推测您的问题出在其他地方。所以我要大胆猜测:

  • 您是否有很多正在进行的动态实例化? (调用 new)

如果是这样,您将通过删除它们获得更多 yield 。

有一种称为Strategy 的设计模式,它非常适合您的具体情况。这种模式的想法实际上类似于虚函数的使用,但它实际上将这些函数外部化了。

这是一个简单的例子:

class StrategyInterface
{
public:
Move GenerateMove(Player const& player) const;
private:
virtual Move GenerateMoveImpl(Player const& player) const = 0;
};

class SuicideChessStrategy: public StrategyInterface
{
virtual Move GenerateMoveImpl(Player const& player) const = 0;
};

// Others

一旦实现,您需要一个函数来获得正确的策略:

StrategyInterface& GetStrategy(GameType gt)
{
static std::array<StrategyInterface*,3> strategies
= { new SuicideChessStrategy(), .... };
return *(strategies[gt]);
}

最后,您可以在不对其他结构使用继承的情况下委派工作:

class Player
{
public:
Move GenerateMove() const { return GetStrategy(gt).GenerateMove(*this); }

private:
GameType gt;
};

成本与使用虚函数非常相似,但是您不再需要为游戏的基本对象动态分配内存,而且堆栈分配要快得多。

关于c++ - 基于 C++ 的变体国际象棋引擎设计问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3693652/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com