Playing cards: Difference between revisions
m (→{{header|J}}: Style condensation.) |
m (→{{header|J}}: Simplified example with dyadic use of conew) |
||
Line 361: | Line 361: | ||
=={{header|J}}== |
=={{header|J}}== |
||
Note 'playingcards.ijs' |
Note 'playingcards.ijs' |
||
This is a class script. Object modularity is used because |
This is a class script. Object modularity is used because |
||
deck is altered over time as cards are dealt. |
the deck is altered over time as cards are dealt. |
||
Multiple decks may be used, one for each instance of this class. |
Multiple decks may be used, one for each instance of this class. |
||
) |
) |
||
Line 384: | Line 384: | ||
shuffle=: 3 : 0 |
shuffle=: 3 : 0 |
||
1: TheDeck=: TheDeck {~ ?~ #TheDeck |
1: TheDeck=: TheDeck {~ ?~ # TheDeck |
||
) |
) |
||
Line 398: | Line 398: | ||
Left parameter (x) is number of players, with default to one. |
Left parameter (x) is number of players, with default to one. |
||
Right parameter (y) is number of cards to be dealt to each player. |
Right parameter (y) is number of cards to be dealt to each player. |
||
Used monadically, |
Used monadically, the player-axis is omitted from output. |
||
) |
) |
||
Example use: |
Example use: |
||
load 'coutil' |
load 'coutil' |
||
load 'c:\documents and settings\user_name\j602-user\playingcards.ijs' |
load 'c:\documents and settings\user_name\j602-user\playingcards.ijs' |
||
pc=: conew 'rcpc' |
pc=: <nowiki>''</nowiki> conew 'rcpc' |
||
create__pc <nowiki>''</nowiki> |
|||
1 |
|||
startNewDeck__pc<nowiki>''</nowiki> |
startNewDeck__pc<nowiki>''</nowiki> |
||
1 |
1 |
||
Line 412: | Line 410: | ||
shuffle__pc<nowiki>''</nowiki> |
shuffle__pc<nowiki>''</nowiki> |
||
1 |
1 |
||
sayCards__pc 2 dealCards__pc 5 NB. deal two hands of five cards |
sayCards__pc 2 dealCards__pc 5 NB. deal two hands of five cards |
||
Nine of Hearts |
Nine of Hearts |
||
Three of Clubs |
Three of Clubs |
Revision as of 18:00, 5 May 2008
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++
Since all the functions are quite simple, they are all defined inline <cpp>
- ifndef CARDS_H_INC
- define CARDS_H_INC
- include <deque>
- include <algorithm>
- include <ostream>
- 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; }
}
- endif
</cpp>
D
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*4 ; } 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>
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 as cards are dealt. 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. )
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
<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>