Las unidades Delphi no están "fundamentalmente rotas". La forma en que trabajan facilita la velocidad fenomenal del compilador y promueve diseños de clase limpios.
Ser capaz de distribuir clases sobre unidades de la manera que permite Prims/.NET es el enfoque que se puede decir que fundamentalmente se rompe, ya que promueve la organización caótica de clases al permitir al desarrollador ignorar la necesidad de diseñar adecuadamente su marco, promoviendo la imposición de reglas de estructura de código arbitrarias tales como "una clase por unidad", que no tiene ningún mérito técnico u organizativo como dictum universal.
En este caso, inmediatamente noté una idiosincrasia en el diseño de clase que surge de este dilema de referencia circular.
Es decir, ¿por qué una pieza alguna vez tiene alguna necesidad de hacer referencia a una placa?
Si una pieza es sacada de un tablero, tal referencia no tiene sentido, o tal vez los "MoveTargets" válidos para una pieza eliminada son solo los válidos para esa pieza como una "posición inicial" en un nuevo juego. Pero no creo que esto tenga sentido como algo más que una justificación arbitraria para un caso que exige que GetMoveTargets admita la invocación con una referencia de placa NIL.
La colocación particular de una pieza individual en un momento dado es una propiedad de un juego individual de ajedrez, y por igual las VÁLIDA mueve que puede ser POSIBLE para cualquier pieza dada dependen de la colocación de OTRAS piezas en el juego.
TChessPiece.GetMoveTargets no necesita el conocimiento del estado actual del juego. Esta es la responsabilidad de un TChessGame. Y un TChessPiece no necesita una referencia a un juego oa un tablero para determinar los objetivos de movimiento válidos desde una posición actual dada. Las restricciones de la placa (8 rangos y archivos) son constantes de dominio, no propiedades de una instancia determinada de la placa.
Así, un TChessGame se requiere que encapsula el conocimiento que combina el conocimiento de un tablero, las piezas y - especialmente - las reglas, pero el tablero y las piezas no necesitan el conocimiento de la otra o del juego.
Puede parecer tentador poner las reglas correspondientes a las diferentes piezas de la clase para el tipo de pieza en sí, pero esto es un error, ya que muchas de las reglas se basan en interacciones con otras piezas y, en algunos casos, con tipos de piezas Tales comportamientos de "panorama general" requieren un grado de visión excesiva (léase: descripción general) del estado total del juego que no es apropiado en una clase de pieza específica.
p. Ej. un TChessPawn puede determinar que un objetivo de movimiento válido es uno o dos cuadrados hacia adelante o un cuadrado diagonalmente hacia delante si cualquiera de esos cuadrados diagonales está ocupado. Sin embargo, si el movimiento del peón expone al Rey a una situación de CHECK, entonces el peón no se puede mover en absoluto.
Me acercaría a esto simplemente permitiendo que la clase de peones indique todos los objetivos de movimiento POSIBLES: 1 o 2 casillas hacia delante y diagonalmente hacia adelante. El TChessGame determina cuál de estos es válido por referencia a la ocupación de esos objetivos de movimiento y estado del juego.2 cuadros hacia adelante solo son posibles si el peón está en su rango principal, los cuadros delanteros están ocupados BLOQUEAR un movimiento = objetivo inválido, cuadrados diagonales NO ocupados FACILITAR un movimiento, y si cualquier movimiento válido expone al Rey, ese movimiento tampoco es válido.
Una vez más, la tentación podría ser la de poner las normas de aplicación general en la base TChessPiece clase (por ejemplo, no exponga un movimiento dado el rey?), Pero la aplicación de esa norma requiere el conocimiento del estado del juego en general - es decir, la colocación de otras piezas - por lo que más bien pertenece como un comportamiento generalizado de la clase TChessGame, en mi humilde opinión
Además de mover los objetivos, las piezas también tienen que indicar CaptureTargets, que en el caso de la mayoría de las piezas es el mismo, pero en algunos casos bastante diferentes: el peón es un buen ejemplo. Pero, una vez más, la captura de todas las posibles capturas, si es que la hay, es efectiva para cualquier movimiento dado, es una evaluación de las reglas del juego, no del comportamiento de una pieza o clase de piezas.
Como es el caso en el 99% de estas situaciones (ime - ymmv) el dilema quizás se resuelva mejor cambiando el diseño de clase para representar mejor el problema modelado, no encontrando la manera de calzar el diseño de clase en un arbitrario organización de archivos.
Aceptado esto, ya que es corto y proporciona una solución a mi pregunta. Voté todas las otras buenas respuestas. – jpfollenius
Me aparece el error "Tipo E2086 'tBaseChessBoard' aún no está completamente definido". – Ampere
@Alaun, esto no fue un snippit totalmente funcional, solo lo suficiente para mostrar el camino a una solución. El tBaseChessBoard debe tener métodos que sean necesarios para GetMoveTargets pero implementados como abstractos o virtuales. – skamradt