C # — Стандартная колода из 52 карт

Я сделал 52 стандартных генератора колод для отработки моего C #. Просто чтобы вы знали, что я использую редактор единства.

Я хотел бы получить ваши отзывы о том, хороший / плохой мой код или что вы пожелаете.

using System;
using System.Collections.Generic;
using UnityEngine;

public class Standard52Deck {
    List<Card> deck = new List<Card>();

    enum Ranks {
        Ace,
        Two,
        Three,
        Four,
        Five,
        Six,
        Seven,
        Eight,
        Nine,
        Ten,
        Jack,
        Queen,
        King
    }

    enum Suits {
        Hearts,
        Spades,
        Clubs,
        Diamonds,
    }

    class Card {
        readonly Ranks rank;
        readonly Suits suit;

        public Card(Ranks rank, Suits suit) {
            this.rank = rank;
            this.suit = suit;
        }

        public override string ToString() {
            return $"{rank} of {suit}";
        }
    }

    IEnumerable<Card> GetSortedCardsBySuits() {
        var rankCount = Enum.GetValues(typeof(Ranks));
        var suitCount = Enum.GetValues(typeof(Suits));
        var deckCount = rankCount.Length * suitCount.Length;

        var index = 0;
        var rankIteration = 0;

        while (index < deckCount) {
            var rank = (Ranks)(index % 13);
            if (rank == 0 && index != 0) {
                rankIteration++;
            }
            var suit = (Suits)(rankIteration);

            index++;
            yield return new Card(rank, suit);
        }
    }

    void CreateSortedCards() {
        foreach (var card in GetSortedCardsBySuits()) {
            deck.Add(card);
        }
    }

    void Start() {
        CreateSortedCards();
    }
}

4 ответа
4

Вы хорошо прочитали, чтобы ранжировать подсчет в переменной rankCount, но позже вы пишете index % 13. Не сразу понятно, откуда взялось это число. Используйте и эту переменную.

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

const int RankCount = 13;
const int SuitCount = 4;
const int DeckCount = RankCount * SuitCount;  

Я обычно не использую var для встраиваемых типов. Вы не сэкономите много печатания, написав int или же string вместо var и читать легче.


Два целочисленных оператора % а также / дополняют друг друга и хорошо сочетаются друг с другом.

var rank = (Ranks)(index % RankCount);
var suit = (Suits)(index / RankCount);

Обратите внимание, что целочисленное деление обрезает результат. Это делает rankIteration переменная лишняя.

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

IEnumerable<Card> GetSortedCardsBySuits()
{
    foreach (Suits suit in Enum.GetValues(typeof(Suits))) {
        foreach (Ranks rank in Enum.GetValues(typeof(Ranks))) {
            yield return new Card(rank, suit);
        }
    }
}

С использованием List<T>.AddRange() делает явный цикл излишним

void CreateSortedCards()
{
    deck.AddRange(GetSortedCardsBySuits());
}

Здесь есть небольшая проблема с именами. Список колод создается в другом месте. Здесь он только инициализируется. Поскольку в списке есть перегрузка конструктора List<T>(IEnumerable<T>), мы могли бы вместо этого создать и инициализировать колоду в конструкторе класса (или, если мы сделаем CreateSortedCards static, также в инициализаторе)

private readonly List<Card> deck;

public Standard52Deck()
{
    deck = new List<Card>(GetSortedCardsBySuits());
}

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


Канонический способ зацикливания диапазона чисел (вместо цикла while)

for (int index = 0; index < DeckCount; index++)
{
    ...
}

Он унифицирует и стандартизирует объявление и инициализацию переменной цикла, условия цикла и увеличение переменной цикла. Кроме того, он охватывает переменную цикла локально в цикле. Однако я предпочитаю foreach подход, упомянутый ранее.


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

    class Card {
        readonly Ranks rank;
        readonly Suits suit;
        // ...
    }
    

    В этом определении типа есть грамматическое несоответствие: Ranks rank имеет имя множественного числа, но имя переменной в единственном числе.

    Типы Ranks а также Suits следует переименовать в Rank а также Suit, чтобы сделать код более последовательным.

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

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

      enum Ranks {
          Ace = 1,
          Two,
          Three,
          Four,
          Five,
          Six,
          Seven,
          Eight,
          Nine,
          Ten,
          Jack,
          Queen,
          King
      }
      

      Значит это 9 == (int) Nine правда, что менее странно, чем Nine восемь лет.

      Примечание. Традиционный порядок мастей (от высшей к низшей) — пики, червы, бубны, трефы.

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

      Поэтому я бы посоветовал изменить порядок в ваших костюмах. enum Suits подходить.

      • Точно так же я бы подумал о том, чтобы поставить туза после короля. Я имею в виду, это зависит от игры, но наиболее Я знаю, что в карточных играх есть старшие тузы. Возможно, придется создать разные версии класса для поддержки любого порядка. Тузы, вероятно, потребуют особого обращения во многих играх, несмотря ни на что.

        — Даррел Хоффман

      • @DarrelHoffman Ace представляет собой особую проблему, потому что во многих карточных играх его значение динамично: оно может меняться от высокого к низкому в зависимости от обстоятельств. Однако это деноминация 1 и традиционный заказ является числовым, что делает его первым (т. е. перед двумя). В большинстве программ карточных игр, которые я видел, сначала перечисляется туз со значением, равным единице. Иногда, если Туз всегда высокий, то сначала перечисляются два с явным значением 2, а Туз — последним (с неявным значением 14). Поскольку это, похоже, предназначено для использования в нескольких играх, Ace = 1 кажется правильным.

        — RBarryYoung

      Добавить комментарий

      Ваш адрес email не будет опубликован. Обязательные поля помечены *