【问题标题】:Implementing a Deck of Cards in C++用 C++ 实现一副纸牌
【发布时间】:2011-07-12 13:59:53
【问题描述】:

所以我正在尝试创建一副纸牌,但我对 C++ 和指针还是有点陌生​​,所以我有几个问题

所以我有Card 类与getRankgetSuit,以及两个Card 对象的比较。 我设置套牌的方式是等级为 1-13,套装为 1-4。所以这是一个简单的比较,将一张卡的等级+花色与另一张卡的等级+花色进行比较。

所以我像这样初始化并声明了两个数组:

char Rank[13] = {1,...13};
char Suit[4] = ...

而我的方法是这样的:

char* getSuit(){ return Suit; }
char* getRank(){ return Rank; }
int Comp(Card *card) {
    if (rank+suit < card->rank+card->suit)
        return 1;
    else if...
    else ...
}

我的问题是,拥有一个 char 数组是不是一个坏主意,因为我只存储整数? 稍后我计划将这些数字转换为输出“三锹”,这就是我使用 char 的原因。

我的另一个问题是,我的 get 方法看起来正确吗? get 方法是返回整个数组还是返回数组的索引,这正是我想要的。我正确使用'->'吗?

我仍在绘图过程中,这就是为什么我只有 sn-ps 的代码

