tablero programacion orientada objetos movimiento juegos fuente consola codigo caballo ajedrez design chess oop

design - programacion - tablero ajedrez c#



DiseƱo orientado a objetos para un juego de ajedrez (2)

Estoy tratando de tener una idea de cómo diseñar y pensar de una manera orientada a objetos y quiero obtener algunos comentarios de la comunidad sobre este tema. El siguiente es un ejemplo de un juego de ajedrez que deseo diseñar de manera OO. Este es un diseño muy amplio y mi enfoque en esta etapa es solo identificar quién es responsable de qué mensajes y cómo los objetos interactúan entre sí para simular el juego. Por favor señale si hay elementos de mal diseño (alto acoplamiento, mala cohesión, etc.) y cómo mejorarlos.

El juego de ajedrez tiene las siguientes clases

  • Tablero
  • Jugador
  • Pieza
  • Cuadrado
  • Ajedrez

La Junta se compone de cuadrados, por lo que la Junta se puede encargar de crear y gestionar objetos cuadrados. Cada pieza también está en un cuadrado, por lo que cada pieza también tiene una referencia al cuadrado en el que se encuentra. (¿Esto tiene sentido?). Cada pieza es responsable de moverse de una casilla a otra. La clase de jugador contiene referencias a todas las piezas que posee y también es responsable de su creación (¿Debería el jugador crear Piezas?). El jugador tiene un método takeTurn que a su vez llama a un método movePiece que pertenece a la pieza Class que cambia la ubicación de la pieza desde su ubicación actual a otra ubicación. Ahora estoy confundido sobre de qué exactamente es responsable la clase de la Junta. Supuse que era necesario determinar el estado actual del juego y saber cuándo terminó el juego. Pero cuando una pieza cambia su ubicación, ¿cómo debe actualizarse la placa? ¿debería mantener una matriz separada de cuadrados en los que existen piezas y que recibe actualizaciones a medida que las piezas se mueven?

Además, ChessGame crea inicialmente la Junta y los objetos del jugador que a su vez crean cuadrados y piezas respectivamente y comienzan la simulación. En pocas palabras, esto podría ser lo que el código en ChessGame puede parecer

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

No estoy seguro de cómo se actualizará el estado de la junta. ¿Debería la pieza tener una referencia al tablero? ¿Dónde debería estar la mentira de la responsabilidad? ¿Quién tiene qué referencias? Por favor ayúdenme con sus entradas y señale los problemas en este diseño. Deliberadamente, no me estoy centrando en ningún algoritmo ni en otros detalles del juego, ya que solo me interesa el aspecto del diseño. Espero que esta comunidad pueda proporcionar información valiosa.


De hecho, acabo de escribir una implementación completa de C # de un tablero de ajedrez, piezas, reglas, etc. He aquí más o menos cómo lo modelé (se eliminó la implementación real, ya que no quiero quitar toda la diversión de la codificación):

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 { // .... }

La idea básica es que Game / Board / etc simplemente almacena el estado del juego. Puede manipularlos para, por ejemplo, establecer una posición, si eso es lo que quiere. Tengo una clase que implementa mi interfaz IGameRules que es responsable de:

  • Determinar qué movimientos son válidos, incluidos el enroque y el en passant.
  • Determinar si un movimiento específico es válido.
  • Determinar cuándo los jugadores están en jaque / jaque mate / empate.
  • Ejecutando movimientos.

Separar las reglas de las clases de juego / tablero también significa que puede implementar variantes con relativa facilidad. Todos los métodos de la interfaz de reglas toman un objeto de Game que pueden inspeccionar para determinar qué movimientos son válidos.

Tenga en cuenta que no guardo información del jugador en el Game . Tengo una Table clase separada que se encarga de almacenar los metadatos del juego, como quién estaba jugando, cuándo tuvo lugar el juego, etc.

EDITAR: Tenga en cuenta que el propósito de esta respuesta no es darle un código de plantilla que pueda completar; mi código tiene un poco más de información almacenada en cada elemento, más métodos, etc. El propósito es guiarlo hacia el objetivo que estás tratando de lograr.


Esta es mi idea, para un juego de ajedrez bastante básico:

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