Страницы

Поиск по вопросам

понедельник, 8 октября 2018 г.

Как правильно в коде описать отношения между картами в колоде?

Предположим, что каждое достоинство игральной карты описывается отдельным классом:
class Jack { ... }
class Queen { ... }
Также есть отдельный класс колоды:
class Deck { ... }
Интересует, как правильно описать взаимоотношения старшинств карт. Например, что в любой карточной игре (практически, но это неважно) Дама старше Валета, а двойка младше Короля. Также не стоит забывать, что в некоторых играх есть козырь, соответственно любая козырная карта будет старше любой не козырной.
Насколько я понимаю, это должно описываться в каждом классе (для каждого вида карты). Но как? И придется ли перечислять все карты, старше (и младше) которой является данная? И как это сделать, если я рассуждаю правильно, в языке Ruby? Или все-таки взаимоотношения должны описываться на уровне всей колоды?


Ответ

Я бы предложил унаследовать все типы карт от базового класса Card, в котором бы при помощи внутреннего массива, например, TYPES задал бы порядок следования "типов" карт. Индекс такого массива будет определять старшинство среди типов. В конструктор можно было бы передавать масть suit и признак, является ли карта козырем trump
Чтобы объекты класса можно было сравнивать друг с другом мы при помощи include модуль Comparable, а потом переопределяем оператор <=>.
class Card include Comparable
# Массив-константа типов карт в порядке убывания значения TYPES = %w(Jocker Ace King Queen Jack Ten Nine Eight Seven Six).freeze
# Конструктор принимает масть suit и признак является ли карта козырем trump def initialize(suit, trump = false) @suit = suit @trump = trump end
# Вес мастей относительно друг друга def weight TYPES.find_index(self.class.name) end
# Козырь или нет? def trump @trump end
# Масть def suit @suit end
# Переопределение оператора сравнения <=> def <=>(other) if other.trump == @trump other.weight <=> weight else (other.trump && !@trump) ? -1 : 1 end end end
После этого можно либо явно унаследовать классы карты от базового класса Card
class Jocker < Card end
class Ace < Card end
class King < Card end
class Queen < Card end
class Jack < Card end
...
class Six < Card end
Либо создать их средствами метапрограммирования (все-равно они однотипные). В цикле обходим наш массив карт Card::TYPES и динамически создаем одноименные классы, унаследованные от класса Card
Card::TYPES.each do |klass| Kernel.const_set(klass, Class.new(Card)) end
В результате объекты эти классов можно сравнивать друг с другом. С учетом козырной масти, чтобы пометить карту козырной, передаем true в качестве второго аргумента конструктора.
jack = Jack.new(:hearts) ace = Ace.new(:clubs)
p ace < jack # false p ace > jack # true p ace == ace # true
jack = Jack.new(:hearts, true) ace = Ace.new(:clubs) trump = Ace.new(:clubs, true)
p ace < jack # true p ace > jack # false p ace == trump # false
В результате, какую бы колоду вы не сформировали затем в Deck ее всегда можно будет корректно отсортировать и сравнить каждую карту с другой, учитывая текущую козырную масть.

Комментариев нет:

Отправить комментарий