Conception orientée object pour un jeu d’échecs

J’essaie d’avoir une idée de la manière de concevoir et de réfléchir de manière orientée object et je veux obtenir des commentaires de la communauté sur ce sujet. Voici un exemple de jeu d’échecs que je souhaite concevoir de manière OO. Cette conception est très large et mon objective à ce stade est d’identifier qui est responsable de quels messages et comment les objects interagissent pour simuler le jeu. Veuillez indiquer s’il existe des éléments de mauvaise conception (couplage élevé, mauvaise cohésion, etc.) et comment les améliorer.

Le jeu d’échecs a les classes suivantes

  • Planche
  • Joueur
  • Pièce
  • Carré
  • Jeu d’échecs

Le Conseil est composé de carrés et le Conseil peut être chargé de créer et de gérer des objects Square. Chaque pièce est également sur un carré, donc chaque pièce a également une référence au carré sur lequel elle se trouve. (Est-ce que ça a du sens?). Chaque pièce est alors responsable de se déplacer d’un carré à l’autre. La classe de joueur détient des références à toutes les pièces qu’il possède et est également responsable de leur création (le joueur doit-il créer des pièces?). Le joueur a une méthode takeTurn qui à son tour appelle une méthode movePiece qui appartient à la pièce Class qui change l’emplacement de la pièce de son emplacement actuel à un autre emplacement. Maintenant, je suis confus quant à ce que doit être exactement la classe du Conseil. J’ai supposé que c’était nécessaire pour déterminer l’état actuel du jeu et savoir quand la partie est terminée. Mais quand une pièce change d’emplacement, comment le conseil devrait-il être mis à jour? Devrait-il conserver une rangée de carrés séparée sur laquelle les pièces existent et qui est mise à jour au fur et à mesure que les pièces se déplacent?

En outre, ChessGame crée initialement les objects Board et player qui créent à leur tour des carrés et des pièces et commencent la simulation. En bref, cela pourrait être ce que le code dans ChessGame peut ressembler

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

Je ne sais pas comment l’état du forum sera mis à jour. Le morceau devrait-il avoir une référence à la planche? Où devrait être la responsabilité? Qui détient quelles références? S’il vous plaît aidez-moi avec vos entrées et signaler les problèmes dans cette conception. Je ne mets pas délibérément l’accent sur des algorithmes ou d’autres détails du jeu, car je ne m’intéresse qu’à l’aspect design. J’espère que cette communauté peut fournir des informations précieuses.

En fait, je viens d’ écrire une implémentation C # complète d’un échiquier, de morceaux, de règles, etc. Voici comment je l’ai modélisé (l’implémentation a été supprimée car je ne veux pas prendre tout votre plaisir):

 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(ssortingng str) { // Parses ssortingngs 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 ssortingng AlgebraicNotation { get; } } public class Game { public Board Board { get; } public IList 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 { // .... } 

L’idée de base est que Game / Board / etc stocke simplement l’état du jeu. Vous pouvez les manipuler pour, par exemple, définir une position, si c’est ce que vous voulez. J’ai une classe qui implémente mon interface IGameRules qui est responsable de:

  • Déterminer quels mouvements sont valides, y compris le castling et en passant.
  • Déterminer si un coup spécifique est valide.
  • Déterminer quand les joueurs sont en échec / échec / échec.
  • Exécuter des mouvements.

Séparer les règles des jeux / classes de plateau signifie également que vous pouvez implémenter des variantes relativement facilement. Toutes les méthodes de l’interface de règles prennent un object de Game qu’ils peuvent inspecter pour déterminer quels mouvements sont valides.

Notez que je ne stocke pas les informations du joueur sur le Game . J’ai une Table classe séparée qui est chargée de stocker les métadonnées du jeu, par exemple qui a joué, quand le jeu a eu lieu, etc.

EDIT: Notez que le but de cette réponse n’est pas vraiment de vous donner le code de modèle que vous pouvez remplir – mon code contient en fait un peu plus d’informations sur chaque élément, plus de méthodes, etc. Le but est de vous guider vers le objective que vous essayez d’atteindre.

Voici mon idée, pour un jeu d’échecs assez basique:

 class GameBoard { IPiece config[8][8]; init { createAndPlacePieces("Black"); createAndPlacePieces("White"); setTurn("Black"); } createAndPlacePieces(color) { //generate pieces using a factory method //for eg 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 {....}