473,725 Members | 2,251 Online

# Shuffle card deck

I am very new to programming and learning on my own.

Why do I keep getting duplicate values using this code? I want to shuffle a
deck of 52 cards. The logic seems right to me.

Randomize

For C = 0 To 1000

C1 = Cards(Int(Rnd * 52)) ' returns a number from 0 to 51

C2 = Cards(Int(Rnd * 52)) ' returns a number from 0 to 51
Cards(C1) = C2 ' Value of C2 is assigned to Card in location Cards(C1)
Cards(C2) = C1 ' Value of C1 is assigned to Card in location Cards(C2)

Next C

TIA
Jul 17 '05 #1
23 12951
"JC" <no************ @hotmail.com> wrote in
I am very new to programming and learning on my own.

Why do I keep getting duplicate values using this code? I want to
shuffle a deck of 52 cards. The logic seems right to me.

Randomize

For C = 0 To 1000

C1 = Cards(Int(Rnd * 52)) ' returns a number from 0 to 51

C2 = Cards(Int(Rnd * 52)) ' returns a number from 0 to 51
Cards(C1) = C2 ' Value of C2 is assigned to Card in location
Cards(C1) Cards(C2) = C1 ' Value of C1 is assigned to Card in
location Cards(C2)

Next C

Your random values are not guaranteed to be unique. When shuffling
cards you have only one value of each card in a normal deck.

You need an entirely different algorithm.

Following is an example of defining a deck of cards, including the
shuffle routine, using the Ada language:

First I define a package specification for cards:

-----------------------------------------------------------------------
-- Package implementing a standard deck of playing cards
-----------------------------------------------------------------------

package Cards is

type Card is private;

-- Print the value of a card
procedure Print(Item : in Card);

type Deck is private;

-- Create an initial deck (open a new deck of cards)
function Fill_Deck return Deck;

-- Print all the cards remaining in a deck
procedure Print(Item : in Deck);

-- Shuffle the deck (randomize the order of the cards in the deck)
procedure Shuffle(The_Dec k : in out Deck);

-- Deal the next card from the deck
procedure Deal(The_Card : out Card; From : in out Deck);

-- Return the number of cards left in the deck
function Cards_Left(In_D eck : Deck) return Natural;

-- Deck_Empty exception raised when trying to deal from an empty deck.
Deck_Empty : Exception;

private

-- Define the face values of the cards
type Pips is (Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten,
Jack, Queen, King, Ace);

-- Define the card suits
type Suits is (Hearts, Spades, Clubs, Diamonds);

-- A card is defined by its combination of face value
-- and suit.
type Card is record
Pip : Pips;
Suit : Suits;
end record;

-- Define the number of cards in a standard deck.
subtype Deck_Index is integer range 1..52;

-- Cards in the deck are accessed through an order list.
-- The values in the order list are sorted to create a
-- shuffled deck.
type Order_List is array(Deck_Inde x) of Deck_Index;

-- A deck is an order list, an index into the order list
-- indicating the next card to deal, and a count of the
-- number of cards left (not yeat dealt) in the deck.
type Deck is record
This_Order : Order_List;
Deal_Next : Deck_Index := Deck_Index'Firs t;
Num_Left : Natural := 0;
end record;
end Cards;

Next I define the implementation of all the routines declared in
the package specification above:

-----------------------------------------------------------------------
-- Implementation of the Cards package
-----------------------------------------------------------------------

package body Cards is

----------------
-- The Card_Deck array is the only array in this package
-- actually containing Card objects.
-- A Deck is a data structure with an array of indexes into
-- this single Card_Deck.
-- A Deck is shuffled by randomizing that array of indexes.
----------------
type Card_Deck is array(Deck_Inde x) of Card;

--------------
-- Internal Function: Initialize
-- Purpose: Initialize the value of the common Card_Deck
-- This function is called only once during the elaboration of
-- this package. It is used to initialize the values in the
-- All_Decks array.
--------------
function Initialize return Card_Deck is
Result : Card_Deck;
Temp_Index : Integer := Deck_Index'Firs t;
begin
for The_Suit in Suits loop
for The_Pip in Pips loop
Result(Temp_Ind ex) := (The_Pip, The_Suit);
Temp_Index := Temp_Index + 1;
end loop;
end loop;
return Result;
end Initialize;

