Предположим, что каждое достоинство игральной карты описывается отдельным классом:
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 ее всегда можно будет корректно отсортировать и сравнить каждую карту с другой, учитывая текущую козырную масть.
Комментариев нет:
Отправить комментарий