Playing cards

From Rosetta Code
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>

C++

Works with: g++ version 4.1.2 20061115 (prerelease) (SUSE Linux)

Since all the functions are quite simple, they are all defined inline <cpp>

  1. ifndef CARDS_H_INC
  2. define CARDS_H_INC
  1. include <deque>
  2. include <algorithm>
  3. include <ostream>
  4. include <iterator>

namespace cards {

 class card
 {
 public:
   enum pip_type { two, three, four, five, six, seven, eight, nine, ten,
                   jack, queen, king, ace };
   enum suite_type { hearts, spades, diamonds, clubs };
   // construct a card of a given suite and pip
   card(suite_type s, pip_type p): value(s + 4*p) {}
   // construct a card directly from its value
   card(unsigned char v = 0): value(v) {}
   // return the pip of the card
   pip_type pip() { return pip_type(value/4); }
   // return the suit of the card
   suite_type suite() { return suite_type(value%4); }
 private:
   // there are only 52 cards, therefore unsigned char suffices
   unsigned char value;
 };
 char const* const pip_names[] =
   { "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
     "jack", "queen", "king", "ace" };
 // output a pip
 std::ostream& operator<<(std::ostream& os, card::pip_type pip)
 {
   return os << pip_names[pip];
 };
 char const* const suite_names[] =
   { "hearts", "spades", "diamonds", "clubs" };
 // output a suite
 std::ostream& operator<<(std::ostream& os, card::suite_type suite)
 {
   return os << suite_names[suite];
 }
 // output a card
 std::ostream& operator<<(std::ostream& os, card c)
 {
   return os << c.pip() << " of " << c.suite();
 }
 class deck
 {
 public:
   // default constructor: construct a default-ordered deck
   deck()
   {
     for (int i = 0; i < 52; ++i)
       cards.push_back(card(i));
   }
   // shuffle the deck
   void shuffle() { std::random_shuffle(cards.begin(), cards.end()); }
   // deal a card from the top
   card deal() { card c = cards.front(); cards.pop_front(); return c; }
   // iterators (only reading access is allowed)
   typedef std::deque<card>::const_iterator const_iterator;
   const_iterator begin() const { return cards.begin(); }
   const_iterator end() const { return cards.end(); }
 private:
   // the cards
   std::deque<card> cards;
 };
 // output the deck
 inline std::ostream& operator<<(std::ostream& os, deck const& d)
 {
   std::copy(d.begin(), d.end(), std::ostream_iterator<card>(os, "\n"));
   return os;
 }

}

  1. endif

</cpp>

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.
Added support for 'extra' card, for example, the Joker, or Major Arcana in Tarot. <d>module deck ; import std.random, std.string, std.algorithm, std.stdio ; import std.format : Format = format;

int maxlen(T)(T[] s) { return reduce!(max)(0, map!("a.length")(cast(T[])null,s)) ; }

template DCFG(string s, string p, string x, string c, string d) {

 static const string[] suits = mixin(s) ; 
 static const string[] pips = mixin(p) ;
 static const string[] extras = mixin(x) ;   
 static const string connect = c ;
 static const string dg = d ;
 const int fmtp = maxlen(pips), fmts = maxlen(suits) ;
 const int fmtall = max(fmtp + connect.length + fmts, maxlen(extras)) ;
   
 bool invalid(int p, int s) {
   return (!(p < pips.length && s >= 0 && s < suits.length) && 
     !(s == -1 &&  p - 1 < extras.length)) || p < 0 ;
 }
 string to_s(int p, int s) {
   auto fmt2 = Format("%%%ds", fmtall) ;
   if(s == -1) return Format(fmt2, extras[p - 1]) ;
   auto fmt1 = Format("%%%ds%s%%-%ds",fmtp, connect, fmts) ;
   return Format(fmt2, Format(fmt1, pips[p],suits[s])) ;
 }

}

class Card(alias D : DCFG) {

 const int NSuit = D.suits.length ;
 const int NPip = D.pips.length ;
 const int NExtra = D.extras.length ;
 
 const int pip, suit ; // suit == -1 for extra card
 this(int c) { if(c < 0) this(-c, -1) ;  else this(c % NPip, (c / NPip) % NSuit) ; }
 this(int p, int s) { 
   if(D.invalid(p,s)) throw new Exception("Invalid Card") ;
   pip = p ; suit = s ; 
 }
 string toString() { return D.to_s(pip, suit) ; }
 int order() { mixin(D.dg) ; }
 int opCmp(Card rhs) { return order - rhs.order ; }

}

class Deck(C) {