All_Decks : constant Card_Deck := Initialize;

-----------
-- Procedure: Print
-- Purpose: Print the value of a card on standard output
-- The enumeration labels will be printed using instantiations
-- of the generic package Ada.Text_IO.Enu meration_Io.
-----------
procedure Print(Item : in Card) is
package Pip_Io is new Ada.Text_Io.Enu meration_IO(Pip s);
package Suit_Io is new Ada.Text_Io.Enu meration_Io(Sui ts);
begin
Pip_Io.Put(Item => Item.Pip);
Ada.Text_Io.Put (Item => " of ");
Suit_Io.Put(Ite m => Item.Suit);
end Print;

-----------------
-- Function: Fill_Deck
-- Purpose: Create a new card deck with all cards in order
----------------
function Fill_Deck return Deck is
Result : Deck;
begin
for Temp_Index in Deck_Index'Rang e loop
Result.This_Ord er(Temp_Index) := Temp_Index;
end loop;
Result.Num_Left := Deck_Index'Last ;
return Result;
end Fill_Deck;

---------
-- Procedure: Print
-- Purpose: Print all the cards remaining in the deck
---------

procedure Print(Item : in Deck) is
begin
if Item.Num_Left > 0 then
for Temp_Index in Item.Deal_Next. .Deck_Index'Las t loop
Print(All_Decks (Item.This_Orde r(Temp_Index))) ;
end loop;
else
end if;
end Print;

------------
-- Procedure Swap
-- Exchange two Deck_Index values
-- This procedure is visible only to functions and procedures
-- defined in this package body.
--------------
procedure Swap(Left, Right : in out Deck_Index) is
Temp : Deck_Index := Left;
begin
Left := Right;
Right := Temp;
end Swap;

-------------
-- Procedure: Shuffle
-- Purpose: Randomize the This_Order array for a deck to force
--
-- This algorithm is order O(n) and will work with any discrete
-- index type.
-- The Ada.Numerics.Fl oat_Random routine is used so that the
-- random number generator is reset only once per shuffle. This
-- produces more random results than can be achieved by
-- resetting the generator for each iteration as would be needed
------------

procedure Shuffle(The_Dec k : in out Deck) is
Seed : Generator;
-- Max_Search is set to the 2nd to last value in Deck_Index
Max_Search : Deck_Index := Deck_Index'Pred (Deck_Index'Las t);
Difference : Integer;
Rand_Value : Integer;
Swap_Val : Deck_Index;
begin
Reset(Seed);
The_Deck.Deal_N ext := Deck_Index'Firs t;
The_Deck.Num_Le ft := Deck_Index'Last ;
for Index in Deck_Index'Firs t .. Max_Search loop
Difference := Deck_Index'Pos( Deck_Index'Last ) -
Deck_Index'Pos( Index);
Rand_Value := Integer( Random(Seed) * Float(Differenc e)) +
Deck_Index'Pos( Index);
Swap_Val := Deck_Index'Val( Rand_Value);
Swap(The_Deck.T his_Order(Index ),
The_Deck.This_O rder(Swap_Val)) ;

end loop;
end Shuffle;

--------------------------------------------------------------------
-- The Deal procedure produces the next card from the deck.
-- It also updates the Deck data structure to account for
-- another card being removed from the Deck, and finally, it
-- updates the Deck's Deal_Next index to point to the next card
-- to deal from the deck.
-------------------------------
procedure Deal(The_Card : out Card; From : in out Deck) is
begin
if From.Num_Left > 0 then
The_Card := All_Decks(From. This_Order(From .Deal_Next));
From.Num_Left := From.Num_Left - 1;
if From.Deal_Next < Deck_Index'Last then
From.Deal_Next := From.Deal_Next + 1;
end if;
else
-- raise the Deck_Empty exception
raise Deck_Empty;
end if;
end Deal;

