Игральные карты: они должны быть enum, struct или class? - PullRequest
11 голосов
/ 30 марта 2012

Я разрабатываю игровой сайт, где многие (надеюсь, тысячи) игроков будут одновременно играть в определенные карточные игры друг с другом.Колода - стандартная колода из 52 карт.У каждой карты есть масть и ранг.Карты будут перетасовываться, разыгрываться, собираться, заказываться, разыгрываться все время.У меня вопрос: должно ли Card быть перечислением, структурой или классом?

  • Для перечисления: Пусть каждая карта будет байтом 0..51.Таким образом, карта будет занимать очень мало места.Вы можете представить руку как битовый набор из 8 байтов.Вы можете вычислить масть и ранг данной карты очень быстро, когда возникнет такая необходимость: т.е. масть (n) = n / 13.Это будет очень эффективно.Если вам нужно написать методы для карт, запишите их с помощью методов расширения.

  • Для структуры: Нет, это похоже на написание машинного кода.Карта представляет собой простую структуру, содержит очень мало данных, неизменна, мала.Он не имеет большого поведения, поэтому сделайте его структурой и рассматривайте его как пассивную структуру данных.Вы можете вычислить индекс в 0..51 по данной карте очень быстро, когда возникнет такая необходимость.

  • Для класса: Нет, это не объектно-ориентированное мышление.Сделай карточку классом.Сделайте это неизменным.Создайте ровно 52 экземпляра.Пусть пул карт содержит эти экземпляры.Поэтому, когда кому-то нужна Пиковая дама, она попросит об этом карточный пул.Будет одна и только одна Пиковая дама, даже если будут тысячи игр.Сохраните индексное поле 0..51 в Карте, если хотите.

Я склоняюсь к последнему варианту (классу), но я не уверен.Я не очень беспокоюсь о производительности;Возможно, я буду делать более серьезные ошибки по пути.Меня беспокоит то, что вся моя точка зрения может быть неправильной;может быть, это очень простое решение, и я сомневаюсь, потому что мне не хватает некоторых знаний, которыми обладают все остальные.

Что вы думаете?

Редактировать: О поведении карт.Я думаю, что карта будет знать только о других картах.Например, он может определить частичный порядок «кто бьет, кто в бридж».Не нужно ничего знать о колоде.И это будет код на стороне сервера;конечно, не нужно знать, чтобы рисовать себя на экране и т. д.

Ответы [ 7 ]

13 голосов
/ 30 марта 2012

Фундаментальный вопрос, который вам следует задать себе при выборе между ссылочным типом или типом значения, это, конечно, - это то, что я логически моделирую значение или указанная ? Вот почему типы значений и ссылочные типы называются "типами значений" и "ссылочными типами".

Планируете ли вы рассматривать «карточку» как эталон так же, как вы бы относились к физическому объекту, который имеет идентичность? Предположим, например, что вы моделируете Canasta, в которую одновременно играют две стандартные колоды карт. Вы когда-нибудь захотите отслеживать две разные пиковые дамы и рассматривать их как относительно разные ?

Или вы собираетесь относиться к ним как к значениям , как к числам ? Вы никогда не говорите «это число шесть здесь отличается от того числа шесть там», потому что числа различаются только по их значениям.

5 голосов
/ 30 марта 2012

Сделайте struct или class для карты, значение карты должно быть enum, а масть карты также - enum.

Чтобы сделать колоду, вы можете использовать class, она содержит список card, имеет такие операции, как Shuffle и т. Д.

2 голосов
/ 30 марта 2012

Я не верю, что вы действительно задаете правильный вопрос здесь. Как представлять вашу карточку, должно зависеть от того, что вы хотите делать со своими карточками; Отвечая на вопрос о том, как представить свою карту сейчас, вы автоматически ограничиваете себя в способах решения других более интересных / сложных проблем, с которыми вы столкнетесь позже.

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

  • Что произойдет, когда будет сдана "карта"? Вы хотите представить карту, которая физически перемещается между объектами, которые держат эти карты? (например, колода, рука игрока, колода сброса, стол и т. д.), или вы хотите, чтобы эти сущности просто ссылались на центральное хранилище карт? или, возможно, использовать совершенно отдельный механизм подсчета?

  • Как вы собираетесь "заказывать" карты? Игры, которые вы выбираете для реализации, могут иметь разные правила (например, Ace Low или High - все ли королевские масти имеют одинаковое значение? Будет ли козырь?) Это могут быть решения, которые определяются в каждой игре отдельно. и даже изменился во время самой игры, поэтому простое определение карты на основе ее порядка может оказаться плохой абстракцией.

    • Какие типы поведения и правила игры вы пытаетесь представить, которые будут зависеть от карты? Будут ли у самой карты какие-либо зависимости?

    • Будет ли карта участвовать во взаимодействии с пользователем, или это просто логический объект, используемый частями вашей программы, который определяет карточную игру (игры)?

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

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

