我正在开展游戏Baroque Chess的爱好项目。对于那些没有玩过它的人来说,它具有与国际象棋相同的基本规则,但是移动和捕捉的方法是不同的。
当然,我为游戏创建了标准类:GameState
,Board
,Square
,以及为从BasePiece
继承的每个作品指定的类。
每件作品都有2个主要的虚拟方法,GetPossibleMoves(Board board)
和GetCapturedSquares(Board board, Square toSquare)
。
现在,其中一件作品the Imitator通过“模仿”它拍摄的作品来捕捉作品。例如,Long Leaper可以通过跳过它们捕获碎片。这意味着模仿者可以跳过敌人的Long Leapers捕捉它们(但不能跳过任何其他东西)。
我为除了模仿者之外的所有作品完成了GetCapturedSquares()
功能(这绝对是最狡猾的作品)。
我对模仿者的基本算法是:
由于我已经为其他部分的动作编写了代码,我想我会实例化新的部分并使用他们的GetCapturedSquares()
方法,具体取决于敌人的类型。为此,我设置了Dictionary
,如您所见,将System.Type
映射到所述类型的实例化对象:
var typeToPiece = new Dictionary<Type, BasePiece>()
{
{typeof(Pincer), new Pincer() { Color = this.Color, CurrentSquare = this.CurrentSquare}},
{typeof(Withdrawer), new Withdrawer() { Color = this.Color, CurrentSquare = this.CurrentSquare }},
{typeof(Coordinator), new Coordinator() { Color = this.Color, CurrentSquare = this.CurrentSquare }},
{typeof(LongLeaper), new LongLeaper() { Color = this.Color, CurrentSquare = this.CurrentSquare }},
{typeof(King), new King() { Color = this.Color, CurrentSquare = this.CurrentSquare }},
};
//...
var possibleMoves = typeToPiece[enemySquare.Occupant.GetType()].GetPossibleMoves(board, toSquare);
这样做会让我内心感到肮脏。是否更适合创建表示作为字典键的片段类型的enum
或string
,或者它真的无关紧要?有没有不同的方法来处理这个?我认为它的方式很好,但我很想听听你的想法。
答案 0 :(得分:7)
我认为你应该向abstract
添加BasePiece
方法,“克隆”当前的片段并返回模拟片段。
你在每种作品中都override
这种方法。要在它们之间共享代码,可以向基类添加protected
方法,将共享属性复制到传递给它的实例:
abstract class BasePiece {
protected BasePiece(BasePiece pieceToClone) {
this.Color = pieceToClone.Color;
this.CurrentSquare = pieceToClone.CurrentSquare;
}
public abstract BasePiece GetSimulatedClone();
}
class King : BasePiece {
protected King(King pieceToClone) : base(pieceToClone) { }
public King() { }
public override BasePiece GetSimulatedClone() {
return new King(this);
}
}
一般来说,每当你根据变量的类型进行切换时,你应该三思而后行,看看你是否可以改变多态性。
答案 1 :(得分:1)
我的想法更加优雅:
private abstract class Piece {}
private class King : Piece { }
private class Imitator : Piece { }
private void main(object sender, EventArgs e) {
Piece x;
x = CreateNewPiece(new King());
x = CreateNewPiece(new Imitator());
}
private T CreateNewPiece<T>(T piece) where T : Piece, new() {
return new T();
}
它依赖于new()
泛型约束来实例化一个类型变量。
答案 2 :(得分:1)
我个人同意你可以做你正在做的事情,因为它是一个小型的爱好项目。但是,如果你真的想解决你所描述的问题,你可以创建一个单独的Mover
对象层来处理围绕不同部分实际移动的逻辑。然后,不是要求一块它可以做什么动作,而是将该块的位置信息以及板状态信息传递给Mover,Mover告诉你该块可以做出什么动作。然后,与模仿者相关联的Mover可以是您描述的所有其他移动者的组合,但无需动态创建假“碎片”。
我提出的另一个建议,就是逻辑而不是你的模型,就是改变你的逻辑:
这是一个微妙的差异,但会显着减少所需的计算量。
Recursive的评论让我意识到我可能不太清楚Mover等级如何运作。想法是拥有一个KingMover,PincerMover等等,它们知道特定棋子类型的动作。由于它们与一件作品类型而非作品本身相关,因此它们甚至可能是单身人士。每个部分类型都可以有一个指向其移动者的Mover字段,然后您的业务逻辑可以直接调用Mover的GetPossibleMoves方法,或者您的部件的GetPossibleMoves方法可以简单地调用Mover的方法,将其自身作为参数传递。 ImitatorMover会知道彼此类型的移动器可能的移动,然后根据他们是否会攻击与该移动器相关联的类型来过滤这些移动。
您的代码与当前系统中的代码几乎相同,但每个代码的代码实际上只能专注于表示Piece的信息(位置,颜色等),而实际确定一个部分的代码移动将被移动到单独的类层中。每个班级都有一个目的。
答案 3 :(得分:1)
在没有触及这个特定问题的情况下,字典的概念没有任何内在错误,它允许您按类型查找对象。实际上,.NET FCL已经提供了这样一种类型 - 它被称为KeyedByTypeCollection<T>
。对于这样的事情可能更好,因为它确保对象的键是GetType()
(而不是其他随机类型)返回的类型,并且不允许添加两个相同类型的对象。 / p>