Я сделал 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 ответа
Вы хорошо прочитали, чтобы ранжировать подсчет в переменной 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