2 голосов
/ 30 марта 2012

Вы можете представить каждую карточку в виде числа, а затем обернуть их как в перечисление, так и в класс. Если каждое число от 0 до 51 представляет карточку, вы можете получить перечисление:

public enum Card
{
   HeartsAce = 0,
   Hearts2 = 1,
   // ... and so on
}

int nCard = 16;
Card myCard = (Card)nCard;

Вы также можете иметь класс:

public class Card
{
    private readonly int _nSuitNumber = 0;
    private readonly int _nCardNumber = 0;

    public Card(int a_nNumber)
    {
        _nSuitNumber = a_nNumber / 13;  // 1 = Hearts, 2 = Diamonds ...
        _nCardNumber = a_nNumber % 13;  // 1 = ace, 2 = two
    }
}

Или еще лучше объединить их.

public class Card
{
    private readonly Suit _suit;
    private readonly Value _value;

    public Card(int a_nNumber)
    {
        _suit = (Suit)(a_nNumber / 13);  // 0 = Hearts, 1 = Diamonds ...
        _value = (Value)(a_nNumber % 13 + 1);  // 1 = ace, 2 = two
    }

    public Suit Suit
    {
        get { return _suit; }
    }

    public Value Value
    {
        get { return _value; }
    }

    public int ToNumber()
    {
        return (int)_suit * 13 + ((int)_value - 1);
    }
}

public enum Suit
{
    Hearts = 0,
    Diamonds = 1,
    Clubs = 2,
    Spades = 3
}

public enum Value
{
    Ace = 1,
    Two = 2,
    Three = 3,
    Four = 4,
    Five = 5,
    Six = 6,
    Seven = 7,
    Eight = 8,
    Nine = 9,
    Ten = 10,
    Jack = 11,
    Queen = 12,
    King = 13,
}

С этим классом у вас есть преимущество как от числа (0-52), так и от класса. Класс - не более чем обертка вокруг числа. Вы можете добавлять любые операции, методы, свойства в класс по мере необходимости. А когда вы сохраняете или передаете данные, вам нужно использовать только номер.

0 голосов
/ 30 марта 2012

Я бы пошел с классом или структурой, используя перечисления, и поместил бы внутренний конструктор:

publlic class PlayingCard
{
    internal PlayingdCard(CardSuit suit, CardFace face)
    {
        this.Suit = suit;
        this.Face = face;
    } 

    public CardSuit Suit { get; private set; }

    public CardFace Face { get; private set; }
}

Определите свой класс колоды для управления картами, но (что наиболее важно), чтобы он распределял карты во время своего построения:

public class Deck
{
    private List<PlayingCard> cards = new List<PlayingCard>();
    public Deck()
    {
        for(var i = 0; i < 4; i++)
        {
            for (var j = 1 to 13)
            {
                this.cards.Add(new PlayingCard((CardSuit)i, (CardFace)j));
            }
        }
    }

    public void Shuffle() { /* implement me */ }
    public PlayingCard GetNextCard() { /* implement me */ }

}

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

0 голосов
/ 30 марта 2012

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

Поскольку несколько человек играют в игры с разными типами игр, каждая колода должна знать, какие у нее картыи сколько.Но сама карта была бы неизменной даже в этом случае.

0 голосов
/ 30 марта 2012

Вы можете подумать, какую абстракцию вы пытаетесь построить. Если карта является объектом низкого уровня, передаваемым другими функциями, то более простой enum может быть более подходящим, но если вы хотите, чтобы карта могла draw() сама или что-то еще, вам понадобится что-то более сложное. Как карта будет вписываться в ваше приложение?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...