"JC" <no************@hotmail.com> wrote in
news:bk**********@sun-news.laserlink.net:
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:
-----------------------------------------------------------------------
-- Cards.ads
-- 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_Deck : 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_Deck : 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_Index) 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'First;
Num_Left : Natural := 0;
end record;
end Cards;
Next I define the implementation of all the routines declared in
the package specification above:
-----------------------------------------------------------------------
-- Cards.adb
-- Implementation of the Cards package
-----------------------------------------------------------------------
with Ada.Numerics.Float_Random;
with Ada.Text_Io;
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_Index) 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'First;
begin
for The_Suit in Suits loop
for The_Pip in Pips loop
Result(Temp_Index) := (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.Enumeration_Io.
-----------
procedure Print(Item : in Card) is
package Pip_Io is new Ada.Text_Io.Enumeration_IO(Pips);
package Suit_Io is new Ada.Text_Io.Enumeration_Io(Suits);
begin
Pip_Io.Put(Item => Item.Pip);
Ada.Text_Io.Put(Item => " of ");
Suit_Io.Put(Item => Item.Suit);
Ada.Text_Io.New_Line;
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'Range loop
Result.This_Order(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'Last loop
Print(All_Decks(Item.This_Order(Temp_Index)));
end loop;
else
Ada.Text_Io.Put_Line("The deck is empty.");
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
-- random access to the deck of cards
--
-- This algorithm is order O(n) and will work with any discrete
-- index type.
-- The Ada.Numerics.Float_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
-- if the Ada.Numerics.Discrete_Random package had been used.
------------
procedure Shuffle(The_Deck : in out Deck) is
use Ada.Numerics.Float_Random;
Seed : Generator;
-- Max_Search is set to the 2nd to last value in Deck_Index
Max_Search : Deck_Index := Deck_Index'Pred(Deck_Index'Last);
Difference : Integer;
Rand_Value : Integer;
Swap_Val : Deck_Index;
begin
Reset(Seed);
The_Deck.Deal_Next := Deck_Index'First;
The_Deck.Num_Left := Deck_Index'Last;
for Index in Deck_Index'First .. Max_Search loop
Difference := Deck_Index'Pos(Deck_Index'Last) -
Deck_Index'Pos(Index);
Rand_Value := Integer( Random(Seed) * Float(Difference)) +
Deck_Index'Pos(Index);
Swap_Val := Deck_Index'Val(Rand_Value);
Swap(The_Deck.This_Order(Index),
The_Deck.This_Order(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_Deck : Deck) return Natural is
begin
return In_Deck.Num_Left;
end Cards_Left;
end Cards;
Finally, I show a procedure to create a deck of cards and do the
shuffling:
-----------------------------------------------------------------------
-- Card_Deck.adb
-- This procedure is a test driver for the Cards package
-----------------------------------------------------------------------
with Ada.Text_Io;
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;
Ada.Text_Io.Put_Line("Initial Deck:");
Cards.Print(My_Deck);
-- Shuffle the deck so that the cards are accessed in a
-- random order.
Cards.Shuffle(My_Deck);
Ada.Text_Io.New_Line(2);
Ada.Text_Io.Put_Line("Shuffled Deck:");
Cards.Print(My_Deck);
-- Deal out the cards, printing each dealt card.
Ada.Text_Io.New_Line(2);
Ada.Text_Io.Put_Line("Printing each card as it is dealt:");
while Cards.Cards_Left(In_Deck => My_Deck) > 0 loop
Cards.Deal(The_Card => This_Card, From => My_Deck);
Cards.Print(This_Card);
end loop;
-- Attempt to deal one more card from the deck. This will raise
-- the Deck_Empty exception.
Ada.Text_Io.New_Line(2);
Ada.Text_Io.Put_Line("Attempting to deal from an empty deck:");
-- the following unnamed block encapsulates its own exception
-- handler for the Cards.Deck_Empty exception.
begin
Cards.Deal(The_Card => This_Card, From => My_Deck);
Cards.Print(This_Card);
exception
when Cards.Deck_Empty =>
Ada.Text_Io.Put_Line(
"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