--------------------------------------------------------------------
-- The function Cards_Left simply reports the number of cards not
-- yet dealt from the deck. This number can be anything from 0
-- through Deck_Index'Last .
--------------------------------------------------------------------
function Cards_Left(In_D eck : Deck) return Natural is
begin
return In_Deck.Num_Lef t;
end Cards_Left;
end Cards;

Finally, I show a procedure to create a deck of cards and do the
shuffling:

-----------------------------------------------------------------------
-- This procedure is a test driver for the Cards package
-----------------------------------------------------------------------

with Cards;

procedure Card_Deck is

My_Deck : Cards.Deck;
This_Card : Cards.Card;

begin

-- Create a new deck of cards, like opening a new deck of
-- cards. The deck returned is sorted by suit and value.
My_Deck := Cards.Fill_Deck ;
Cards.Print(My_ Deck);

-- Shuffle the deck so that the cards are accessed in a
-- random order.
Cards.Shuffle(M y_Deck);
Cards.Print(My_ Deck);

-- Deal out the cards, printing each dealt card.
Ada.Text_Io.Put _Line("Printing each card as it is dealt:");
while Cards.Cards_Lef t(In_Deck => My_Deck) > 0 loop
Cards.Deal(The_ Card => This_Card, From => My_Deck);
Cards.Print(Thi s_Card);
end loop;

-- Attempt to deal one more card from the deck. This will raise
-- the Deck_Empty exception.
Ada.Text_Io.Put _Line("Attempti ng to deal from an empty deck:");

-- the following unnamed block encapsulates its own exception
-- handler for the Cards.Deck_Empt y exception.
begin
Cards.Deal(The_ Card => This_Card, From => My_Deck);
Cards.Print(Thi s_Card);
exception
when Cards.Deck_Empt y =>
"ERROR: You attempted to deal from an empty deck.");
end;

-- Attempt to print an empty deck
Cards.Print(My_ Deck);

end Card_Deck;

Jim Rogers
Jul 17 '05 #2

"James Rogers" <ji************ **@att.net> wrote in message
news:Xn******** *************** *******@204.127 .36.1...
"JC" <no************ @hotmail.com> wrote in
I am very new to programming and learning on my own.

Why do I keep getting duplicate values using this code? I want to
shuffle a deck of 52 cards. The logic seems right to me.

Randomize

For C = 0 To 1000

C1 = Cards(Int(Rnd * 52)) ' returns a number from 0 to 51

C2 = Cards(Int(Rnd * 52)) ' returns a number from 0 to 51
Cards(C1) = C2 ' Value of C2 is assigned to Card in location
Cards(C1) Cards(C2) = C1 ' Value of C1 is assigned to Card in
location Cards(C2)

Next C

Your random values are not guaranteed to be unique. When shuffling
cards you have only one value of each card in a normal deck.

You need an entirely different algorithm.

Following is an example of defining a deck of cards, including the
shuffle routine, using the Ada language:
Jim Rogers

Thanks for the response. What I am looking for is just a Randomizing
routine that could be applied to any situation.

I have already created an array for the cards. I just want to randomize
the index which I address them by, but sometimes 2 or even three cards of
same suit and value are being drawn. I am guessing that my index itself has
to have the duplicate values, since each of my 52 card array is unique.
Don't know how much sense this makes to anyone.

I wrote a video poker program on my Commodore 64 about twenty years ago,
which was the first and last time I ever tried to write a real program. It
worked great in that I got it to do exactly what I wanted, and I used a
routine similar to the above, without have duplicate cards showing up.

Jul 17 '05 #3
In your code, C1 and C2 are not the indexes of the cards, they are the values.
Yet you use them to decide which two cards to exchange, instead of the indexes
where they came from.

Say the first Int(Rnd * 52) came up 22, and the second one came up 37. Say that
Cards(22) = 17, and Cards(37) = 24. Your code would then change the value of
Cards(17) to 24 (same as Cards(37), which is unchanged), and change Cards(24) to
17 (same as Cards(22), also unchanged). Now you have two 17's and two 22's.