 alias Deck!(C) DK ;
 private C[] deck ;
 private Mt19937 rnd ; // A MersenneTwisterEngine Random Number Generator
 this(int pack = 1) {
   foreach(p ; 0..pack)
     foreach(int c; -C.NExtra..C.NPip*C.NSuit) addCard(new C(c)) ;
   rnd.seed(unpredictableSeed) ; // default unpredictable seed
 }
 DK addCard(C card) {deck ~= card ; return this ; }
 C peekCard(int deckLoc) { return deck[deckLoc] ; }
 DK dealCard(int deckLoc, Deck toDeck = null) {
   if(!(toDeck is null)) toDeck.addCard(deck[deckLoc]) ;// just discard if no target deck
   foreach(i ; deckLoc..deck.length - 1 ) deck[i] = deck[i+1] ;  // shift 
   deck.length = deck.length - 1 ;
   return this ;
 }
 DK dealTop(Deck toDeck = null) { return dealCard(deck.length - 1, toDeck) ; }
 DK showDeck() { writefln(this.toString) ; return this ; }
 int length() { return deck.length ; }
 string toString() { return Format("%s", deck) ; } 
 DK shuffle() { // Fisher-Yates shuffle algorithm, simplified
   for(int n = length ; n > 1 ; n--) swap(deck[uniform(rnd, 0, n)], deck[n - 1]) ;
   return this ;
 } 
 DK sortDeck() { std.algorithm.sort(deck) ; return this ; }

}

alias DCFG!( // It can be changed to a Tarot config, for example.

 `["Diamond", "Heart", "Club","Spade"]`,
 `["Ace","2","3","4","5","6","7","8","9","Ten","Jack","Queen","King"]`,
 `["*** Joker ***"]`,
 " of ",
 ` if(suit < 0) return suit*pip ;
   int p = pip == 0 ? NPip - 1 : pip - 1 ;
   return p*NSuit + suit ;`) Poker ;

alias Card!(Poker) GameCard ; alias Deck!(GameCard) GameDeck ;

void main() {

 GameDeck[9] guests ; 
 foreach(i,inout g ; guests) g = new GameDeck(0);
 
 GameDeck host = (new GameDeck).addCard(new GameCard(-1)). // add one more Joker
   showDeck.shuffle.showDeck ; // shuffle
 
 while(host.length > 0) 
   foreach(inout g ; guests) if(host.length > 0) host.dealTop(g) ; // deal cards
 foreach(g ; guests) g.sortDeck.showDeck ; // show decks

}</d>

Haskell

Straightforward implementation with explicit names for pips and suits. A deck is just a list of cards. Dealing consists of splitting off cards from the beginning of the list by the usual pattern matching (not shown). Printing is automatic. Purely functional shuffling is a bit tricky, so here we just do the naive quadratic version. This also works for other than full decks.

import System.Random

data Pip = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | 
           Jack | Queen | King | Ace 
  deriving (Ord, Enum, Bounded, Eq, Show)

data Suit = Diamonds | Spades | Hearts | Clubs
  deriving (Ord, Enum, Bounded, Eq, Show)

type Card = (Pip, Suit)

fullRange :: (Bounded a, Enum a) => [a]
fullRange = [minBound..maxBound]

fullDeck :: [Card]
fullDeck = [(pip, suit) | pip <- fullRange, suit <- fullRange]

insertAt :: Int -> a -> [a] -> [a]
insertAt 0 x ys     = x:ys
insertAt n _ []     = error "insertAt: list too short"
insertAt n x (y:ys) = y : insertAt (n-1) x ys

shuffle :: RandomGen g => g -> [a] -> [a]
shuffle g xs = shuffle' g xs 0 [] where
  shuffle' g []     _ ys = ys
  shuffle' g (x:xs) n ys = shuffle' g' xs (n+1) (insertAt k x ys) where
    (k,g') = randomR (0,n) g

J

Note 'playingcards.ijs'
This is a class script. Object modularity is used because 
the deck is altered over time by shuffling or dealing.
Multiple decks may be used, one for each instance of this class.
)

coclass 'rcpc'   NB. Rosetta Code playing cards class

