Playing cards: Difference between revisions

From Rosetta Code
Content added Content deleted
(Added Java)
(+D)
Line 134: Line 134:
end Playing_Cards;
end Playing_Cards;
</ada>
</ada>
=={{header|D}}==
{{works with|D|2.008+}}
The shuffle algorithm is simplified from [[wp:Fisher-Yates_shuffle#Modern_method|Fisher-Yates shuffle]]
The random number generator used is a better generator added at 2.008 release.
<d>module deck ;
import std.stdio, std.string, std.algorithm ;
import std.format : Format = format;


class InvalidCard : Exception { this() { super("Invalid Card") ; } }
class DealError : Exception { this() { super("Cannot deal Card on deck") ; } }

class Card {
const int cardcode ;
const string[] suitName = ["diamond", "heart", "club","spade"] ;
const string[] valueName = ["A","2","3","4","5","6","7","8","9","10","J","Q","K"] ;
this(int code) {
if (code < 0) throw new InvalidCard ;
cardcode = code ;
}
this(int value, int suit, int pack) {
if (value < 0 || value >= 13 ) throw new InvalidCard ;
if (suit < 0 || suit >= 4 ) throw new InvalidCard ;
if (pack < 0) throw new InvalidCard ;
cardcode = pack*52 + suit*13 + value ;
}
int value() { return cardcode % 13 ; }
int suit() { return (cardcode / 13) % 4 ; }
int pack() { return cardcode / 52 ; }
string toString() { return Format("%2s.%-7s",valueName[value], suitName[suit]) ; }
}

class GameCard : Card { // customize, eg. card order, according to a game rule
this(int code) { super(code) ; }
this(int value, int suit, int pack) { super(value, suit, pack) ; }
int valueOrder() { return value == 0 ? 12 : value - 1 ; }
int cardOrder() { return suit + valueOrder*13 ; }
int opCmp(GameCard rhs) { return cardOrder - rhs.cardOrder ; }
}

class Deck(C : Card) {
C[] deck ;
Mt19937 rnd ; // A MersenneTwisterEngine Random Number Generator
this(int pack = 1) {
for(int code = 0 ; code < pack*52 ; code++)
addCard(new C(code)) ;
rnd.seed(unpredictableSeed) ; // default unpredictable seed
}
void reSeed(int newseed) { rnd.seed(newseed) ; } // same seed repeat same run
void addCard(C card) {deck ~= card ; }
void dealCard(int deckLoc, Deck toDeck = null) {
if(deckLoc < 0 || deckLoc >= deck.length) throw new DealError ;
if(!(toDeck is null))
toDeck.addCard(deck[deckLoc]) ; // just discard if no target deck
for(int i = deckLoc ; i < deck.length - 1 ; i++) // delete the card & shift remaining
deck[i] = deck[i+1] ;
deck.length = deck.length - 1 ;
}
void dealTop(Deck toDeck = null) { dealCard(deck.length - 1, toDeck) ; }
void dealLast(Deck toDeck = null) { dealCard(0, toDeck) ; }
void dealRnd(Deck toDeck = null) { dealCard(uniform(rnd, 0, deck.length), toDeck) ; }
int length() { return deck.length ; }
string toString() { return Format("%s", deck) ; }
// http://en.wikipedia.org/wiki/Fisher-Yates_shuffle#Modern_method
void shuffle() {
for(int n = length ; n > 1 ; n--)
swap(deck[uniform(rnd, 0, n)], deck[n - 1]) ;
}
}

alias Deck!(GameCard) GameDeck ;

void main() {
GameDeck host = new GameDeck ;
GameDeck[4] guests ;
foreach(inout g ; guests)
g = new GameDeck(0) ;
writefln(host) ;

host.shuffle ;

while(host.length > 0)
foreach(inout g ; guests)
host.dealTop(g) ;
foreach(g ; guests)
writefln(g) ;
}</d>
=={{header|Java}}==
=={{header|Java}}==
{{works with|Java|1.5+}}
{{works with|Java|1.5+}}

Revision as of 10:38, 27 April 2008

Task
Playing cards
You are encouraged to solve this task according to the task description, using any language you may know.

Create a data structure and the associated methods to define and manipulate a deck of playing cards. The deck should contain 52 unique cards. The methods must include the ability to make a new deck, shuffle (randomize) the deck, deal from the deck, and print the current contents of a deck. Each card must have a pip value and a suit value which constitute the unique value of the card.

Ada

Here is the package specification for a deck of playing cards. <ada> package Playing_Cards is

  pragma Elaborate_Body(Playing_Cards);
  type Card is private;
  procedure Print(The_Card : Card);
  type Deck is private;
  procedure Print(the_Deck : Deck);
  procedure Deal(From : in out Deck; The_Card : out Card);
  procedure Shuffle(The_Deck : in out Deck);
  function New_Deck return Deck;
  Deck_Empty : exception;

private

  type Pips is (Two, Three, Four, Five, Six, Seven,
     Eight, Nine, Ten, Jack, Queen, King, Ace);
  type Suits is (Diamonds, Spades, Hearts, Clubs);
  type Card is record
     Pip : Pips;
     Suit : Suits;
  end record;
  type Index is range 1..53;
  subtype Deck_Index is Index range 1..52;
  type Deck_Reference is array(Deck_Index) of Deck_Index;
  type Deck is record
     Next_Card : Index;
     Deck_Offsets : Deck_Reference;
  end record;

end Playing_Cards; </ada> Here is the package body for that same playing card package. This implementation stores one array of cards in sorted order. Each deck contains an array of indices into that one array of cards. Shuffling the deck actually results in randomizing the order of those indices into the array of cards. This approach maximizes shuffling efficiency by only exchanging indices. It also maximizes memory efficiency since an array of cards requires more memory than an array of indices. <ada> with Ada.Numerics.Discrete_Random; With Ada.Text_IO;

package body Playing_Cards is

  type Internal_Deck is array(Deck_Index) of Card;
  Base_Deck : Internal_Deck;
  Base_Index : Index;
  ----------
  -- Deal --
  ----------
  procedure Deal (From : in out Deck; The_Card : out Card) is
  begin
     if From.Next_Card not in Deck_Index then
        raise Deck_Empty;
     end if;
     The_Card := Base_Deck(From.Deck_Offsets(From.Next_Card));
     From.Next_Card := From.Next_Card + 1;
  end Deal;
  --------------
  -- New_Deck --
  --------------
  function New_Deck return Deck is
     Temp : Deck;
  begin
     for I in Base_Deck'range loop
        Temp.Deck_Offsets(I) := I;
     end loop;
     Temp.Next_Card := 1;
     return Temp;
  end New_Deck;
  -----------
  -- Print --
  -----------
  
  procedure Print(The_Card : Card) is
     package Pip_Io is new Ada.Text_Io.Enumeration_Io(Pips);
     use Pip_Io;
     package Suit_Io is new Ada.Text_Io.Enumeration_Io(Suits);
     use Suit_Io;
  begin
     Put(Item => The_Card.Pip, Width => 1);
     Ada.Text_Io.Put(" of ");
     Put(Item => The_Card.Suit, Width => 1);
     Ada.Text_Io.New_Line;
  end Print;
  
  -----------
  -- Print --
  -----------
  
  procedure Print(The_Deck : Deck) is
  begin
     for I in The_Deck.Next_Card..Deck_Index'Last loop
        Print(Base_Deck(The_Deck.Deck_Offsets(I)));
     end loop;
  end Print;
  
  -------------
  -- Shuffle --
  -------------
  procedure Shuffle (The_Deck : in out Deck) is
     procedure Swap(Left, Right : in out Deck_Index) is
        Temp : Deck_Index := Left;
     begin
        Left := Right;
        Right := Temp;
     end Swap;
     Swap_Index : Deck_Index;
  begin
     for I in Deck_Index'First..Deck_Index'Pred(Deck_Index'Last) loop
        declare
           subtype Remaining_Indices is Deck_Index range I..Deck_Index'Last;
           package Rand_Card is new Ada.Numerics.Discrete_Random(Remaining_Indices);
           use Rand_Card;
           Seed : Generator;
        begin
           Reset(Seed);
           Swap_Index := Random(Seed);
           Swap(The_Deck.deck_Offsets(I), The_Deck.Deck_Offsets(Swap_Index));
        end;
     end loop;
     The_Deck.Next_Card := 1;
  end Shuffle;
  

begin

  Base_Index := 1;
  for Suit in Suits'range loop
     for Pip in Pips'range loop
        Base_Deck(Base_Index) := (Pip, Suit);
        Base_Index := Base_Index + 1;
     end loop;
  end loop;

end Playing_Cards; </ada>

D

Works with: D version 2.008+

The shuffle algorithm is simplified from Fisher-Yates shuffle The random number generator used is a better generator added at 2.008 release. <d>module deck ; import std.stdio, std.string, std.algorithm ; import std.format : Format = format;

class InvalidCard : Exception { this() { super("Invalid Card") ; } } class DealError : Exception { this() { super("Cannot deal Card on deck") ; } }

class Card {

 const int cardcode ;
 const string[] suitName = ["diamond", "heart", "club","spade"] ; 
 const string[] valueName = ["A","2","3","4","5","6","7","8","9","10","J","Q","K"] ;
 
 this(int code) { 
   if (code < 0) throw new InvalidCard ;
   cardcode = code ;
 }
 this(int value, int suit, int pack) { 
   if (value < 0 || value >= 13 ) throw new InvalidCard ;
   if (suit < 0 || suit >= 4 ) throw new InvalidCard ;
   if (pack < 0) throw new InvalidCard ;   
   cardcode = pack*52 + suit*13 + value ; 
 }
 int value() { return cardcode % 13 ; }
 int suit() { return (cardcode / 13) % 4 ; }
 int pack() { return cardcode / 52 ; }
 string toString() { return Format("%2s.%-7s",valueName[value], suitName[suit]) ; }

}

class GameCard : Card { // customize, eg. card order, according to a game rule

 this(int code) { super(code) ; }
 this(int value, int suit, int pack) { super(value, suit, pack) ; }
 int valueOrder() { return value == 0 ? 12 : value - 1 ; } 
 int cardOrder() { return suit + valueOrder*13 ; }
 int opCmp(GameCard rhs) { return cardOrder - rhs.cardOrder ; }

}

class Deck(C : Card) {

 C[] deck ;
 Mt19937 rnd ; // A MersenneTwisterEngine Random Number Generator
 
 this(int pack = 1) {
   for(int code = 0 ; code < pack*52 ; code++)
     addCard(new C(code)) ;
   rnd.seed(unpredictableSeed) ; // default unpredictable seed
 }
 void reSeed(int newseed) { rnd.seed(newseed) ; } // same seed repeat same run 
 void addCard(C card) {deck ~= card ; }
 void dealCard(int deckLoc, Deck toDeck = null) {
   if(deckLoc < 0 || deckLoc >= deck.length) throw new DealError ;
   if(!(toDeck is null))
     toDeck.addCard(deck[deckLoc]) ; // just discard if no target deck
   for(int i = deckLoc ; i < deck.length - 1 ; i++) // delete the card & shift remaining
     deck[i] = deck[i+1] ;
   deck.length = deck.length - 1 ;
 }
 void dealTop(Deck toDeck = null) { dealCard(deck.length - 1, toDeck) ; }
 void dealLast(Deck toDeck = null) { dealCard(0, toDeck) ; }
 void dealRnd(Deck toDeck = null) { dealCard(uniform(rnd, 0, deck.length), toDeck) ; } 
 int length() { return deck.length ; }
 string toString() { return Format("%s", deck) ; }
 // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle#Modern_method
 void shuffle() {
   for(int n = length ; n > 1 ; n--)
     swap(deck[uniform(rnd, 0, n)], deck[n - 1]) ;
 } 

}

alias Deck!(GameCard) GameDeck ;

void main() {

 GameDeck host = new GameDeck ;
 GameDeck[4] guests ;
 
 foreach(inout g ; guests)
   g = new GameDeck(0) ;
 
 writefln(host) ;
 host.shuffle ;
 while(host.length > 0) 
   foreach(inout g ; guests)
     host.dealTop(g) ;
 
 foreach(g ; guests)
   writefln(g) ;

}</d>

Java

Works with: Java version 1.5+

The card: <java>public class Card{ private final int suit, value; public Card(int s, int v){ suit= s; value= v; }

public String toString(){ String retVal= ""; if(value <= 10){ retVal+= value; }else if(value == 11){ retVal+= "Jack"; }else if(value == 12){ retVal+= "Queen"; }else if(value == 13){ retVal+= "King"; }else if(value == 14){ retVal+= "Ace"; } retVal+= " of "; switch(suit){ case 0: retVal+= "Spades"; break; case 1: retVal+= "Diamonds"; break; case 2: retVal+= "Hearts"; break; case 3: retVal+= "Clubs"; default: } return retVal; } }</java> The deck: <java>import java.util.Collections; import java.util.LinkedList;

public class Deck{ LinkedList<Card> deck= new LinkedList<Card>();

public Deck(){ for(int suits= 0;suits <= 3;suits++){ for(int values= 2;values <= 14;values++){ deck.add(new Card(suits, values)); } } }

public Card deal(){ if(deck.size() > 0){ return deck.remove(); }else{ return null; } }

public void shuffle(){ Collections.shuffle(deck);// I'm such a cheater }

public String toString(){ String retVal= ""; for(Card card: deck){ retVal+= card + (card != deck.getLast() ? ", " : ""); } return retVal; } }</java>