Try substituting this in the loop:

c1 = (Int(Rnd * 52)) ' returns an index from 0 to 51
c2 = (Int(Rnd * 52)) ' returns an index from 0 to 51

temp = Cards(c1) 'save value of cards(c1)
Cards(c1) = Cards(c2) 'assign cards(c2) to cards(c1)
Cards(c2) = temp 'assign old cards(c1) to cards(c2)

If c1 = c2, there is not much point in swapping a card with itself, so you could
put in a test to skip those.

Steve

"JC" <no************ @hotmail.com> wrote in message
I am very new to programming and learning on my own.

Why do I keep getting duplicate values using this code? I want to shuffle a
deck of 52 cards. The logic seems right to me.

Randomize

For C = 0 To 1000

C1 = Cards(Int(Rnd * 52)) ' returns a number from 0 to 51

C2 = Cards(Int(Rnd * 52)) ' returns a number from 0 to 51
Cards(C1) = C2 ' Value of C2 is assigned to Card in location Cards(C1)
Cards(C2) = C1 ' Value of C1 is assigned to Card in location Cards(C2)

Next C

TIA

Jul 17 '05 #4

"Steve Gerrard" <no************ *@comcast.net> wrote in message
news:YT******** ************@co mcast.com...
In your code, C1 and C2 are not the indexes of the cards, they are the values. Yet you use them to decide which two cards to exchange, instead of the indexes where they came from.

Say the first Int(Rnd * 52) came up 22, and the second one came up 37. Say that Cards(22) = 17, and Cards(37) = 24. Your code would then change the value of Cards(17) to 24 (same as Cards(37), which is unchanged), and change Cards(24) to 17 (same as Cards(22), also unchanged). Now you have two 17's and two 22's.
Try substituting this in the loop:

c1 = (Int(Rnd * 52)) ' returns an index from 0 to 51
c2 = (Int(Rnd * 52)) ' returns an index from 0 to 51

temp = Cards(c1) 'save value of cards(c1)
Cards(c1) = Cards(c2) 'assign cards(c2) to cards(c1)
Cards(c2) = temp 'assign old cards(c1) to cards(c2)

If c1 = c2, there is not much point in swapping a card with itself, so you could put in a test to skip those.

Steve

That's it! I know I shouldn't try to think on an empty brain...... I was not
assigning Cards(c1) = Cards(c2) but instead, going straight to Cards(c1) =
c2.

Thanks, Steve!
Jul 17 '05 #5
"JC" <no************ @hotmail.com> wrote in
Thanks for the response. What I am looking for is just a Randomizing
routine that could be applied to any situation.
The Shuffle procedure in my example does what you want.
It is a reasonably efficient way to randomize a collection of objects
and avoid duplicates.

I have already created an array for the cards. I just want to
randomize
the index which I address them by, but sometimes 2 or even three cards
of same suit and value are being drawn. I am guessing that my index
itself has to have the duplicate values, since each of my 52 card
array is unique. Don't know how much sense this makes to anyone.
There is nothing wrong with moving a single card more than once
during a shuffle. What is wrong is having the same card value more
than once in a single deck.

Your deck is not properly constructed if you are generating the values
more than once.

Note that the example I showed created and initialized a deck of cards
before shuffling them. No new card values were created. Only the order
of the values was changed.

My example optimized this a bit by using an ordering array, rather than
actually moving about the cards in the card deck. The reason for this
is that the cards may be constructed using a larger data representation,
including information such as suit and pip value. Moving large values
is less efficient than simply moving index values. My example simply
moves around the index values.

Using this scheme, I only have one deck of actual card values. The user
"deck" really contains an ordering array of index values. This allows
minimum use of memory and maximum shuffling speed. Users can appear to
have individual decks, but all decks refer to a single set of actual
card values. Each user simply sees those values in a different order.

