Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ A C++;基于遗传算法的象棋引擎设计问题_C++_Templates_Chess - Fatal编程技术网

C++ A C++;基于遗传算法的象棋引擎设计问题

C++ A C++;基于遗传算法的象棋引擎设计问题,c++,templates,chess,C++,Templates,Chess,我有一个国际象棋引擎,可以和普通的国际象棋一起玩输棋。随着时间的推移,我可能会为我的引擎添加更多的变体。该引擎完全采用C++实现,并正确使用面向对象编程。我的问题是关于这种变型发动机的设计 起初,这个项目只是一个自杀式的引擎,随着时间的推移,我加入了其他的口味。为了增加新的变体,我首先使用C++中的多态性进行了实验。例如,一个MoveGenerator抽象类有两个子类sequestemovegenerator和NormalMoveGenerator,根据用户选择的游戏类型,工厂将实例化正确的子类

我有一个国际象棋引擎,可以和普通的国际象棋一起玩输棋。随着时间的推移,我可能会为我的引擎添加更多的变体。该引擎完全采用C++实现,并正确使用面向对象编程。我的问题是关于这种变型发动机的设计

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

<>但是,我想到了使用模板专用的C++模板,为不同的变量分离逻辑,最大限度地重用代码。这看起来也很合乎逻辑,因为动态链接在上下文中并不是真正必要的,因为一旦你选择了游戏类型,你基本上会坚持到游戏结束。C++模板专门化提供了完全的静态多态性。模板参数为
自杀
失败者
正常

enum GameType { LOSERS, NORMAL, SUICIDE };
所以一旦用户选择了一个游戏类型,相应的游戏对象就会被实例化,从那里调用的所有东西都会被适当地模板化。例如,如果用户选择自杀式国际象棋,则假设:

ComputerPlayer<SUICIDE>
ComputerPlayer
对象被实例化,并且该实例化基本上静态地链接到整个控制流。
ComputerPlayer
中的功能将与
MoveGenerator
Board
等一起工作,而相应的
正常
功能将正常工作

总的来说,这让我能够在一开始就实例化正确的模板化专用类,而无需任何其他
条件,整个过程都可以完美运行。最好的事情是根本没有表现上的惩罚

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

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


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

我不太确定这是否合适,但您可以通过对原始设计稍作修改来实现静态多态性

“在紧密循环中调用虚拟函数效率低下”

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

但有一点让我担心:

“显然,因为实例化包含虚拟函数的类[效率]很低”

现在。。。我真的很惊讶

使用虚拟函数实例化一个类的成本与不使用任何虚拟函数实例化一个类的成本几乎是无法区分的:这只是多了一个指针,仅此而已(在流行的编译器上,它对应于
\u vptr

我猜想你的问题在别处。所以我要做一个大胆的猜测:

  • 您是否有机会进行大量动态实例化?(正在调用
    新建
如果是这样的话,你可以通过移除它们获得很多好处

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

下面是一个简单的例子:

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]);
}

成本与使用虚拟功能非常相似,但是您不再需要为游戏的基本对象动态分配内存,堆栈分配速度也快得多。

我个人认为ComputerPlayer的读写难度不会比自杀电脑玩家高,但如果您这样做,您可能真的将其称为自杀计算机层(有继承或没有继承),并使用Traits技术()来定义哪些类属于同一个类。我同意这并不难理解,但它不再像以前那样清晰,因为一旦你的大多数类被模板化,所有的方法,调用语义,所有的东西都使用模板构造,这会导致不必要的冗长和膨胀。事实上,我对当前的设计还可以,但我想探索一下是否有更好的设计可以使用。策略模式非常适合。我相信我可以使用它,如果我能确保它不会因为虚拟功能而减慢速度。为了回答您关于动态实例化的问题,是的,我在前面提到的非模板设计中使用了很多动态实例化,但似乎没有办法解决。当谈到使用虚拟函数时,我甚至认为不会有任何明显的性能损失,但这是错误的。国际象棋引擎是非常计算密集型的
class Player
{
public:
  Move GenerateMove() const { return GetStrategy(gt).GenerateMove(*this); }

private:
  GameType gt;
};