On 8/1/2014 9:16 PM, Scott Dunning wrote:
> [...]
> //shuffle(n): shuffle the deck
> public void shuffle(int n) {
> int i, j, k;
> for ( k = 0; k < n; k++ ) {
> i = (int) ( NEWCARDS * Math.random() ); // Pick 2 random
> cards
> j = (int) ( NEWCARDS * Math.random() ); // in the deck?
>
> //swap these randomly picked cards
> Card temp = deckOfCards[i];
> deckOfCards[i] = deckOfCards[j];
> deckOfCards[j] = temp;
> }
> currentCard = 0; // Reset current card to deal
> }
> }
Others have observed that this shuffling procedure is biased,
and suggested alternatives. I'd just like to point out that it
may not be necessary to shuffle at all!
Here's the idea: The fundamental operation is "deal one card"
(you can do this N times in succession to deal N cards). Judging
by your `currentCard' variable, you plan to shuffle the deck and
then march through it in sequence, dealing the first, second, ...
card in succession. That will work fine (if you shuffle well).
But what if you just left the deck in any old order at all,
even "factory-fresh" with all the suits and ranks in order, *but*
instead of dealing sequentially you dealt a randomly-chosen card?
You'd need to ensure that a card already dealt wouldn't be dealt
again (in the Wild West, the presence of two Aces of Spades in a
single poker game might well provoke a gunfight), so after dealing
the randomly-chosen card you'd need to remove it from the deck.
That's a fairly natural idea, though: If you start with a deck of
fifty-two cards and deal one of them, you expect to be holding a
deck of fifty-one, right?
Okay, so this suggests not a `currentCard' variable that
marches through a shuffled array, but a `cardCount' variable
that keeps track of how many un-dealt cards remain. In a fresh
deck this variable would be 52, and it would decrease as cards
were plucked out and dealt away. Here's an outline:
public class DeckOfCards {
private final Card[] cards;
private int cardCount;
public DeckOfCards() {
cards = new Card[52];
// ... fill the array with Cards ...
cardCount = 52;
}
public Card dealOneCard() {
// Sanity check:
if (cardCount <= 0) {
throw new IllegalStateException("no cards left!");
}
// Choose the card to deal:
int which = (int) (cardCount * Math.random());
Card dealt = cards[which];
// Remove the dealt card from the deck by moving
// the last card into the vacated place (this works
// even when dealing the last card):
cards[which] = cards[cardCount - 1];
// There's one less card than there used to be:
--cardCount;
return dealt;
}
// ... other methods ...
}
(I've written this for clarity; there are ways to tighten it up a
little, and Mike Amling has suggested a better way to get a random
integer, but I've tried to stick with things you've already shown
you're familiar with.)
Your assignment says you're supposed to write a shuffle() method,
and perhaps your instructor will give you poor marks if you don't.
But then you might pacify him with
public void shuffle() {
if (cardCount != 52) {
throw new IllegalStateException(
"not playing with a full deck (as promised)!");
}
}
If he objects that the method is vacuous, your comeback could be
that *by using the other methods of DeckOfCards* there's no way
to detect the vacuousness! He can create as many decks and deal
as many cards as he likes and subject them to every statistical
test at his disposal, and although he might uncover problems with
Math.random() and so on he will never be able to show that you're
not shuffling!
... and *that* is another important aspect of programming,
called "encapsulation." The idea is that a class promises to
provide various behaviors but does not promise to do so in any
particular way. You describe *what* the (exposed) methods of
your class will do, but you don't say *how* they'll do it. This
means that next week when you think of a better way (like, how
to deal a random card without shuffling), you're free to change
how you've done things. A short-order cook filling two orders
for scrambled eggs needn't reveal whether he cooked two small
batches or one big one; all he needs to do is serve up two platefuls
by whatever means he finds convenient. If your classes follow a
similar philosophy, you'll write better classes.
--
eso...@comcast-dot-net.invalid