I wrote a video poker program on my Commodore 64 about twenty years
ago,
which was the first and last time I ever tried to write a real
program. It worked great in that I got it to do exactly what I wanted,
and I used a routine similar to the above, without have duplicate
cards showing up.

My suspicion is that your current routine is somehow different from the
one you used twenty years ago. That difference just might be important.

Jim Rogers
Jul 17 '05 #6
' initialize the deck
For i = 1 To 52
cards(i) = i
Next i

' shuffle the deck
' this shuffling assures that each card
' changes position at least once
For N = 52 To 2 Step -1
K = Int(N * Rnd) + 1
TEMP = cards(N)
cards(N) = cards(K)
cards(K) = TEMP
Next N

' array cards(i) now has the shuffled deck
==========
On Tue, 23 Sep 2003 20:39:21 -0700, "JC" <no************ @hotmail.com>
wrote:
I am very new to programming and learning on my own.

Why do I keep getting duplicate values using this code? I want to shuffle a
deck of 52 cards. The logic seems right to me.

Randomize

For C = 0 To 1000

C1 = Cards(Int(Rnd * 52)) ' returns a number from 0 to 51

C2 = Cards(Int(Rnd * 52)) ' returns a number from 0 to 51
Cards(C1) = C2 ' Value of C2 is assigned to Card in location Cards(C1)
Cards(C2) = C1 ' Value of C1 is assigned to Card in location Cards(C2)

Next C

TIA

Jul 17 '05 #7
JC wrote:
I am very new to programming and learning on my own.

Why do I keep getting duplicate values using this code? I want to shuffle a
deck of 52 cards. The logic seems right to me.

Randomize

For C = 0 To 1000

C1 = Cards(Int(Rnd * 52)) ' returns a number from 0 to 51

C2 = Cards(Int(Rnd * 52)) ' returns a number from 0 to 51
Cards(C1) = C2 ' Value of C2 is assigned to Card in location Cards(C1)
Cards(C2) = C1 ' Value of C1 is assigned to Card in location Cards(C2)

When you swap two things you need a temporary**! Why loop to 1000? You
can loop just 52 times if you swap each card with a randomly chosen other.

for(int c=0; c<52; ++c)
{
Card &a = cards[c];
Card &b = cards[rnd(52)]; // For a suitably defined rnd

Card t = a; // Not expensive if Card is 4 bytes or 1 byte
a = b;
b = t;
}
** There is also an XOR hack:
a = a^b, b = a^b, a = a^b;
saves a register spill :-)

Jul 17 '05 #8
In article <Xn************ *************** ***@204.127.36. 1>,
James Rogers <ji************ **@att.net> wrote:
.... -- Define the number of cards in a standard deck.
subtype Deck_Index is integer range 1..52;
Just curious -- why not

subtype Deck_Index is integer range Pips'Range * Suits'Range;

Is that not possible? That would be consistent with the Initialize
function:
function Initialize return Card_Deck is
Result : Card_Deck;
Temp_Index : Integer := Deck_Index'Firs t;
begin
for The_Suit in Suits loop
for The_Pip in Pips loop
Result(Temp_Ind ex) := (The_Pip, The_Suit);
Temp_Index := Temp_Index + 1;
end loop;
end loop;
return Result;
end Initialize;

Cheers,
Gorazd
--
Gorazd Bozic <gb*@email.si >
Jul 17 '05 #9
> I am very new to programming and learning on my own.

Why do I keep getting duplicate values using this code? I want to shuffle a deck of 52 cards. The logic seems right to me.

Randomize

For C = 0 To 1000

C1 = Cards(Int(Rnd * 52)) ' returns a number from 0 to 51

C2 = Cards(Int(Rnd * 52)) ' returns a number from 0 to 51
Cards(C1) = C2 ' Value of C2 is assigned to Card in location Cards(C1)
Cards(C2) = C1 ' Value of C1 is assigned to Card in location Cards(C2)

Next C

previous post of mine in case the 2nd method used in it seemed useful to
you.

Rick - MVP

Not sure what "Hit or Miss" solitaire is, but here are two versions of a
card shuffling routine (depending on how you store the cards) that I have
posted previously.

