我试图了解如何以面向对象的方式进行设计和思考,并希望从社区获得有关此主题的一些反馈。以下是我希望以OO方式设计的国际象棋游戏的示例。这是一个非常广泛的设计,我在这个阶段的重点只是确定谁负责什么消息以及对象如何相互作用来模拟游戏。请指出是否有不良设计元素(高耦合,内聚力差等)以及如何改进它们。
国际象棋游戏有以下几个类
董事会由正方形组成,因此董事会可以负责创建和管理Square对象。每件作品也都放在一个正方形上,因此每件作品都可以参考它所在的正方形。 (这有意义吗?)。然后每件作品都有责任将自己从一个方格移动到另一个方格。 Player类拥有对他拥有的所有作品的引用,并且还负责创建它们(玩家应该创建Pieces吗?)。播放器有一个方法takeTurn,后者又调用一个方法movePiece,它属于片段Class,它将片段的位置从当前位置改变到另一个位置。现在我对Board类必须负责的内容感到困惑。我认为需要确定游戏的当前状态并知道游戏何时结束。但是当一个部分改变它的位置时,董事会应该如何更新?它应该保持一个单独的正方形数组,其中存在碎片并随着碎片的移动而得到更新吗?
此外,ChessGame最初创建棋盘和玩家对象,他们分别创建正方形和棋子并开始模拟。简而言之,这可能是ChessGame中的代码可能是什么样的
Player p1 =new Player();
Player p2 = new Player();
Board b = new Board();
while(b.isGameOver())
{
p1.takeTurn(); // calls movePiece on the Piece object
p2.takeTurn();
}
我不清楚董事会的状态将如何更新。应该是否有参考板?责任在哪里?谁持有什么参考?请帮助我提供您的输入并指出此设计中的问题。我故意不关注任何算法或游戏的进一步细节,因为我只对设计方面感兴趣。我希望这个社区可以提供有价值的见解。
答案 0 :(得分:53)
我实际上只是写了一个完整的国际象棋棋盘,片段,规则等的C#实现。这里大致是我如何建模它(实际实现已删除,因为我不想采取所有编码的乐趣):
public enum PieceType {
None, Pawn, Knight, Bishop, Rook, Queen, King
}
public enum PieceColor {
White, Black
}
public struct Piece {
public PieceType Type { get; set; }
public PieceColor Color { get; set; }
}
public struct Square {
public int X { get; set; }
public int Y { get; set; }
public static implicit operator Square(string str) {
// Parses strings like "a1" so you can write "a1" in code instead
// of new Square(0, 0)
}
}
public class Board {
private Piece[,] board;
public Piece this[Square square] { get; set; }
public Board Clone() { ... }
}
public class Move {
public Square From { get; }
public Square To { get; }
public Piece PieceMoved { get; }
public Piece PieceCaptured { get; }
public PieceType Promotion { get; }
public string AlgebraicNotation { get; }
}
public class Game {
public Board Board { get; }
public IList<Move> Movelist { get; }
public PieceType Turn { get; set; }
public Square? DoublePawnPush { get; set; } // Used for tracking valid en passant captures
public int Halfmoves { get; set; }
public bool CanWhiteCastleA { get; set; }
public bool CanWhiteCastleH { get; set; }
public bool CanBlackCastleA { get; set; }
public bool CanBlackCastleH { get; set; }
}
public interface IGameRules {
// ....
}
基本思想是游戏/棋盘/等只是存储游戏的状态。您可以将它们操纵到例如设置一个位置,如果这是你想要的。我有一个实现我的IGameRules接口的类,负责:
将规则与游戏/棋盘类分开也意味着您可以相对轻松地实现变体。规则接口的所有方法都采用Game
对象,他们可以检查这些对象以确定哪些移动有效。
请注意,我不会在Game
上存储播放器信息。我有一个单独的班级Table
,负责存储游戏元数据,例如谁在玩游戏,游戏发生时等等。
编辑:请注意,这个答案的目的并不是为您提供可以填写的模板代码 - 我的代码实际上存储了每个项目的更多信息,更多方法,目的是引导您实现您想要实现的目标。
答案 1 :(得分:4)
这是我的想法,对于一个相当基本的国际象棋游戏:
class GameBoard {
IPiece config[8][8];
init {
createAndPlacePieces("Black");
createAndPlacePieces("White");
setTurn("Black");
}
createAndPlacePieces(color) {
//generate pieces using a factory method
//for e.g. config[1][0] = PieceFactory("Pawn",color);
}
setTurn(color) {
turn = color;
}
move(fromPt,toPt) {
if(getPcAt(fromPt).color == turn) {
toPtHasOppositeColorPiece = getPcAt(toPt) != null && getPcAt(toPt).color != turn;
possiblePath = getPcAt(fromPt).generatePossiblePath(fromPt,toPt,toPtHasOppositeColorPiece);
if(possiblePath != NULL) {
traversePath();
changeTurn();
}
}
}
}
Interface IPiece {
function generatePossiblePath(fromPt,toPt,toPtHasEnemy);
}
class PawnPiece implements IPiece{
function generatePossiblePath(fromPt,toPt,toPtHasEnemy) {
return an array of points if such a path is possible
else return null;
}
}
class ElephantPiece implements IPiece {....}
答案 2 :(得分:0)
我最近用PHP(website click here,source click here)创建了一个象棋程序,并使其面向对象。这是我使用的课程。
generate_legal_moves()
代码放在这里。该方法有一个董事会,该董事会轮到该董事会,并提供一些变量来设置输出的详细程度,并且它将生成该职位的所有合法举动。它返回ChessMoves的列表。int
。我目前正在尝试将此代码转换为国际象棋A.I.,因此需要快速。我已经将generate_legal_moves()
函数从1500ms优化为8ms,并且仍在研究中。我从中学到的教训是...
int
。这就是为什么ChessSquare
将排名和文件存储为int
,而不是还存储具有人类可读的象棋正方形符号(例如“ a4”)的字母数字string
的原因。我知道这很老了,但希望对您有所帮助。祝你好运!