Дизайн иерархии шахматных фигур: наследование против полей типа - PullRequest
7 голосов
/ 31 декабря 2010

У меня есть базовый класс для штук

 class piece;

и массив, содержащий производные объекты

piece* board[8][8];

Преимущество, чистый дизайн с помощью виртуальных функций. Недостаток: если мне нужно найти фигуру на доске или сравнить фигуру, я должен вернуться к динамическому касту (или typeid). Это ужасно и может привести к снижению производительности при выполнении миллионов запросов.

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

Это для развлечения (так что без битборда).

РЕДАКТИРОВАТЬ 1

Читая некоторые ответы, я думаю, что использование полей типа только для перегрузки операторов (==,! = ...) может принести лучшее из обоих слов.

Вариант boost :: тоже выглядит очень интересно.

Ответы [ 6 ]

5 голосов
/ 31 декабря 2010

В качестве альтернативы, если ваш набор классов ограничен - т.е. вы знаете количество, используйте вариант и посетителей.Например, boost::variant<king, queen, bishop, knight ...> А плата составлена ​​из двумерного массива этого типа.Теперь для опроса вы можете использовать посетителей ...

3 голосов
/ 31 декабря 2010

Я бы пошел с иерархией классов.

Для поиска фигуры вы можете сохранить отдельный список для каждого типа фигуры. Таким образом, вы знаете, где искать каждый тип фигуры.

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

Еще один подход - использовать компонентную архитектуру (как описано здесь: http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/),, но я думаю, что это слишком много для шахматной игры, в которой вы четко знаете типы и знаете, что эти типы скоро не изменятся :) .

1 голос
/ 31 декабря 2010

Вы не можете одновременно беспокоиться о производительности и коде для удовольствия:)

Подумайте о том, чтобы вместо битборта было "nibbleboard" (или, по крайней мере, байтборд), где каждый клочок представляет один тип фигуры.Каждый кусочек также является индексом в таблице одноэлементных объектов, которые работают с этим типом фрагмента.

class Empty : public Piece {};
class Rook : public Piece {};
...

const int wrook = 1;
...
const int bpawn = 12;

Piece* Operator[13] = {new Empty(), new Rook(), ..., new Pawn()};

byte table[64] = {
    wrook, wbishop, wknight, wking, wqueen, wknight, wbishop, wrook,
    wpawn, wpawn, wpawn, wpawn, wpawn, wpawn, wpawn, wpawn, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    bpawn, bpawn, bpawn, bpawn, bpawn, bpawn, bpawn, bpawn, 
    brook, bbishop, bknight, bking, bqueen, bknight, bbishop, brook};

// Given some position and some operation DoSomething we would have this:
Operator[table[position]]->DoSomething(table, position, <other parameters>);

// Possible return value of DoSomething might be new table
1 голос
/ 31 декабря 2010

Я никогда не писал шахматную программу, но думаю, что наиболее распространенными операциями будут такие вещи, как:

  • отображение / печать доски
  • получение набора возможных ходовдля каждой фигуры
  • суммируйте значения всех фигур для доски, возможно, суммируйте какое-то "значение позиции", которое зависит от фигуры (ладья на открытой линии и тому подобное)

Кроме того, некоторые фигуры имеют «состояние» (король может замок, только если он не двигался раньше, пешка может ударить мимоходом, если другая пешка только сдвинула два квадрата), которые относятся только к одному виду.части.

Это все кричит иерархия классов для меня.(Предполагая, что вам не нужна производительность на битборде)

С другой стороны, маловероятно, что вам когда-нибудь придется добавлять новые типы фигур или что вы когда-либо сможете повторно использовать один из типов фигурв разлуке.т.е. расширяемость и модульность на самом деле не проблема.Так что, если вы обнаружите, что какая-то важная часть вашего алгоритма, которая действительно должна быть в одном месте, разбросана по нескольким классам элементов - используйте оператор switch.Просто добавьте абстрактный метод в класс Piece, который возвращает перечисление PieceType, и включите его.

1 голос
/ 31 декабря 2010

Я бы пошел с иерархией, и если я хочу знать тип (почему?), Есть виртуальный метод, который идентифицирует тип.

0 голосов
/ 31 декабря 2010

«Супер уродливый оператор переключения» - это правильный метод .Это не безобразно.Это называется функциональным программированием.

Наследование - совершенно неправильная техника.Каждая из фигур движется по-разному, имеет различную графику и другие свойства.Там нет ничего общего.Шахматные фигуры не являются абстрактными.Это конкретная коллекция дискретных объектов.

Вы должны сделать что-то общее с помощью объединения: создать так называемый тип суммы.В Ocaml:

type shape = Pawn | Rook | Knight | Bishop | Queen | King
type color = Black | White
type piece = shape * color
type pos = { row:int;  col:int }

let check_white_move piece board from to = match piece with
| Pawn -> on_board to && (from.row = 2 && to.row = 4 or to.row = from.row + 1)
| ....

В C ++ нет правильного типа суммы, который вы можете использовать вместо:

enum shape { pawn, rook, knight, bishop, queen, king};
..
bool check_white_move (..) { switch piece {
 case pawn: ...

Это более неуклюже.Пожаловаться на комитеты C и C ++.Но используйте правильную концепцию.Типы сумм (различимые объединения, варианты) являются способом объединения дискретного набора конкретных типов.Классы и наследование используются для представления абстракций и их реализации.

В шахматах нет ничего абстрактного.Это все о комбинациях.Это не вопрос преимуществ и недостатков различных техник: речь идет об использовании правильной техники.

[Кстати: да, вы можете попробовать вариант буста, хотя я не могу рекомендовать его для этого приложения, так какнет связанных данных перечисление идеально]

...