【问题讨论】:

    标签: c++ oop pointers


    【解决方案1】:

    -&gt; 将获取 LHS 引用的对象的 RHS 元素的值。所以是的,只要您处理 LHS 上的指针,这是正确的。

    你可以考虑使用枚举来匹配。

    我建议您按照@littleadv 所说的那样再次查看您的评估逻辑。

    【讨论】:

      【解决方案2】:

      这里有一些草稿供您考虑,并可能在此基础上构建您的课程:

      class Card
      {
      public:
         // TODO: provide suitable constructor...
      
         enum Suit {Hearts, Diamonds, Clubs, Spades};
         enum Rank {A, K, Q, J, _10, _9, _8, _7, _6, _5, _4, _3, _2};
      
         Suit getSuit() { return suit; }
         Rank getRank() { return rank; }
      
         // TODO: implement setters, etc...
      
         bool operator==(const Card& c) const
         {
            return rank == c.rank && suit == c.suit;
         }
      
      private:
         Suit suit;
         Rank rank;
      };
      

      所以我建议你使用枚举并使用精确的比较标准来比较两张卡。
      同样,您可以根据自己的游戏规则和逻辑来实现 operator> 和 operator

      你明白了……

      【讨论】:

      • @Hovhannes 我尝试使用枚举类型,但出现错误。即使编译您的代码也不起作用。错误是:“错误 C4430:缺少类型说明符 - 假定为 int。注意:C++ 不支持 default-int”
      • @Danny 您在哪一行代码上遇到错误?可以正常编译了。
      • @Hovhannes 没关系。创建了一个新项目并再次尝试了您的代码,它编译得很好。谢谢。但我有个问题。为什么我需要一个 set 方法?是因为我需要稍后存储它以创建卡片的字符串表示形式吗?
      • @Hovhannes when is do "enum Suit {1, 2, 3, 4};"我得到了那个错误。是因为枚举不能接受数字吗? (这就是为什么你有下划线?)
      • @Danny: Card c1(Card::A, Card::Hearts)
      【解决方案3】:

      这是个坏主意,因为它浪费空间。您只需要存储一个号码,而不是所有号码。

      此外,您可以实现 == 运算符,而不是编写一组方法来执行此操作,这样使用起来会更自然。

      顺便说一句:你的逻辑 - 等级+西装是比较标准吗?花色4的等级9优于花色2的等级10?不知道你模拟的是什么游戏,不过一般都是名次在花色之前...不过那要看实际的要求了,当然只是说....

      关于使用 -&gt;get 方法的问题 - 查看编译器错误时得到它们。是的,在 get 方法中,您返回数组。但是如果你停止使用数组,只存储你需要的——那么这些方法就可以了。

      如果您出于某种原因想要返回字符串 - 您可以将存储的整数索引映射到某个静态数组中的正确字符串,这样您就不会在类的不同实例中存储冗余字符串。

      【讨论】:

      • 我忘了说。它是一款扑克游戏,但我试图通过使卡片的更高价值变得简单,从而使卡片的价值更大 3 的方块 (3+1) 小于 10 的黑桃 (10+4)。此外,当您说使用 == 操作时。你在哪里指的是我的比较方法?我需要在比较方法中返回 -1 0 1 并且它也必须获取方法
      • 呃。我误读了你的评论。等级确实先于花色。我会解决的
      • 这没关系。卡片没有内在价值,只有那些(如果有的话)由它们所使用的游戏定义。事实上,对于扑克,只有 hands 在问题域中具有任何等级。如果您需要使用标准库容器或算法的顺序,请实现您喜欢的任何总顺序。
      【解决方案4】:

      通过这个比较过程,您最终将拥有相同的牌...我猜这是错误的... 编辑:Visual Studio 中还有一个二十一点入门套件,您可以查看。

      【讨论】:

      • 我有 vs08。你知道我在哪里可以找到它吗?
      【解决方案5】:

      您开始使用工作代码逐步构建您的课程。从一个只有getSuitgetRank 方法的简单类开始。编写一个测试它的小应用程序,例如:

      int main() {
          Card card(10, 4);
      
          if (card.getSuit() != 4) {
              std::cout << "Wrong suit!" << std::endl;
          }
      
          return 0;
      }
      

      通过为您实现的每个功能编写这样的小测试,您可以确保一切正常。此外,如果您首先考虑要如何使用类,您将更好地理解实现。

      关于实际实施,很大程度上取决于您的课堂所教的内容。要考虑的一件简单的事情是使用enum 来表示等级和花色。

      【讨论】:

        【解决方案6】:

        这种有区别的联合最好用 ML 家族的语言来处理。

        例如在 OCaml 中:

        type suit = Hearts | Diamonds | Clubs | Spades
        type rank = Ace | King | Queen | Jack | Num of int
        type card = rank * suit
        

        是的,我知道,发帖人要求提供 C++ 答案...

        【讨论】:

          【解决方案7】:

          卡片不适合类 - 它没有行为,因此没有方法。因此,最好将其建模为结构。卡片是不可变的(除非你在作弊),所以会显示 const 成员数据:

          enum Suit {Hearts, Diamonds, Clubs, Spades};
          enum Rank {A, K, Q, J, _10, _9, _8, _7, _6, _5, _4, _3, _2};
          
          struct Card
          {
              Card(Suit suit, Rank rank) : suit(suit), rank(rank) {}
          
              const Suit suit;
              const Rank rank;
          };
          

          如果您可以使用语法 card.first 而不是 card.suit,您可以使用标准对免费获得:

          #include <utility>
          typedef const std::pair<Suit, Rank> Card;
          
          Card aceOfSpades(Spades, A);
          

          奖励:您还可以免费获得合理的 == 和

          【讨论】:

            【解决方案8】:

            使用界面

            它为学生提供了一种在他们编写的代码中使用卡片的简单方法,而无需访问 卡片的内部结构,无法创建特定的卡片,也不知道卡片的原理 实施的。这个过程从卡接口的代码开始,我们称之为 ICard 的接口。

            public interface ICard extends Comparable
            {
            public static final int SPADES = 0;
            public static final int HEARTS = 1;
            public static final int DIAMONDS = 2;
            public static final int CLUBS = 3;
            public int getSuit();
            public int getRank();
            }
            

            接口指定卡片的行为,但不提供卡片如何使用的信息 实施的。一旦他们知道getSuit() 返回一个类似 ICard.HEARTS 的值,并且 getRank()返回一个1(ace)到13(king)范围内的值,同学们可以从这里写代码 规格。例如,这是检查卡片数组是否已排序的代码。我们不 知道它是如何排序的(例如,是所有的 A 都在两个之前,还是所有的黑桃都来 在红心之前?),但我们可以确定一个数组是排序的。 1 以大写 I 开头的接口名称,后跟大写的名称,是一种常见的命名方式 许多语言的面向对象编程中的约定,而不仅仅是 Java。

            public boolean isSorted(ICard[] list){
            for(int k=1; k < list.length; k++){
            if (list[k-1].compareTo(list[k]) > 0){
            return false;
            }
            }
            return true;
            }
            

            从这个简单的ICard界面开始,我们可以问学生多种 用于测试和审查从 Java 语法到问题解决的概念的问题 关于一张、两张或多张牌。这里包括一些简单的例子, 网站上提供了更多信息。在回答这些问题时,学生必须 了解接口,因为没有实现。学生专注于 行为而不是实例变量和其他实现细节,例如 如何创建一个字符串来表示黑桃 A。 ICard 学习/代码问题

            1. 编写函数 isRed,如果其 ICard 参数为红色(红心或 钻石),否则返回 false。 public boolean isRed(ICard card){…}

            2. 对子是两张相同点数的牌(例如,两张 K 或两张 8)。编写函数 isPair 如果其两个 ICard 参数表示一对,则返回 true 并返回 false 否则。 public boolean isPair(ICard a, ICard b){…}

            3. 同花是一手牌,比如扑克牌,其中所有的牌都有相同的花色(例如,五张红心, 或五张梅花五张牌)。编写函数 isFlush,如果 卡片数组是同花顺,否则返回 false。 public boolean isFlush(ICard[] hand){…}

            4. 在 21 点或 21 点中,一手牌的价值是牌的总和,其中 J、Q 和 国王(分别为 11、12 和 13,由 getRank() 返回)每个都算作 10,并且 ace 算作 1 或 10,以更好的为准。总共超过 21 是半身像;破产不好。 编写函数handTotal,它返回一手牌的总值。 public int handTotal(ICard[] hand){…} 从接口到实现 ICard 接口提供了足够的信息来编写关于卡片的代码, 但是没有办法创建一个卡片数组,例如,甚至一个 card 来测试上面写的函数(比如 isPair 和 handTotal)。在哪里 卡是从哪里来的?在大多数现实世界的例子中,卡片来自甲板。好吧 设计一个模拟 Deck 的类——它基本上是一个创建和获取卡片的工厂。 为了简单起见,并鼓励学习一些标准的 Java 接口,该类 Deck 将实现 java.util.Iterator 接口。例如,要存储所有 将卡片中的卡片放入 ArrayList 变量中,我们可以使用以下代码:

              甲板 d = 新甲板(); ArrayList 卡片 = 新的 ArrayList(); 而(d.hasNext()){ ICard 卡 = (ICard) d.next(); System.out.println(card); 卡片。添加(卡片); } System.out.println("发牌数 = " + cards.size());

            这段代码 sn-p 输出的最后几行可能如下所示。他们会有所不同 每次都是因为这里开发的 Deck 类通过迭代洗牌它处理的牌。 … 黑桃王牌 千斤顶 黑桃六 红心十 黑桃十 发牌数 = 52 如果我们如下改变循环后的行,输出也会改变。

            Collections.sort(cards);
            for(int k=0; k < cards.size(); k++){
            System.out.println(cards.get(k));
            }
            

            System.out.println("发牌数 = " + cards.size()); 输出显示了从 Deck 类返回的卡片如何实现 Comparable 接口。 … 九个俱乐部 十家具乐部 千斤顶 夜店女王 俱乐部之王 发牌数量 = 52 Deck 类的完整代码如下所示。方法 hasNext()、next() 和 实现 Iterator 接口的类需要 remove()。下面的代码 显示了 Card 类型的对象是如何构造的。

            public class Deck implements Iterator{
            private ArrayList myCardList;
            private int myIndex;
            public Deck(){
            myCardList = new ArrayList();
            for(int suit = ICard.SPADES; suit <= ICard.CLUBS; suit++){
            for (int rank = 1; rank <= 13; rank++){
            myCardList.add(new Card(suit,rank));
            }
            }
            shuffle();
            }
            private void shuffle(){
            Collections.shuffle(myCardList);
            myIndex = 0;
            }
            public boolean hasNext() {
            return myIndex < myCardList.size();
            }
            public Object next() {
            ICard card = (ICard) myCardList.get(myIndex);
            myIndex++;
            return card;
            }
            public void remove() {
            throw new UnsupportedOperationException();
            }
            }
            

            一个 Deck 对象存储 52 张卡片 --- 这些卡片可以从一个 Deck 中获得 对象通过迭代,但 Deck 对象不能重新洗牌和重用。 相反,必须创建一个新的 Deck 对象来发新牌。这保持 事情很简单,并提供了一个易于理解的类示例 实现迭代器接口。方法 remove() 是可选的 --- 对于 调用此方法的 Deck 类会引发异常。 套牌研究/代码问题

            1. 就在构造函数中调用shuffle方法之前,描述 存储在 myCardList 中的对象。

            2. 描述当实例变量 myCardList 改变时每个 Deck 方法如何改变 到 Card 对象的数组,例如, 私人 ICard[] myCardList; myCardList 的哪个选择更好?为什么?

            3. 编写客户端代码,定义一个 Deck 对象并创建一个包含 13 个 ICard 对象的数组 代表从甲板上处理的黑桃。通过检查处理的每个对象来做到这一点 并且只存放黑桃牌。

            4. 编写下面指定的假设 Hand 类构造函数的主体

              私有 ArrayList myCards; /**

              • 从 d 处理 numCards 卡,存储在 myCards 中
              • (假设 d 中至少还剩 numCards 卡) */ 公共手(Deck d,int numCards){ }

            从套牌到卡片 我们最初关心的是使用 ICard 接口,而不是担心如何 卡实施。然而,在某些时候,需要有一个 执行。不难争论 Card 对象应该由 甲板课。这是我们在这里使用的方法。 Card 类是一个私有类 在 Deck 类中声明。实际上没有充分的理由在内部声明它 甲板(Deck.java 文件)。但是,通过将其声明为私有,我们将其设为 任何代码 class2 都不可能;它可以很容易地声明为内部的非公共类 Deck 类以外的方法来构造 Card 对象。这有助于实现我们最初的目标。 客户端程序可以从 Deck 中获取卡片,但不能创建卡片。由于甲板供应 ICard 对象,一旦从 Deck 中获得卡片,就无法更改卡片,因为 ICard 接口不支持修改卡片。

            正如所写,在 Deck 类中定义的私有 Card 类不支持 修改,因为它的私有状态实例变量是最终的,但这是额外的 可能不需要的保护,因为没有客户端代码可以访问私有 Card 类。 2 通常在另一个类中声明的类经常引用封闭对象的状态。在这个 如果嵌套类 Card 被声明为私有静态类,因此它不能引用私有非静态状态 在 Deck 对象中。 Card 类可以引用静态 Deck 状态,但此代码中没有。 Card 类在 Web 站点上可用;自实施以来,我们没有将其包括在此处 与我们关于设计的讨论没有直接关系。 细心的读者可能会声称我们最初的目标没有实现。客户端代码可以作弊,因为 例如,通过创建一个 Deck 对象,然后从该对象发牌直到一张 A 空格 (或任何其他牌)被处理。在 Deck 类的当前设计中,这是正确的。然而,我们 可以创建一个单独的 Deck 对象,就像类 Random 的单个实例一样 用于海洋生物学案例研究。单例对象通常通过声明创建 构造函数,以便它们是私有的。在这种情况下,Deck 构造函数将从 public 更改为 私人的。客户端代码通过调用公共的 getInstance() 方法获取 Deck,该方法 返回存储在 Deck 类中的私有静态 Deck 对象。 getInstance 方法 在第一次调用 getInstance 时创建此私有对象。

            【讨论】:

              猜你喜欢
              • 2014-02-27
              • 2012-06-07
              • 1970-01-01
              • 2014-07-16
              • 1970-01-01
              • 2023-01-11
              • 1970-01-01
              • 2020-09-30
              • 1970-01-01
              相关资源
              最近更新 更多