在 C# 中表示游戏卡类的最佳方式

发布于 2024-10-25 01:35:27 字数 200 浏览 5 评论 0原文

我使用 Card 类,其中包含 2 个枚举属性(套件 - 红心、钻石、黑桃和梅花)和从 2 到 A 的卡值。并覆盖 ToString() 方法以返回类似 Ah Ad 等的内容。一切正常,但枚举值无法启动带有数字,因此我列举的卡值看起来像x2,x3,x4......这并不美观。

还需要简单的方法来从单个字符串中解析几张卡片。

谁知道设计这个类的最佳方法?

I use class Card which contains 2 enumerated properties (suite - hearts diamonds spades and clubs) and card value from 2 to A. And overrides ToString() method to returns something like Ah Ad etc. All ok, but enum value can't starts with number, therefore my card value enumerated looks like x2, x3, x4 ... it is not beautiful.

Also need simple approach to parse few cards from single string.

Who know the best approach to design this class?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

青瓷清茶倾城歌 2024-11-01 01:35:27

你不能将 Jack、Queen、King 和 Ace 分别指定为 11、12、13 和 14 吗?它最终看起来像这样:

public class Card
{
    public int Value { get; private set; }
    public enum SuitType
    {
        Clubs, Spades, Hearts, Diamonds
    }
    public SuitType Suit { get; private set; }
    public Card(int value, SuitType suit)
    {
        Suit = suit;
        Value = value;
    }
    public Card(string input)
    {
        if (input == null || input.Length < 2 || input.Length > 2)
            throw new ArgumentException();
        switch (input[0])
        {
            case 'C': case 'c':
                Suit = SuitType.Clubs;
                break;
            case 'S': case 's':
                Suit = SuitType.Spades;
                break;
            case 'H': case 'h':
                Suit = SuitType.Hearts;
                break;
            case 'D': case 'd':
                Suit = SuitType.Diamonds;
                break;
            default:
                throw new ArgumentException();
        }
        int uncheckedValue = (int)input[1];
        if (uncheckedValue > 14 || uncheckedValue < 1)
            throw new ArgumentException();
        Value = uncheckedValue;
    }
    public string encode()
    {
        string encodedCard = "";
        switch (Suit)
        {
            case SuitType.Clubs:
                encodedCard += 'c';
                break;
            case SuitType.Spades:
                encodedCard += 's';
                break;
            case SuitType.Hearts:
                encodedCard += 'h';
                break;
            case SuitType.Diamonds:
                encodedCard += 'd';
                break;
        }
        encodedCard += (char) Value;
        return encodedCard;
    }
    public override string ToString()
    {
        string output = "";
        if (Value > 10)
        {
            switch (Value)
            {
                case 11:
                    output += "Jack";
                    break;
                case 12:
                    output += "Queen";
                    break;
                case 13:
                    output += "King";
                    break;
                case 14:
                    output += "Ace";
                    break;
            }
        }
        else
        {
            output += Value;
        }
        output += " of " + System.Enum.GetName(typeof(SuitType), Suit);
        return output;
    }
}

编辑:
我添加了一些字符串功能。
我从 乔恩·汉纳的回答

Couldn't you assign Jack, Queen, King, and Ace to be 11, 12, 13, and 14, respectively? It'd end up looking something like:

public class Card
{
    public int Value { get; private set; }
    public enum SuitType
    {
        Clubs, Spades, Hearts, Diamonds
    }
    public SuitType Suit { get; private set; }
    public Card(int value, SuitType suit)
    {
        Suit = suit;
        Value = value;
    }
    public Card(string input)
    {
        if (input == null || input.Length < 2 || input.Length > 2)
            throw new ArgumentException();
        switch (input[0])
        {
            case 'C': case 'c':
                Suit = SuitType.Clubs;
                break;
            case 'S': case 's':
                Suit = SuitType.Spades;
                break;
            case 'H': case 'h':
                Suit = SuitType.Hearts;
                break;
            case 'D': case 'd':
                Suit = SuitType.Diamonds;
                break;
            default:
                throw new ArgumentException();
        }
        int uncheckedValue = (int)input[1];
        if (uncheckedValue > 14 || uncheckedValue < 1)
            throw new ArgumentException();
        Value = uncheckedValue;
    }
    public string encode()
    {
        string encodedCard = "";
        switch (Suit)
        {
            case SuitType.Clubs:
                encodedCard += 'c';
                break;
            case SuitType.Spades:
                encodedCard += 's';
                break;
            case SuitType.Hearts:
                encodedCard += 'h';
                break;
            case SuitType.Diamonds:
                encodedCard += 'd';
                break;
        }
        encodedCard += (char) Value;
        return encodedCard;
    }
    public override string ToString()
    {
        string output = "";
        if (Value > 10)
        {
            switch (Value)
            {
                case 11:
                    output += "Jack";
                    break;
                case 12:
                    output += "Queen";
                    break;
                case 13:
                    output += "King";
                    break;
                case 14:
                    output += "Ace";
                    break;
            }
        }
        else
        {
            output += Value;
        }
        output += " of " + System.Enum.GetName(typeof(SuitType), Suit);
        return output;
    }
}

