Шахматный движок. Можно ли передавать ссылку на объект доски фигуре?

Я создаю шахматный движок. У меня есть интерфейс Piece, Rook, Bishop и т. Д. Реализует этот класс. Моя доска представляет собой массив Piece [] []. Допустим, белый игрок хочет переместить слона. Координаты места назначения и табличку передаю епископу. Слон проверяет, находится ли пункт назначения на той же диагонали, затем он спрашивает доску, нет ли фигур между его положением и полем назначения. Это нормально с точки зрения ООП? Спасибо


person user1872329    schedule 18.04.2015    source источник
comment
Это нормально с точки зрения ООП, но может вызвать у некоторых сторонников различных схем MVC и прочего толку.   -  person Hot Licks    schedule 18.04.2015
comment
Спасибо. У меня есть класс GameController, он ищет чеки, мат, сохраняет историю. Было бы лучше, если бы Бишоп проверял, только если он находится на той же диагонали, а GameController проверяет квадраты между ними?   -  person user1872329    schedule 18.04.2015
comment
Обычно лучше, если вы сконцентрируете подобную логику в одном месте. Например, епископ спрашивает объект доски, есть ли какие-то фигуры, о которых ему может понадобиться знать, предоставляя параметры, описывающие, что интересно, и другие части будут делать то же самое.   -  person Hot Licks    schedule 18.04.2015


Ответы (3)


Это немного деликатно.

Точка зрения ООП

С точки зрения ООП, можно было бы задаться вопросом, должна ли Board быть Piece[][] массивом в первую очередь. Хороший, чистый, объектно-ориентированный дизайн здесь, вероятно, будет включать в себя что-то вроде

interface Board {
    Piece get(int r, int c);

    // Should this be here? See below...
    void set(int r, int c, Piece p);
}

И тогда возникает один важный вопрос: поместится ли Слон «себя» в целевую позицию данной доски? Или, сосредотачиваясь на точке зрения ООП: является ли доска, переданная части, изменяемой или она «только для чтения»? Можно представить себе класс MaliciousBishop, который, получив Board, убивает короля противника, занимая позицию короля.

С очень высокоуровневой абстрактной точки зрения ООП можно было бы задаться вопросом, должен ли Piece вообще иметь какой-либо интеллект. Piece - тупой кусок пластика. Ему ничего не известно о шахматных правилах. Вы (игрок) можете разместить эту фигуру где угодно, подчиняясь или игнорируя правила шахмат. Так что подчиняться или даже проверять какие-либо правила - это, конечно, не работа пьесы. Можно сказать, что подчинение правилам - это то, что ожидается от игрока, а обеспечение соблюдения правил - это работа вышестоящего интеллекта. (возможно, какой-нибудь класс "ChessGameManager").

Простой подход, который кажется (!) Подходит для объектно-ориентированной шахматной реализации, состоит в том, чтобы иметь такие классы, как

abstract class Piece {
    Color getColor() { ... }
    Point getPosition() { ... }

    abstract void doMove(...) { ... }
}

class Bishop extends Piece {
    void doMove(....) { ... }   
}

// + other classes extending "Piece"

Но учтите, что это не всегда может быть лучшим подходом и, возможно, не всегда достаточным. В частности, вы должны иметь очень четкое представление о том, как взаимодействуют ваши Engine, Board, Piece и Player классы и каковы их обязанности. (Поразмыслив над этим некоторое время, вы, вероятно, придете к выводу, что вам также понадобится класс Move ...). В общем, проверка правильности хода намного сложнее, чем кажется на первый взгляд. Вы упомянули, что для хода слона вы проверяете, верна ли целевая позиция и нет ли других фигур между ними. Но этот ход по-прежнему недействителен, если этот ход заставляет собственного короля оставаться под шахом. Проверить это можно только «двигателем», а не по частям. Другие вещи, о которых люди склонны забывать (и которые связаны с информацией о всем состоянии игры и, таким образом, вряд ли могут быть обработаны с помощью одного элемента), - это Castling или На проходе движется.

