Java程序设计 - 卡洗牌器

时间:2011-09-15 15:35:01

标签: java oop hashset design-principles sortedset

我正在尝试写一个卡洗牌器,我知道我想要洗牌的方法。但是,我无法用最好的面向对象的方式来编写它。

这个方法很常见,如下:

  • 为每个分配一个0到2,147,483,647
  • 之间的随机数值
  • 如果有重复的键值(非常不可能),请扔掉牌组,然后重新开始。
  • 将卡存放在一套
  • 按每张卡片的关键值排序

我的问题在于用最好的OOP方式来编写它。起初我想出了一个名为Card的对象,其中包含一个套装值,一个数值和随机键值。然后我会有一个名为Deck的类扩展HashSet,我会将每张卡存储到HashSet中,然后按键值对其进行排序。在我努力的地方,首先“生成”52 Card个对象的最有效方法是什么,以及如何订购该集合。我会实现接口'SortedSet',如果是这样,我将如何编写比较器?

相当广泛的问题,更多基于OOP设计实践,但我希望这是一个非常流畅的基于对象的解决方案

干杯,

编辑:

感谢大家的帮助。我的解决方案如下:

  • 2个枚举(CardValues,CardSuits),包含4个套装和13个可能的值
  • Card类,用作CardValue和CardSuit的构造函数参数。
  • 扩展TreeMap的Deck类

当创建和改组新牌组时,我循环穿过CardSuit Enum并创建牌,然后在该圈内,我通过CardValue Enum。这会创建卡片,然后我生成一个随机密钥并将它们放在TreeMap中。

由于密钥重复的可能性很小,如果最终套牌大小不是52,我会抛出一个新的InvalidDeckException。

感谢您的建议,我对此解决方案感到非常满意。

7 个答案:

答案 0 :(得分:1)

我写了一些扑克分析的东西。我为所有卡类型创建了一个枚举,包含rankvalue字段。然后我初始化了所有52张卡枚举的可能性。所以是的,我有52个enum defs(并且所有可能的2张牌起手都有一个单独的枚举 - 有时候蛮力是最好的选择)

然后我创建了一个Deck类,其中List种类型为Enum<Card>

初始化Deck就像生成Enum的EnumSet一样简单,并将该集传递给List。然后,您可以将shuffle方法放在Deck类上,并让它使用Deck列表。

这样做的好处是你的应用程序中只有52张牌 - 就像FlyWeight模式一样。

答案 1 :(得分:1)

使用TreeMap,为每张卡生成一张地图中不存在的随机数,并将其作为卡的密钥插入地图中,完成。

地图现在按生成的随机数排序。

另见http://en.wikipedia.org/wiki/Shuffling#Shuffling_algorithms

请注意,这是一种迟钝的混淆方式,只需使用Collections.shuffle()

答案 2 :(得分:1)

<强> Card.java

import java.util.*;

public class Card {
    public enum Rank { DEUCE, THREE, FOUR, FIVE, SIX,
        SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE }

    public enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES }

    private final Rank rank;
    private final Suit suit;
    private Card(Rank rank, Suit suit) {
        this.rank = rank;
        this.suit = suit;
    }

    public Rank rank() { return rank; }
    public Suit suit() { return suit; }
    public String toString() { return rank + " of " + suit; }

    private static final List<Card> protoDeck = new ArrayList<Card>();

    // Initialize prototype deck
    static {
        for (Suit suit : Suit.values())
            for (Rank rank : Rank.values())
                protoDeck.add(new Card(rank, suit));
    }

    public static ArrayList<Card> newDeck() {
        return new ArrayList<Card>(protoDeck); // Return copy of prototype deck
    }
}

Deal.java

import java.util.*;

public class Deal {
    public static void main(String args[]) {
        int numHands = Integer.parseInt(args[0]);
        int cardsPerHand = Integer.parseInt(args[1]);
        List<Card> deck  = Card.newDeck();
        Collections.shuffle(deck);
        for (int i=0; i < numHands; i++)
            System.out.println(deal(deck, cardsPerHand));
    }

    public static ArrayList<Card> deal(List<Card> deck, int n) {
         int deckSize = deck.size();
         List<Card> handView = deck.subList(deckSize-n, deckSize);
         ArrayList<Card> hand = new ArrayList<Card>(handView);
         handView.clear();
         return hand;
     }
}

<强>输出:

 $ java Deal 4 5
    [FOUR of HEARTS, NINE of DIAMONDS, QUEEN of SPADES, ACE of SPADES, NINE of SPADES]
    [DEUCE of HEARTS, EIGHT of SPADES, JACK of DIAMONDS, TEN of CLUBS, SEVEN of SPADES]
    [FIVE of HEARTS, FOUR of DIAMONDS, SIX of DIAMONDS, NINE of CLUBS, JACK of CLUBS]
    [SEVEN of HEARTS, SIX of CLUBS, DEUCE of DIAMONDS, THREE of SPADES, EIGHT of CLUBS]

参考

答案 3 :(得分:1)

我会将卡定义为持有套件的班级和数字值。卡不应该知道相关的随机数。

The Deck是一个包含卡片列表并具有随机播放方法的类,使用随机数字,可能不需要存储在某处。

答案 4 :(得分:1)

http://download.oracle.com/javase/6/docs/api/java/util/Collections.html#shuffle(java.util.List

请参阅java.util.Collections util class中的shuffle方法的上述doc。

 class Card {

private int number;
// other attributes

}

并简单地使用: -

Collections.shuffle(listOfCards);

答案 5 :(得分:0)

最简单的方法是为卡类型分配一个值属性并对其进行排序(面卡J,Q,K,A-> 11,12,13,14)。订购套装你必须选择一些任意的订单并组织成一个“包装”的卡片,其中包含每件套装的分类。由于套装永远不会改变,所以Pack总是有一个分类的每个俱乐部,心,钻石和黑桃。

生成卡片?每包显然必须定义4套。在给出套装的情况下,拥有一个CardFactory,为每件套装生成并返回一整套。工厂只会盲目地生成2-10和面部卡并返回有序集。

答案 6 :(得分:0)

您不应该进入与特定卡片不同的Card类数据。特别是,套牌内的顺序和随机数不是卡的属性(无论卡片在哪里,卡都是相同的。)

卡片属性应该只是它的值和状态(显示/隐藏)。卡组可以用List实现。洗牌只是Deck的一种方法。

对于改组,有几种选择:

1)你自己列出随机数并订购它们;随机数列表中的每个变化都在卡列表中再现。问题是您无法使用已经可用于List()

的排序方法

2)你添加一个包含卡片和随机数的中间类(让我们称之为ShufCard),并且:或者

a)牌组是这些中间物体的清单。

b)对于改组,你创建一个包含甲板内容的时间列表,随机播放,然后从保存其顺序的时间列表中检索卡片。类似的东西:

 List<ShufCard> list = new ArrayList<ShufCard>();
 for (Card card : Deck.cards) {
   list.add(new ShufCard(Card, Random.getNumber());
 }

 list.sort();
 Deck.cards.clear();

 for (ShufCard shCard : list) {
   Deck.cards.add(shCard.getCard());
 }