扑克牌:应该是枚举、结构还是类

本文关键字:结构 枚举 扑克牌 | 更新日期: 2023-09-27 18:29:22

我正在设计游戏网站,许多(希望是数千)玩家将在这里同时玩某些纸牌游戏。牌组是标准52牌组。每张牌都有一套和一个等级。这些牌将一直被洗牌、发牌、挑选、订购、玩。我的问题是,Card应该是枚举、结构还是类?

  • 对于枚举:让每个卡都是一个字节0..51。所以一张牌只会占用很少的空间。您可以将一只手表示为一个8字节的位集。当需要时,你可以很快计算出一张牌的花色和等级:即花色(n)=n/13。这将非常有效。如果您需要为Cards编写方法,请通过扩展方法编写。

  • 对于结构:不,这就像写机器代码。卡片是一种简单的结构,可以容纳很少的数据,是不可变的,很小。它没有太多的行为,所以将其作为一个结构,并将其视为一个被动数据结构。当需要时,你可以很快从一张给定的卡片中计算出0..51中的索引。

  • 对于类:不,这不是一种面向对象的思维方式。制作一个Card类。使其不可变。正好创建52个实例。让卡池保存这些实例。因此,当一个人需要黑桃皇后时,他会向卡池索要。即使有成千上万的游戏在进行,也只有一个黑桃皇后。如果你愿意,在Card中存储一个索引字段0..51。

我倾向于最后一种选择(上课),但我不确定。我不太担心表现;一路上我可能会犯更严重的错误。我担心的是我的整个观点可能是错误的;也许这是一个非常容易的决定,我犹豫了,因为我缺乏其他人所拥有的一些知识。

你觉得怎么样?

编辑:关于纸牌的行为。我认为一张牌只会知道其他牌。例如,它可以定义"谁在桥上打败谁"的偏序。它不需要知道任何关于甲板的信息。这将是服务器端代码;当然,它不需要知道在屏幕上画自己。

扑克牌:应该是枚举、结构还是类

在决定引用类型还是值类型时,你应该问自己的基本问题当然是我在逻辑上建模的是,还是引用的东西这就是为什么值类型和引用类型首先被称为"值类型"answers"引用类型"。

你是否打算将"卡片"作为参考,就像对待有身份的实物一样?例如,假设您正在为Canasta建模,它同时使用两个标准牌组进行游戏。你会想跟踪两个不同的黑桃皇后,并将它们视为参考的不同吗?

还是将它们视为,就像对待数字一样?你永远不会说"这里的数字6和那里的数字6不同",因为数字只是根据它们的值来区分的。

为卡制作structclass,卡的值应该是enum,卡的套装也是enum

要制作一副牌,可以使用class,它包含card的列表,具有Shuffle等操作。

您可以将每张牌表示为一个数字,然后将它们封装在枚举和类中。如果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)和类的好处。这个类不过是一个数字的包装器。您可以根据需要向类中添加任何操作、方法和属性。当你存储或传输数据时,你只需要使用数字。

我不相信你在这里问的问题是对的。如何代表你的卡片应该由你想用你的卡片做什么来决定;通过回答现在如何表示你的卡片的问题,你会自动约束自己,以解决以后遇到的其他更有趣/更具挑战性的问题。

你真正应该考虑的事情是如何实现你的程序行为。例如

  • 当发"牌"时会发生什么?你想表示一张卡在持有这些卡的实体之间物理移动吗?(例如,牌组、玩家的手牌、弃牌堆、桌子等),还是你希望这些实体只引用一个中央卡片库?或者使用完全独立的计数机制?

  • 你打算如何"订购"卡片?你选择实施的游戏可能有不同的规则(例如,王牌低还是高?皇家套装都有相同的价值吗?会有王牌吗?)这些决定可能是在逐个游戏的基础上决定的,甚至在游戏本身发生了变化,因此,简单地根据牌的顺序对牌进行单一定义可能不是一个好的抽象概念。

    • 你试图代表什么样的行为和游戏规则,这将取决于牌?卡片本身会有依赖关系吗?

    • 卡片是要参与用户交互,还是纯粹是程序中定义卡片游戏的部分使用的逻辑对象?

    • 你有没有想过使用不同种类卡片的可能性?你在最初的问题中提到了一个类,但如果一张卡要定义任何类型的状态、行为或与其他类交互,那么在担心细节之前,你可能首先想定义一个卡接口。

作为对你问题的直接回答——我个人认为,除了基于猜测和个人偏好的观点之外,你还没有提供足够的信息来回答问题;这没关系,因为在开始实现之前,通常很难找到所有的答案。你可能想试试这三种,看看哪一种最适合你的需求。不要通过提前做出这些决定来限制你的设计。

我认为这个问题可以从逻辑上得到回答。我将从面向对象的角度来看待它。我认为一张牌有两个子元素,套装和价值。这些可能是enum。手(类)可能应该包含一个卡片列表,这让我相信最好将卡片作为结构或类。为了确定一只手是否有一对,这将是手类的一个函数。

由于多人玩不同类型的游戏,每副牌都必须知道它有什么类型的牌以及有多少张。但即使在这种情况下,卡片本身也是不可变的。

我会使用枚举来处理一个类或结构,并在它上面放置一个内部构造函数:

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

您可能会考虑要构建什么抽象。如果一张卡是由其他功能传递的低级对象,那么更简单的enum可能更合适,但如果你想让一张卡本身能够draw()或其他什么,那么你需要更复杂的东西。这张卡如何适合你的申请?