8

我有一个基类

 class piece;

和一个包含派生对象的数组

piece* board[8][8];

通过虚拟功能获得优势、简洁的设计。缺点是,如果我必须在棋盘中找到一块或比较一块,我必须恢复为动态转换(或 typeid)。它很丑陋,在发出数百万个请求时可能会导致性能下降。

另一方面,如果我制作一个单件类的数组,它有一个用于识别件的类型字段,我没有这个问题(它应该更快)但我必须制作超级丑陋的 switch 语句。我想由于零件的数量是有限的,而且我认为自己不会进行那么多开关,这最终可能是一个更好的选择,你觉得呢?

这是为了好玩(所以没有位板)。

阅读一些答案,我认为仅将类型字段用于运算符重载 ( ==, !=, ...) 可以充分利用这两个词。

boost::variant看起来也很有趣。

4

6 回答 6

5

或者,如果您的类集有限 - 即您知道数量,请使用变体和访问者。例如,boost::variant<king, queen, bishop, knight ...>而棋盘是由这种类型的二维数组组成的。现在要审讯,您可以使用访问者...

于 2010-12-31T13:51:42.377 回答
4

我会选择类层次结构。

为了找到一件作品,您可以为每种作品类型保留一个单独的列表。所以你知道在哪里寻找每种类型的作品。

为了比较,您也可以依赖虚拟方法。

另一种方法是使用组件架构(如此处所述:http: //cowboyprogramming.com/2007/01/05/evolve-your-heirachy/),但我认为这对于您清楚知道的国际象棋游戏来说太多了类型并知道这些类型不会很快改变:)。

于 2010-12-31T13:40:49.240 回答
1

我会使用层次结构,如果我想知道类型(为什么?)有一个标识类型的虚拟方法。

于 2010-12-31T13:37:57.177 回答
1

我从来没有写过国际象棋程序,但我猜最常见的操作是这样的:

  • 显示/打印板
  • 获取每件可能的动作集
  • 总结一个棋子的所有棋子的值,也许总结某种取决于棋子的“位置值”(在开放线上的车,诸如此类)

此外,一些棋子具有“状态”(如果国王之前没有移动过,则只能城堡,如果另一个棋子刚刚移动了两个方格,则棋子可以通过攻击)仅适用于一种棋子。

这一切都向我表明了阶级等级制度。(假设您不需要位板性能)

另一方面,您不太可能必须添加新的片段类型,或者您将能够在分离中重新使用其中一种片段类型。即可扩展性和模块化并不是真正的问题。因此,如果您发现算法的某些重要部分确实应该在一个地方分散在多个片段类中 - 使用 switch 语句。Piece只需在返回枚举的类中添加一个抽象方法PieceType并打开它。

于 2010-12-31T14:09:45.143 回答
1

您不能同时担心性能和代码的乐趣:)

考虑使用“nibbleboard”(或至少是 byteboard)而不是 bitboard,其中每个 nibble 代表一种类型。每个半字节也是对该片段类型进行操作的单例对象表中的索引。

class Empty : public Piece {};
class Rook : public Piece {};
...

const int wrook = 1;
...
const int bpawn = 12;

Piece* Operator[13] = {new Empty(), new Rook(), ..., new Pawn()};

byte table[64] = {
    wrook, wbishop, wknight, wking, wqueen, wknight, wbishop, wrook,
    wpawn, wpawn, wpawn, wpawn, wpawn, wpawn, wpawn, wpawn, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    bpawn, bpawn, bpawn, bpawn, bpawn, bpawn, bpawn, bpawn, 
    brook, bbishop, bknight, bking, bqueen, bknight, bbishop, brook};

// Given some position and some operation DoSomething we would have this:
Operator[table[position]]->DoSomething(table, position, <other parameters>);

// Possible return value of DoSomething might be new table
于 2010-12-31T14:42:15.297 回答
0

“超级丑陋的 switch 语句”是正确的技术。它并不丑陋。它被称为函数式编程。

继承是完全错误的技术。每个部分都以不同的方式移动,具有不同的图形和其他属性。没有什么共同点。棋子不是抽象的。它们是离散对象的具体集合。

你必须通过统一来实现一些共同点:创建所谓的 sum 类型。在 Ocaml 中:

type shape = Pawn | Rook | Knight | Bishop | Queen | King
type color = Black | White
type piece = shape * color
type pos = { row:int;  col:int }

let check_white_move piece board from to = match piece with
| Pawn -> on_board to && (from.row = 2 && to.row = 4 or to.row = from.row + 1)
| ....

在 C++ 中没有正确的 sum 类型,您可以使用:

enum shape { pawn, rook, knight, bishop, queen, king};
..
bool check_white_move (..) { switch piece {
 case pawn: ...

它更笨拙。向 C 和 C++ 委员会投诉。但是使用正确的概念。求和类型(可区分联合、变体)是统一离散的具体类型集的方法类和继承用于表示抽象并提供其实现。

国际象棋没有什么抽象的。这都是关于组合的。这不是不同技术的优缺点的问题:它是关于使用正确的技术。

[顺便说一句:是的,你可以尝试 boost 变体,虽然我不能推荐它用于这个应用程序,因为这些片段没有关联的数据,枚举是完美的]

于 2010-12-31T14:38:22.847 回答