首页 文章

国际象棋游戏的面向对象设计[关闭]

提问于
浏览
83

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

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

  • 董事会

  • Player

  • 广场

  • 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();

}

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

3 回答

  • 0

    我实际上只是写了一个完整的C#执行棋盘,棋子,规则等等 . 这里's roughly how I modeled it (actual implementation removed since I don' t想要从编码中获取所有乐趣):

    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 负责存储游戏元数据,例如谁在玩游戏,游戏发生时等等 .

    EDIT: 请注意,这个答案的目的并非是为了实现目标 .

  • 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 {....}
    
  • 51

    我最近用PHP创建了一个国际象棋程序(website click heresource click here),我把它作为面向对象的 . 这是我使用的课程 .

    • ChessRulebook(静态) - 我把所有 generate_legal_moves() 代码放在这里 . 该方法给出了一个板,轮到它,以及一些变量来设置输出的详细程度,并为该位置生成所有合法的移动 . 它返回一个ChessMoves列表 .

    • ChessMove - 存储创建algebraic notation所需的一切,包括起始方块,结束方块,颜色,块类型,捕获,检查,将死,促销片类型和en passant . 可选的附加变量包括消歧(对于像Rae4这样的动作),铸造和板 .

    • ChessBoard - 存储与Chess FEN相同的信息,包括表示正方形和存储ChessPieces的8x8数组,其转向,通过目标方,铸造权,半移动时钟和完全移动时钟 .

    • ChessPiece - 存储片段类型,颜色,方形和片段值(例如,pawn = 1,knight = 3,rook = 5等)

    • ChessSquare - 将等级和文件存储为 int .

    我目前正在尝试将此代码转换为国际象棋A.I.,因此它需要快速 . 我已经将 generate_legal_moves() 功能从1500ms优化到8ms,我仍在努力 . 我从中学到的经验是......

    • 默认情况下,不要在每个ChessMove中存储整个ChessBoard . 只在需要时将电路板存放在移动中 .

    • 尽可能使用原始类型,例如 int . 这就是为什么 ChessSquare 存储等级和文件为 int ,而不是存储字母数字 string 与人类可读的象棋方形符号,如"a4" .

    • 该程序在搜索移动树时会创建数万个ChessSquare . 我可能会重构该程序以不使用ChessSquares,这应该会提高速度 .

    • 不要在类中计算任何不必要的变量 . 最初,计算每个ChessBoards中的FEN实际上是在扼杀程序的速度 . 我不得不用profiler找到它 .

    我知道这是旧的,但希望它对某人有所帮助 . 祝好运!

相关问题