create=: 3 : 0
 DeckPrototype=: |.|:(13|i.52),:<.13%~i.52
 CVN=: > ;:'Ace Two Three Four Five Six Seven Eight Nine Ten Jack Queen King'
 CSN=: > ;:'Spades Hearts Diamonds Clubs'
 NB. CVN means card value names, CSN means card suit names.
 sayCards=: ((CVN{~{.),' of ',(CSN{~{:))"1
 1   NB. Return 1 to indicate normal completion.
)

destroy=: codestroy

startNewDeck=: 3 : 0
 1: TheDeck=: DeckPrototype
)

shuffle=: 3 : 0
 1: TheDeck=: TheDeck {~ ?~ # TheDeck
)

dealCards=: 3 : 0
 ((}.@$) $ ,) 1 dealCards y
:
 assert. (# TheDeck) >: ToBeDealt=. x*y
 CardsOffTop=. (i.ToBeDealt) {  TheDeck
 TheDeck    =:    ToBeDealt  }. TheDeck
 (1 0 2)|:(y,x)$CardsOffTop
)
Note 'dealCards'
Left parameter (x) is number of players, with default to one.
Right parameter (y) is number of cards to be dealt to each player.
Used monadically, the player-axis is omitted from output.
)

pcc=: 3 : 0   NB. "Print" current contents of the deck.
sayCards TheDeck
)

Example use:

   load 'coutil'
   load 'c:\documents and settings\user_name\j602-user\playingcards.ijs'
   pc=: '' conew 'rcpc'
   startNewDeck__pc''
1
   $TheDeck__pc
52 2
   shuffle__pc''
1
   sayCards__pc 2 dealCards__pc 5   NB. deal two hands of five cards
Nine  of Hearts  
Three of Clubs   
Seven of Clubs   
Ten   of Hearts  
Three of Diamonds

Seven of Diamonds
Nine  of Spades  
King  of Diamonds
Queen of Hearts  
Six   of Clubs   
   $TheDeck__pc   NB. deck size has been reduced by the ten cards dealt
42 2
   destroy__pc ''
1

Java

Works with: Java version 1.5+

<java>public enum Pip { Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace }</java> <java>public enum Suit { Diamonds, Spades, Hearts, Clubs }</java>

The card: <java>public class Card {

   private final Suit suit;
   private final Pip value;
   public Card(Suit s, Pip v) {
       suit = s;
       value = v;
   }
   public String toString() {
       return value + " of " + suit;
   }

}</java> The deck: <java>import java.util.Collections; import java.util.LinkedList;

public class Deck {

   private final LinkedList<Card> deck= new LinkedList<Card>();
   public Deck() {
       for (Suit s : Suit.values())
           for (Pip v : Pip.values())
               deck.add(new Card(s, v));
   }
   public Card deal() {
       return deck.poll();
   }
   public void shuffle() {
       Collections.shuffle(deck); // I'm such a cheater
   }
   public String toString(){
       return deck.toString();
   }

}</java>

Perl

<perl>package Playing_Card_Deck;

use strict;

@Playing_Card_Deck::suits = qw

  [Diamonds Clubs Hearts Spades];

@Playing_Card_Deck::pips = qw

  [Two Three Four Five Six Seven Eight Nine Ten
   Jack King Queen Ace];
  1. I choose to fully qualify these names rather than declare them
  2. with "our" to keep them from escaping into the scope of other
  3. packages in the same file. Another possible solution is to use
  4. "our" or "my", but to enclose this entire package in a bare block.

sub new

  1. Creates a new deck-object. The cards are initially neatly ordered.
{my $invocant = shift;
 my $class = ref($invocant) || $invocant;
 my @cards = ();
 foreach my $suit (@Playing_Card_Deck::suits)
    {foreach my $pip (@Playing_Card_Deck::pips)
        {push(@cards, {suit => $suit, pip => $pip});}}
 return bless([@cards], $class);}

sub deal

  1. Removes the top card of the given deck and returns it as a hash
  2. with the keys "suit" and "pip".
{return %{ shift( @{shift(@_)} ) };}

sub shuffle

  1. Randomly permutes the cards in the deck. It uses the algorithm
  2. described at:
  3. http://en.wikipedia.org/wiki/Fisher-Yates_shuffle#The_modern_algorithm
{our @deck; local *deck = shift;
   # @deck is now an alias of the invocant's referent.
 for (my $n = $#deck ; $n > 1 ; --$n)
    {my $k = int rand($n + 1);
     if ($k != $n) 
        {my $tmp = $deck[$k];
         $deck[$k] = $deck[$n];
         $deck[$n] = $tmp;}}}

sub print_cards

  1. Prints out a description of every card in the deck, in order.
{print "$_->{pip} of $_->{suit}\n" foreach @{shift(@_)};}</perl>

Some examples of use: <perl>my $deck = new Playing_Card_Deck; $deck->shuffle; my %card = $deck->deal; print uc("$card{pip} OF $card{suit}\n"); $deck->print_cards;</perl> This creates a new deck, shuffles it, removes the top card, prints out that card's name in all caps, and then prints the rest of the deck.