Шахматный движок точки зрения

Для шахматного движка есть несколько требований, которые делают хороший объектно-ориентированный подход особенно сложным. Если вы намереваетесь написать эффективный шахматный движок, то ваша доска, скорее всего, будет массивом long значений, которые управляются поразрядными операциями ....

(Примечание: если вы разработали свой Board класс как интерфейс, как было предложено выше, то вы все равно могли бы сохранить красивое, высокоуровневое, объектно-ориентированное представление на это представление с высокой производительностью, которое используется для самого движка. Мое эмпирическое правило: всегда моделируйте все как интерфейс вначале. Легко сделать его более конкретным после этого)


Итак, в зависимости от того, хотите ли вы написать

  • хорошие, объектно-ориентированные шахматы для двух игроков-людей, с проверкой правил или
  • шахматный движок

Возможно, вы захотите по-другому подойти к некоторым частям игрового дизайна.


РЕДАКТИРОВАТЬ: Вы наверняка наткнетесь на это, когда будете искать информацию о программировании шахмат (движка), но я хотел бы отметить, что https://www.chessprogramming.org/Main_Page предлагает много справочной информации. Опять же: это не совсем объектно-ориентированный дизайн, а больше о мельчайших деталях шахматных движков.

person Marco13    schedule 18.04.2015
comment
Спасибо за столь подробный ответ. Я не упоминал, что у меня есть класс GameController, который проверяет наличие скрытых проверок, сопряжения и т. Д. Теперь я хочу переделать и сделать все части тупыми, чтобы они могли управлять только в том случае, если они могут туда перемещаться, не проверяя доску ( диагональ слона). Тогда GameController позаботится о свободном пути и безопасности. - person user1872329; 18.04.2015
comment
@ user1872329 Подождите: Вы сказали: Теперь я хочу переделать дизайн и сделать все части тупыми. Не следует необдуманно изменять свой дизайн, основываясь на таком единственном ответе. Подумайте, что вы хотите реализовать (игру для двух игроков или движок?), На чем вы хотите сосредоточиться (производительность, объектно-ориентированный дизайн?), И какое решение будет лучшим для вашей цели. В других ответах (включая ответ, предлагающий ...extends Piece подход) есть веские аргументы, и вам следует внимательно обдумать варианты. Не делать фигуры глупыми - тоже нормально, но подумайте о случаях, которые я упомянул (рокировка и т. д.). - person Marco13; 18.04.2015
comment
Я сейчас борюсь с En Passant, поэтому кажется лучшим вариантом передать управление состоянием игры другому классу. Еще раз большое спасибо. - person user1872329; 18.04.2015

С точки зрения ООП слон (ладья и т. Д.) Должен иметь возможность сказать, какой для него правильный ход - то есть, если данное поле находится на той же диагонали. Также он мог сказать доске, что он не может «пропускать» другие фигуры (IIRC только рыцарь может это делать, поэтому рыцарь может отменить это).

Опять же, никакая фигура не может перейти на поле с другой фигурой того же цвета, также ни один ход не должен подвергать опасности (шаху) короля. Эти ограничения должны проверяться вашим классом GameController (или каким-либо базовым классом, который инкапсулирует эту логику), потому что они действуют для всех частей.

Если GameController проверил, пусто ли целевое поле, а затем спросил фигуру, может ли она туда переместиться, сама фигура не должна была бы знать ваш массив доски, и общая логика была бы сосредоточена в контроллере.

Извините за мой плохой шахматный словарный запас :)

person Teyras    schedule 18.04.2015
comment
Спасибо. Я изменю свой дизайн - person user1872329; 18.04.2015
comment
слон (ладья и т. д.) должен иметь возможность сказать, какой для него законный ход - как упоминалось в моем ответе: это одна из версий чистой точки зрения ООП. Но для того, чтобы проверить правильность хода, ладья должна знать другие вещи, например оставляет ли этот ход под шахом своего короля или это ход рокировки (и, следовательно, ходил ли собственный король раньше). Красивое ООП-дизайн шахмат оказывается сложнее, чем кажется на первый взгляд. - person Marco13; 18.04.2015
comment
Да, я пытался сказать, что ладья / конь / кто угодно не должны беспокоиться о проверке, если они оставляют короля под шахом и т. Д. Вместо этого они должны знать только те правила, которые специфичны для них - другие легче проверено контроллером. Однако ваш ответ был гораздо более подробным :) - person Teyras; 18.04.2015