Edit:
I added some string functionality.
I took structure of Card(string input) from Jon Hanna's answer.

请恋爱 2024-11-01 01:35:27

点卡有一个明显的数值,我们可以添加 J=11、Q=12、K=13。

根据所建模的游戏,A=14 可能比 A=1 更方便(因此可以更简单地计算手牌的不同相对值)。

枚举没有提供真正的优势,特别是因为枚举允许超出范围的值,除非您明确检查它们(例如,没有什么可以阻止某人将 (CardValue)54 分配给卡值枚举值) 。

ToString 可以使用值数组 {null,"1","2","3","4","5","6","7","8","9 ","10","J","Q","K"}。同样,{'♥','♦','♠','♣'} 可以提供更好的输出。

即使您对接受的内容非常严格,解析总是比输出字符串更棘手,因为您必须处理无效输入的可能性。一个简单的方法是:

private Card(string input)
{
  if(input == null)
    throw new ArgumentNullException();
  if(input.length < 2 || input.length > 3)
    throw new ArgumentException();
  switch(input[input.Length - 1])
  {
    case 'H': case 'h': case '♥':
      _suit = Suit.Hearts;
      break;
    case 'D': case 'd': case '♦':
      _suit = Suit.Diamonds;
      break;
    case 'S': case 's': case '♠':
      _suit = Suit.Spades;
      break;
    case 'C': case 'c': case '♣':
      _suit = Suit.Clubs;
      break;
    default:
      throw new ArgumentException();
  }
  switch(input[0])
  {
    case "J": case "j":
      _cardValue = 11;
      break;
    case "Q": case "q":
      _cardValue = 12;
      break;
    case "K": case "k":
      _cardValue = 13;
      break;
    case "A": case "a":
      _cardValue = 1;
      break;
    default:
      if(!int.TryParse(input.substring(0, input.Length - 1), out _cardValue) || _cardValue < 2 || _cardVaue > 10)
        throw new ArgumentException;
      break;
  }
}
public static Card Parse(string cardString)
{
  return new Card(cardString);
}

您可能需要添加一个读取较大字符串的静态方法,在解析时生成返回卡片,以便更轻松地对多张卡片进行编码。

There's an obvious numeric value for the pip-cards, and we can add J=11, Q=12, K=13.

It may be more convenient to have A=14 than A=1 depending on the game being modelled (so one can more simply compute different relative values of hands).

Enums gives no real advantage, especially since enums allow out-of-range values unless you explicitly check for them (e.g. there is nothing to stop someone assigning (CardValue)54 to the card-value enumeration value).

ToString can be aided with an array of the values {null,"1","2","3","4","5","6","7","8","9","10","J","Q","K"}. Likewise {'♥','♦','♠','♣'} could give a nicer output.

Parsing always trickier than outputting a string, even if you are very strict in what you accept, as you have to deal with the potential for invalid input. A simple approach would be:

private Card(string input)
{
  if(input == null)
    throw new ArgumentNullException();
  if(input.length < 2 || input.length > 3)
    throw new ArgumentException();
  switch(input[input.Length - 1])
  {
    case 'H': case 'h': case '♥':
      _suit = Suit.Hearts;
      break;
    case 'D': case 'd': case '♦':
      _suit = Suit.Diamonds;
      break;
    case 'S': case 's': case '♠':
      _suit = Suit.Spades;
      break;
    case 'C': case 'c': case '♣':
      _suit = Suit.Clubs;
      break;
    default:
      throw new ArgumentException();
  }
  switch(input[0])
  {
    case "J": case "j":
      _cardValue = 11;
      break;
    case "Q": case "q":
      _cardValue = 12;
      break;
    case "K": case "k":
      _cardValue = 13;
      break;
    case "A": case "a":
      _cardValue = 1;
      break;
    default:
      if(!int.TryParse(input.substring(0, input.Length - 1), out _cardValue) || _cardValue < 2 || _cardVaue > 10)
        throw new ArgumentException;
      break;
  }
}
public static Card Parse(string cardString)
{
  return new Card(cardString);
}

You might want to add a static method that read a larger string, yield returning cards as it parsed, to allow for easier encoding of several cards.

灯角 2024-11-01 01:35:27

