Игральные карты: должны ли они перечисляться или структурироваться или класса?

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

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

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

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

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

Как вы думаете?

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

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

Вы можете представить каждую карту в виде числа и обернуть их как в перечисление, так и в класс. Если каждое число от 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), так и класс. Класс - это не что иное, как обертка вокруг числа. Вы можете добавлять любые операции, методы и свойства в класс по мере необходимости. И когда вы храните или передаете данные, вам нужно использовать только номер.

Ответ 4

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

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

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

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

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

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

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

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

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; }
}

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

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).