С точки зрения дизайна у вас есть два (а может и больше) варианта, которые следует рассмотреть:

  1. Доска - это своего рода менеджер правил, и она должна знать, как фигуры могут действовать - есть ограничение - доска должна знать каждого участника, поскольку в шахматах ограниченное количество типов, это не проблема.
  2. Доска - это только заполнитель / система координат для фигур. При таком подходе вы можете сэкономить много кода, имея абстрактный класс (или интерфейс, как вы написали, но между частями будет много общих атрибутов, поэтому абстрактный класс выглядит лучше для меня) для Piece, и каждый тип фрагмента будет расширяться / реализовать это. Пример:

    public abstract class Piece
    {
        private int row;
        private int column; // or other method to store position
        private boolean isBlack // or enum for type  
    
        // contructor, getters, setters etc...
    
        public abstract boolean canMove(int newX, int newY);
       /* some other abstract methods if you need */
    }
    

    И позже

    public class Bishop extends Piece
    {
          @Override
          public boolean canMove(int newX, int newY)
          {
                if( /*check if new points aare on diagonal */)
                    return true;
                else
                    return false;
          }
     }
     public class Knight extends Piece
     {
          @Override
          public boolean canMove(int newX, int newY)
          {
                if( /*check if L shaped with prev pos */)
                    return true;
                else
                    return false;
          }
     }
    

Я бы выбрал второй вариант. Это больше ООП +, это дает больше гибкости в хранении частей. В игровом классе у вас могут быть методы, которые принимают в качестве аргумента Piece и передают все, что расширяет класс Piece, Bishops, Knight и т. Д., И полиморфизм сделает эту работу за вас. В случае первого варианта вам, вероятно, потребуется использовать какой-нибудь переключатель / футляр.

Конечно, вам также понадобятся другие классы для игроков, состояния игры и т. Д.

Отвечая на ваш вопрос - можно переходить к доске. В моем предложении фигура знает, где она стоит, поэтому ей нужно только знать, какая новая предлагаемая позиция, чтобы проверить, не превышает ли она размер доски и является ли она допустимой для своего типа. Поскольку игровой контроллер проверяет наличие столкновений, вам на самом деле доска не нужна?

person Kuba    schedule 18.04.2015
comment
Спасибо. Я буду придерживаться интерфейса, потому что он у меня уже есть. Но, похоже, этот абстрактный класс был бы лучшим вариантом. - person user1872329; 18.04.2015
comment
Для меня немного странно, что у Piece будет поле Board. Потому что Доска содержит все фигуры, и каждая фигура будет знать обо всей доске. - person user1872329; 18.04.2015
comment
Я думаю, он должен знать, что я не мастер шахмат, но насколько я помню, вы не можете перемещать свой отряд на другой отряд, поэтому в canMove() вам также нужно проверить, нет ли другой фигуры того же игрока, так что вам понадобится доступ к другим частям. - person Kuba; 18.04.2015
comment
Я думал, что каждая фигура будет управлять только в том случае, если ей разрешено двигаться туда, например, если это один и тот же ряд для ладьи и т. Д. Тогда GameController спросит доску, есть ли какие-либо фигуры между квадратом и квадратом, если этот ход не откроет какой-то скрытый чек и т. д. - person user1872329; 18.04.2015
comment
Это тоже нормально, может быть, даже лучше, у вас не будет двунаправленного соединения между фигурой и доской. Я видел, как вы написали в вопросе, что доска содержит множество фигур, поэтому я убрал это из своего ответа. - person Kuba; 18.04.2015