当我第一次开始使用 card.dll 时,我使用枚举来表示花色和牌排名,但后来我不想处理同样的问题并编写额外的代码来补偿字符串,因此我写了一个摘要只有两个变量的 Info 类
(Flag (byte)) 和 (Name(string)) 由 Rank 类和 Suit 类实现,它们将是 Card 类的成员。我发现这对于命名约定和过滤目的来说效果更好。我喜欢使用枚举,但必须解决变量命名可能会很麻烦,所以有时如果您必须将变量名称作为字符串获取,最好不要这样做。

因此,当调用 Card 构造函数时,输入卡 ID,然后将其传递到 Rank 和 Suit,然后将代码中的 ID 含义分开 (101 = 100 (suit flag) +
1(排名标志))。受保护的抽象 SetName(int cardID) 和 SetFlag(int cardID) 同时通过 Rank 和 Suit 在信息的构造函数中处理其余部分。枚举不再有问题,仍然可以通过标志按数字进行过滤。

When I first started on the card.dll, I was using enumerations for suits and card rankings but then I didn't want to have to deal with that same issue and writing extra code to compensate for the strings, there for I wrote a abstract class Info with only two variables
(Flag (byte)) and (Name(string)) to be implemented by the Rank class and Suit class which would be members of the Card class. I have found this to work a lot better for naming conventions and filtering purposes. I love using enums but having to work around variable naming can be a hassle so sometimes it is best not to if you have to get the variable name as string.

So when the Card constructor get called the card ID is entered and then it passes into the Rank and Suit which will then separate what the ID means in code (101 = 100 (suit flag) +
1 (rank flag)). The protected abstract SetName(int cardID) and SetFlag(int cardID) while handle the rest from there in the info's constructor via Rank and Suit. No more issues with the enumeration and it can still be filtered by number via the Flag.

猫瑾少女 2024-11-01 01:35:27

该卡牌命名系统使用 1 到 4 * 100(表示花色标记)+ 1 到 13(表示卡牌等级)。 500 + 14 到 16 分别是小小丑、大小丑和百搭。

public class Card
{
    short id;

    public Card(string zFile)
    {
        this.id = Convert.ToInt16(zFile.Split('.')[0].Trim());
        this.Rank = new Rank(id);
        this.Suit = new Suit(id);
    }

    public override string ToString()
    {
        if (Suit.Flag == 5)
            return Suit.Name;
        return string.Concat(Rank.Name, " of ", Suit.Name);
    }
    public override int GetHashCode()
    {
        return id;
    }

    public Rank Rank { get; private set; }
    public Suit Suit { get; private set; }

    public static Card GetGreaterRank(Card value1, Card value2)
    {               
        return  (value1.Rank >= value2.Rank) ? value1 : value2;                        
    }

    public static bool CompareRank(Card value1, Card value2)
    {
        return (value1.Rank.Flag == value2.Rank.Flag);
    }
    public static bool CompareSuit(Card value1, Card value2)
    {
        return (value1.Suit.Flag == value2.Suit.Flag);
    }
};    
public abstract class Info
{
    protected Info(short cardID)
    {
        Flag = SetFlag(cardID);            
    }

    protected string SetName(short cardID, params string[] names)
    {
        for (int i = 0; i < names.Length; i++)
        {
           if (Flag == (i + 1))
              return names[i];
        }
        return "Unknown";
    }

    protected abstract byte SetFlag(short cardID);

    public static implicit operator byte(Info info)
    {
        return info.Flag;
    }

    public byte Flag { get; protected set; }
    public string Name { get; protected set; }
};

public class Rank : Info
{
    internal Rank(short cardID) : base(cardID) 
    { 
        string name = SetName(cardID, "A","2","3","4","5","6","7",
               "8","9","10","J","Q","K","Little Joker","Big Joker","Wild");
        Name = (name == "Unknown") ? string.Concat(name, " Rank") : name;
    }

    protected override byte SetFlag(short cardID)
    {
        return Convert.ToByte(cardID.ToString().Remove(0, 1));
    }        
};

public class Suit : Info
{
    internal Suit(short cardID) : base(cardID) 
    { 
        string name = SetName(cardID,"Clubs","Diamonds","Hearts","Spades");
        Name = (name == "Unknown") ? string.Concat(name, " Suit") ? name;
    }

    protected override byte SetFlag(short cardID)
    {
        return Convert.ToByte(cardID.ToString().Remove(1));
    }
};

因此,现在如果您有名为 101.png 的卡牌图像文件并将其传递到 Card ctor 中,它将传递到 Rank and Suit 为您获取信息。实际上,您所做的就是为图像文件提供名称代码(数字)。

This card naming system uses a 1 through 4 * 100 (telling the suit flag) + 1 through 13 (for card rank). 500 + 14 through 16 are Little Joker, Big Joker, and Wild.

