国际象棋游戏的面向对象设计

时间:2010-11-12 18:47:25

标签: chess oop

我试图了解如何以面向对象的方式进行设计和思考,并希望从社区获得有关此主题的一些反馈。以下是我希望以OO方式设计的国际象棋游戏的示例。这是一个非常广泛的设计,我在这个阶段的重点只是确定谁负责什么消息以及对象如何相互作用来模拟游戏。请指出是否有不良设计元素(高耦合,内聚力差等)以及如何改进它们。

国际象棋游戏有以下几个类

  • 播放器
  • ChessGame

董事会由正方形组成,因此董事会可以负责创建和管理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();

}

我不清楚董事会的状态将如何更新。应该是否有参考板?责任在哪里?谁持有什么参考?请帮助我提供您的输入并指出此设计中的问题。我故意不关注任何算法或游戏的进一步细节,因为我只对设计方面感兴趣。我希望这个社区可以提供有价值的见解。

3 个答案:

答案 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接口的类,负责:

  • 确定哪些动作有效,包括castling和en passant。
  • 确定特定移动是否有效。
  • 确定玩家何时处于检查/将死/僵局。
  • 执行动作。

将规则与游戏/棋盘类分开也意味着您可以相对轻松地实现变体。规则接口的所有方法都采用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 heresource click here)创建了一个象棋程序,并使其面向对象。这是我使用的课程。

  • ChessRulebook(静态)-我将所有generate_legal_moves()代码放在这里。该方法有一个董事会,该董事会轮到该董事会,并提供一些变量来设置输出的详细程度,并且它将生成该职位的所有合法举动。它返回ChessMoves的列表。
  • ChessMove-存储创建algebraic notation所需的所有内容,包括开始方块,结束方块,颜色,样片类型,捕获,检查,校验,提升样片类型和整个过程。可选的其他变量包括消歧(针对Rae4等动作),小腿和板。
  • ChessBoard-存储与Chess FEN相同的信息,包括表示正方形并存储ChessPieces的8x8数组,该象棋的轮流是通过的目标正方形,铸造权限,半移动时钟和全移动时钟。 / li>
  • ChessPiece-存储棋子类型,颜色,正方形和棋子值(例如,典当= 1,骑士= 3,rook = 5等)
  • ChessSquare-将排名和文件存储为int

我目前正在尝试将此代码转换为国际象棋A.I.,因此需要快速。我已经将generate_legal_moves()函数从1500ms优化为8ms,并且仍在研究中。我从中学到的教训是...

  • 默认情况下,不要在每个ChessMove中存储整个ChessBoard。仅在需要时才将板存放在移动中。
  • 尽可能使用原始类型,例如int。这就是为什么ChessSquare将排名和文件存储为int,而不是还存储具有人类可读的象棋正方形符号(例如“ a4”)的字母数字string的原因。
  • 程序在搜索移动树时会创建数以万计的ChessSquares。我可能会将该程序重构为不使用ChessSquares,这应该可以提高速度。
  • 不要在您的类中计算任何不必要的变量。最初,在我的每个国际象棋棋盘中计算FEN确实是在破坏程序的速度。我必须用profiler找出答案。

我知道这很老了,但希望对您有所帮助。祝你好运!