FIRST METHOD
=============== ==
The following is a generalized "shuffling" routine. Give it an array of
elements and it will put them in random order and return the randomized
elements back in the original array that was passed to it. It only visits
*each* array element *once* so it is quick. The code takes care of running
the Randomize statement one time only (which is all that is necessary).

Sub RandomizeArray( ArrayIn As Variant)
Dim X As Long
Dim RandomIndex As Long
Dim TempElement As Variant
Static RanBefore As Boolean
If Not RanBefore Then
RanBefore = True
Randomize
End If
If VarType(ArrayIn ) >= vbArray Then
For X = UBound(ArrayIn) To LBound(ArrayIn) Step -1
RandomIndex = Int((X - LBound(ArrayIn) + 1) * _
Rnd + LBound(ArrayIn) )
TempElement = ArrayIn(RandomI ndex)
ArrayIn(RandomI ndex) = ArrayIn(X)
ArrayIn(X) = TempElement
Next
Else
'The passed argument was not an array
'Put error handler here, such as . . .
Beep
End If
End Sub

The passed array may be of any normal type -- integer, string, single, etc.
The neat thing is, if you pass an already randomized array to this routine,
those randomly ordered elements will be randomize -- sort of like shuffling
an already shuffled deck of cards. In your case, simply set up the array
(probably Global or Static if you want to use it over and over) something
like this

Dim DeckOfCards(1 To 52) As Long
For X = 1 To 52
DeckOfCards(X) = X
Next

and to shuffle (randomize) it, simply call

RandomizeArray DeckOfCards

Each array element will now hold a unique, random number from 1 through 52
for the above example.
SECOND METHOD
=============== ==
Here is another take on the same routine which actually returns "named"
cards such as 3 of Hearts (here your DeckOfCards is declared as a String:

Sub ShuffleDeck(Dec k() As String)
Dim X As Integer
Dim TempInt As Integer
Dim TempCard As String
Static TempDeck(1 To 52) As String
Static RanBefore As Boolean
If Not RanBefore Then
RanBefore = True
Randomize
If UBound(Deck) <> 52 Then
'Programmer passed an improper array
MsgBox "Deck array is dimensioned incorrectly"
Exit Sub
ElseIf TempDeck(52) = "" Then
'Initialize the deck of cards
For X = 1 To 52
If ((X - 1) Mod 13) = 0 Then
TempDeck(X) = "Ace"
ElseIf ((X - 1) Mod 13) = 10 Then
TempDeck(X) = "Jack"
ElseIf ((X - 1) Mod 13) = 11 Then
TempDeck(X) = "Queen"
ElseIf ((X - 1) Mod 13) = 12 Then
TempDeck(X) = "King"
Else
TempDeck(X) = CStr(1 + ((X - 1) Mod 13))
End If
TempDeck(X) = TempDeck(X) & " of "
If (X - 1) \ 13 = 0 Then
ElseIf (X - 1) \ 13 = 1 Then
TempDeck(X) = TempDeck(X) & "Hearts"
ElseIf (X - 1) \ 13 = 2 Then
TempDeck(X) = TempDeck(X) & "Diamonds"
ElseIf (X - 1) \ 13 = 3 Then
TempDeck(X) = TempDeck(X) & "Clubs"
End If
Next
End If
End If
'Let us shuffle the deck
X = 52
For X = 52 To 1 Step -1
TempInt = Int(X * Rnd + 1)
Deck(X) = TempDeck(TempIn t)
TempCard = TempDeck(X)
TempDeck(X) = TempDeck(TempIn t)
TempDeck(TempIn t) = TempCard
Next
End Sub

Everything is self-contained in this version; just pass it an array
dimensioned between 1 and 52 as in this example use:

Private Sub Command1_Click( )
Dim MyDeck(1 To 52) As String
ShuffleDeck MyDeck
Debug.Print MyDeck(1) & ", " & MyDeck(4) & ", " & MyDeck(43)
End Sub
Jul 17 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.