public class Card
{
    short id;

    public Card(string zFile)
    {
        this.id = Convert.ToInt16(zFile.Split('.')[0].Trim());
        this.Rank = new Rank(id);
        this.Suit = new Suit(id);
    }

    public override string ToString()
    {
        if (Suit.Flag == 5)
            return Suit.Name;
        return string.Concat(Rank.Name, " of ", Suit.Name);
    }
    public override int GetHashCode()
    {
        return id;
    }

    public Rank Rank { get; private set; }
    public Suit Suit { get; private set; }

    public static Card GetGreaterRank(Card value1, Card value2)
    {               
        return  (value1.Rank >= value2.Rank) ? value1 : value2;                        
    }

    public static bool CompareRank(Card value1, Card value2)
    {
        return (value1.Rank.Flag == value2.Rank.Flag);
    }
    public static bool CompareSuit(Card value1, Card value2)
    {
        return (value1.Suit.Flag == value2.Suit.Flag);
    }
};    
public abstract class Info
{
    protected Info(short cardID)
    {
        Flag = SetFlag(cardID);            
    }

    protected string SetName(short cardID, params string[] names)
    {
        for (int i = 0; i < names.Length; i++)
        {
           if (Flag == (i + 1))
              return names[i];
        }
        return "Unknown";
    }

    protected abstract byte SetFlag(short cardID);

    public static implicit operator byte(Info info)
    {
        return info.Flag;
    }

    public byte Flag { get; protected set; }
    public string Name { get; protected set; }
};

public class Rank : Info
{
    internal Rank(short cardID) : base(cardID) 
    { 
        string name = SetName(cardID, "A","2","3","4","5","6","7",
               "8","9","10","J","Q","K","Little Joker","Big Joker","Wild");
        Name = (name == "Unknown") ? string.Concat(name, " Rank") : name;
    }

    protected override byte SetFlag(short cardID)
    {
        return Convert.ToByte(cardID.ToString().Remove(0, 1));
    }        
};

public class Suit : Info
{
    internal Suit(short cardID) : base(cardID) 
    { 
        string name = SetName(cardID,"Clubs","Diamonds","Hearts","Spades");
        Name = (name == "Unknown") ? string.Concat(name, " Suit") ? name;
    }

    protected override byte SetFlag(short cardID)
    {
        return Convert.ToByte(cardID.ToString().Remove(1));
    }
};

So now if you have your card image file named 101.png and pass it into the Card ctor it will pass to the Rank and Suit getting the info for you. Really all you are doing in giving the image file a code(numeric) for a name.

野鹿林 2024-11-01 01:35:27

我可能会从 2 个枚举开始,1 个代表西装,1 个代表面孔。然后根据这些枚举声明一个公共属性“Suit”和一个公共属性“Face”。您可能还需要一个数组,其中包含卡片可以具有的不同唯一值(即 1 到 13)。

I would probably start out with 2 enums, 1 representing the Suits and 1 representing the Faces. Then declare a public property "Suit" and a public property "Face" based off of these enums. You will also probably need an array with the different unique values that a card can have (i.e. 1 throught 13).

三生路 2024-11-01 01:35:27

您可以以数字开始枚举(尽管最好从零开始)

public enum Card
{
   Two = 2,
   Three,
   Four,
   ...
}

You can start enums with number (although it is preferred to start at zero)

public enum Card
{
   Two = 2,
   Three,
   Four,
   ...
}
傲性难收 2024-11-01 01:35:27

抄一下我之前写的,这个比较好。

using System;

enum Suit
{
    Clubs,
    Hearts,
    Diamonds,
    Spades
}

class Card
{
    Suit Suit
    {
        get;
        private set;
    }

    int Value
    {
        get;
        private set;
    }

    Card(Suit suit, int value)
    {
        Suit = suit;
        Value = value;
    }

    private const string[] valsToString = new string[] { "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A" };

    bool IsValid()
    {
        return Value >= 2 && Value <= 14;
    }

    override string ToString()
    {
        return string.Format("{0} of {1}", valsToString[Value - 2], Suit);
    }
}

Scratch what I wrote before, this is better.

using System;

enum Suit
{
    Clubs,
    Hearts,
    Diamonds,
    Spades
}

class Card
{
    Suit Suit
    {
        get;
        private set;
    }

    int Value
    {
        get;
        private set;
    }

    Card(Suit suit, int value)
    {
        Suit = suit;
        Value = value;
    }

    private const string[] valsToString = new string[] { "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A" };

    bool IsValid()
    {
        return Value >= 2 && Value <= 14;
    }

    override string ToString()
    {
        return string.Format("{0} of {1}", valsToString[Value - 2], Suit);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文